In my program some comboboxes (QComboBox) were used to make several settings. Sometimes its necessary not only to know the item the user selected but also the item which was selected previously in the combobox.
Well, transferring the new selection is quite easy:
self.MyCombobox.activated[str].connect(self.ComboChange)
But how do i manage to not only passing the newly selected but also the previous item to a function when the index changed?
My compromise solution would be to manually set a variable for every combobox which stores the last selected value so that it can be accessed when the selction has changed. But considering that i have a lot of Comboboxes this would be quite prone to errors until some boxes could be updated on different ways.
Thanks in advance for your help
A minimal working example:
import sys
from PyQt5.QtWidgets import ( QApplication, QWidget, QGridLayout, QComboBox,
QLabel)
class BaseWidget(QWidget):
def __init__(self):
super(BaseWidget, self).__init__()
self.setGeometry(300, 300, 300, 200)
# 2 Labels to display the new and the old item after selection
self.LabelOldItem = QLabel(self)
self.LabelNewItem = QLabel(self)
self.MyCombobox = QComboBox(self)
self.MyCombobox.addItems(['Item 1', 'Item 2', 'Item 3', 'Item 4'])
self.MyCombobox.activated[str].connect(self.ComboChange)
grid = QGridLayout()
grid.addWidget(self.MyCombobox, 0, 0, 1, 1)
grid.addWidget(self.LabelOldItem, 1, 0, 1, 1)
grid.addWidget(self.LabelNewItem, 2, 0, 1, 1)
self.setLayout(grid)
def ComboChange(self, newitem):
self.LabelOldItem.setText('Previous Selection: ') # <- crucial point
# How can i transfer both, not only the new item but also the previous
# item of the combobox when it gets activated?
self.LabelNewItem.setText('New Selection: <b>' + newitem + '</b>')
if __name__ == '__main__':
app = QApplication(sys.argv)
pyqtComboExample = BaseWidget()
pyqtComboExample.show()
sys.exit(app.exec_())
A possible solution is to create a custom QComboBox:
import sys
from PyQt5 import QtCore, QtWidgets
class ComboBox(QtWidgets.QComboBox):
new_signal = QtCore.pyqtSignal(str, str)
def __init__(self, parent=None):
super(ComboBox, self).__init__(parent)
self.lastSelected = ""
self.activated[str].connect(self.onActivated)
def onActivated(self, text):
self.new_signal.emit(self.lastSelected, text)
self.lastSelected = text
class BaseWidget(QtWidgets.QWidget):
def __init__(self):
super(BaseWidget, self).__init__()
self.setGeometry(300, 300, 300, 200)
# 2 Labels to display the new and the old item after selection
self.LabelOldItem = QtWidgets.QLabel()
self.LabelNewItem = QtWidgets.QLabel()
self.MyCombobox = ComboBox()
self.MyCombobox.addItems(['Item 1', 'Item 2', 'Item 3', 'Item 4'])
self.MyCombobox.new_signal.connect(self.ComboChange)
grid = QtWidgets.QGridLayout(self)
grid.addWidget(self.MyCombobox, 0, 0, 1, 1)
grid.addWidget(self.LabelOldItem, 1, 0, 1, 1)
grid.addWidget(self.LabelNewItem, 2, 0, 1, 1)
def ComboChange(self, lastitem, newitem):
self.LabelOldItem.setText('Previous Selection: <b>{}</b>'.format(lastitem))
self.LabelNewItem.setText('New Selection: <b>{}</b>'.format(newitem))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
pyqtComboExample = BaseWidget()
pyqtComboExample.show()
sys.exit(app.exec_())
Another possible solution is to use sender() to get the QComboBox used, and save the old item in a property:
import sys
from PyQt5 import QtCore, QtWidgets
class BaseWidget(QtWidgets.QWidget):
def __init__(self):
super(BaseWidget, self).__init__()
self.setGeometry(300, 300, 300, 200)
# 2 Labels to display the new and the old item after selection
self.LabelOldItem = QtWidgets.QLabel()
self.LabelNewItem = QtWidgets.QLabel()
self.MyCombobox = QtWidgets.QComboBox()
self.MyCombobox.addItems(['Item 1', 'Item 2', 'Item 3', 'Item 4'])
self.MyCombobox.activated[str].connect(self.ComboChange)
grid = QtWidgets.QGridLayout(self)
grid.addWidget(self.MyCombobox, 0, 0, 1, 1)
grid.addWidget(self.LabelOldItem, 1, 0, 1, 1)
grid.addWidget(self.LabelNewItem, 2, 0, 1, 1)
def ComboChange(self, newitem):
combo = self.sender()
lastitem = combo.property("lastitem")
self.LabelOldItem.setText('Previous Selection: <b>{}</b>'.format(lastitem))
self.LabelNewItem.setText('New Selection: <b>{}</b>'.format(newitem))
combo.setProperty("lastitem", newitem)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
pyqtComboExample = BaseWidget()
pyqtComboExample.show()
sys.exit(app.exec_())
Related
How to arrange Widgets in QGridLayout as desired? For example, In My code,
I need to reduce the space between the " Country" and "State" Button
Place the Widgets in top left most Corner ( Country Button in Top left Most Corner- No Margins)
reduce the space between "country" and "District" Buttons
Place the "bank" Button to the centre of the "Town" Button
need/arrange layout as per attached image
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class GridLayout_Example(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Grid layout Example")
self.setGeometry(100,100,1600,600)
self.initUI()
self.layout()
self.show()
def initUI(self):
self.btn_country_63 = QPushButton()
self.btn_country_63.setText("Country")
self.btn_country_63.setObjectName("ob_btn_country_63")
self.btn_country_63.setProperty('group', "1")
self.btn_country_63.setFixedSize(100, 25)
self.btn_country_63.setStyleSheet("background-color:red;color:white")
self.btn_country_63.installEventFilter(self)
# self.btn_country_63.clicked.connect(self.btn_country_63_select)
self.btn_state_63 = QPushButton()
self.btn_state_63.setText("State")
self.btn_state_63.setObjectName("ob_btn_state_63")
self.btn_state_63.setProperty('group', "1")
self.btn_state_63.setFixedSize(100, 25)
self.btn_state_63.setStyleSheet("background-color:red;color:white")
self.btn_state_63.installEventFilter(self)
# self.btn_state_63.clicked.connect(self.btn_state_63_select)
self.btn_district_63 = QPushButton()
self.btn_district_63.setText("District")
self.btn_district_63.setObjectName("ob_btn_district_63")
self.btn_district_63.setProperty('group', "1")
self.btn_district_63.setFixedSize(100, 25)
self.btn_district_63.setStyleSheet("background-color:red;color:white")
self.btn_district_63.installEventFilter(self)
# self.btn_district_63.clicked.connect(self.btn_district_63_select)
self.btn_town_63 = QPushButton()
self.btn_town_63.setText("Town")
self.btn_town_63.setObjectName("ob_btn_town_63")
self.btn_town_63.setProperty('group', "1")
self.btn_town_63.installEventFilter(self)
self.btn_town_63.setFixedSize(60, 80)
self.btn_town_63.setStyleSheet("background-color:red;color:white")
# self.btn_town_63.clicked.connect(self.btn_town_63_select)
self.btn_bank_63 = QPushButton()
self.btn_bank_63.setText("Bank")
self.btn_bank_63.setObjectName("ob_btn_bank_63")
self.btn_bank_63.setProperty('group', "1")
self.btn_bank_63.installEventFilter(self)
self.btn_bank_63.setFixedSize(100, 25)
# self.btn_bank_63.clicked.connect(self.btn_bank_63_select)
def layout(self):
self.layout_main_toppannel_2_right = QGridLayout()
self.frame_main_toppannel_2_right = QFrame()
self.frame_main_toppannel_2_right.setProperty('color', "2")
self.frame_main_toppannel_2_right.setFixedHeight(100)
self.frame_main_toppannel_2_right.setStyleSheet("background-color: lightgreen")
self.layout_main_toppannel_2_right = QGridLayout(self.frame_main_toppannel_2_right)
self.container63 = QWidget()
self.container63_box = QGridLayout(self.container63)
self.container63_box.addWidget(self.btn_country_63, 0, 0, Qt.AlignTop)
self.container63_box.addWidget(self.btn_district_63, 0, 1, Qt.AlignTop)
self.container63_box.addWidget(self.btn_state_63, 1, 0, Qt.AlignTop)
self.container63_box.addWidget(self.btn_town_63, 0, 3)
self.container63_box.addWidget(self.btn_bank_63,0,4)
self.container63_box.setColumnStretch(0,4)
self.container63_box.setRowStretch(0,2)
self.stackitem = QStackedWidget()
self.stackitem.addWidget(self.container63)
self.layout_main_toppannel_2_right.addWidget(self.stackitem)
self.main_layout = QHBoxLayout()
self.main_layout.addWidget(self.frame_main_toppannel_2_right)
self.setLayout(self.main_layout)
def main():
app = QApplication(sys.argv)
mainwindow = GridLayout_Example()
mainwindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
In a previous post I explained in detail the operation of the QGridLayout so in this post I will only detail the most outstanding.
To remove the space between the items you must use setSpacing(0) (you can also use setHorizontalSpacing(0) and setVerticalSpacing(0)).
To make the elements be pushed to the left you can set a stretch to the imaginary fifth column.
I have also modified the height of "Town" since it does not match the sum height of the 3 elements, forcing spaces to appear.
I also calculated the height of the green container after setting the items to match the space needed and there are no spaces.
Finally note that some margins cannot be changed by code since they are implemented by the native QStyles of the OS, to avoid that I impose that the style fusion be used.
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class GridLayout_Example(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.build_widgets()
self.build_layout()
self.resize(640, 480)
def build_widgets(self):
self.btn_country_63 = QPushButton(
text="Country", styleSheet="background-color:red;color:white"
)
self.btn_country_63.setFixedSize(100, 25)
self.btn_state_63 = QPushButton(
text="State", styleSheet="background-color:red;color:white"
)
self.btn_state_63.setFixedSize(100, 25)
self.btn_district_63 = QPushButton(
text="District", styleSheet="background-color:red;color:white"
)
self.btn_district_63.setFixedSize(100, 25)
self.btn_town_63 = QPushButton(
text="Town", styleSheet="background-color:red;color:white"
)
self.btn_town_63.setFixedSize(60, 75)
self.btn_bank_63 = QPushButton(
text="Bank", styleSheet="background-color:red;color:white"
)
self.btn_bank_63.setFixedSize(100, 25)
self.btn_zip_63 = QPushButton(
text="Zip Code", styleSheet="background-color:red;color:white"
)
self.btn_zip_63.setFixedSize(100, 25)
def build_layout(self):
self.container = QFrame()
self.container.setStyleSheet("background-color: lightgreen")
grid_layout = QGridLayout(self.container)
grid_layout.addWidget(self.btn_country_63, 0, 0, 1, 1)
grid_layout.addWidget(self.btn_state_63, 1, 0, 1, 1)
grid_layout.addWidget(self.btn_district_63, 0, 1, 1, 1)
grid_layout.addWidget(self.btn_town_63, 0, 2, 3, 1)
grid_layout.addWidget(self.btn_bank_63, 1, 3, 1, 1)
grid_layout.addWidget(self.btn_zip_63, 2, 4, 1, 1)
grid_layout.setColumnStretch(5, 1)
grid_layout.setSpacing(0)
grid_layout.setContentsMargins(0, 0, 0, 0)
lay = QVBoxLayout(self)
lay.addWidget(self.container)
self.container.setFixedHeight(self.container.sizeHint().height())
def main():
app = QApplication(sys.argv)
print("default-style: ", app.style().metaObject().className())
app.setStyle("fusion")
mainwindow = GridLayout_Example()
mainwindow.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Hello I have this code using python and pyqt5 which allows to display a graphical interface :
import sys
from PyQt5 import QtCore, QtWidgets
class TabPage(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
group = QtWidgets.QGroupBox('Monty Python')
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(group)
grid = QtWidgets.QGridLayout(group)
grid.addWidget(QtWidgets.QLabel('Enter a name:'), 0, 0)
grid.addWidget(QtWidgets.QLabel('Choose a number:'), 0, 1)
grid.addWidget(QtWidgets.QLineEdit(), 1, 0)
grid.addWidget(QtWidgets.QComboBox(), 1, 1)
grid.addWidget(QtWidgets.QPushButton('Click Me!'), 1, 2)
grid.addWidget(QtWidgets.QSpinBox(), 2, 0)
grid.addWidget(QtWidgets.QPushButton('Clear Text'), 2, 2)
grid.addWidget(QtWidgets.QTextEdit(), 3, 0, 1, 3)
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.tabs = QtWidgets.QTabWidget()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tabs)
button = QtWidgets.QToolButton()
button.setToolTip('Add New Tab')
button.clicked.connect(self.addNewTab)
button.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_DialogYesButton))
self.tabs.setCornerWidget(button, QtCore.Qt.TopRightCorner)
button1 = QtWidgets.QToolButton()
button1.setToolTip('Remove')
button1.clicked.connect(self.addNewTab)
button1.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_BrowserStop))
self.tabs.setCornerWidget(button1, QtCore.Qt.TopRightCorner)
self.addNewTab()
def addNewTab(self):
text = 'Tab %d' % (self.tabs.count() + 1)
self.tabs.addTab(TabPage(self.tabs), text)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())
When I execute my code I get this :
whereas I would like to get something like this :
How can I do to do this ?
Thank you a lot !
QTabWidget::setCornerWidget(QWidget *widget, Qt::Corner corner = Qt::TopRightCorner)
Any previously set corner widget is hidden. https://doc.qt.io/qt-5/qtabwidget.html#setCornerWidget
Try it:
import sys
from PyQt5 import QtCore, QtWidgets
class TabPage(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
group = QtWidgets.QGroupBox('Monty Python')
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(group)
grid = QtWidgets.QGridLayout(group)
grid.addWidget(QtWidgets.QLabel('Enter a name:'), 0, 0)
grid.addWidget(QtWidgets.QLabel('Choose a number:'), 0, 1)
grid.addWidget(QtWidgets.QLineEdit(), 1, 0)
grid.addWidget(QtWidgets.QComboBox(), 1, 1)
grid.addWidget(QtWidgets.QPushButton('Click Me!'), 1, 2)
grid.addWidget(QtWidgets.QSpinBox(), 2, 0)
grid.addWidget(QtWidgets.QPushButton('Clear Text'), 2, 2)
grid.addWidget(QtWidgets.QTextEdit(), 3, 0, 1, 3)
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.tabs = QtWidgets.QTabWidget()
self.tabs.setTabsClosable(True) # +
self.tabs.tabCloseRequested.connect(self.qtabwidget_tabcloserequested) # +
self.tabs.currentChanged.connect(lambda: print(f'currentIndex->{self.tabs.currentIndex()}')) #+
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tabs)
button = QtWidgets.QToolButton()
button.setFixedSize(20, 20) # +
button.setToolTip('Add New Tab')
button.clicked.connect(self.addNewTab)
button.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_DialogYesButton))
# self.tabs.setCornerWidget(button, QtCore.Qt.TopRightCorner)
button1 = QtWidgets.QToolButton()
button1.setFixedSize(20, 20) # +
button1.setToolTip('Remove')
button1.clicked.connect(self.removeTab) # removeTab
button1.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_BrowserStop))
# Any previously set corner widget is hidden.
# self.tabs.setCornerWidget(button1, QtCore.Qt.TopRightCorner) #
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self.widget = QtWidgets.QWidget()
h_layout = QtWidgets.QHBoxLayout(self.widget)
h_layout.setContentsMargins(0, 0, 0, 0)
h_layout.addWidget(button)
h_layout.addWidget(button1)
self.tabs.setCornerWidget(self.widget, QtCore.Qt.TopRightCorner)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.addNewTab()
def addNewTab(self):
text = 'Tab %d' % (self.tabs.count() + 1)
self.tabs.addTab(TabPage(self.tabs), text)
#QtCore.pyqtSlot(int)
def qtabwidget_tabcloserequested(self, index):
# gets the widget
widget = self.tabs.widget(index)
# if the widget exists
if widget:
# removes the widget
widget.deleteLater()
# removes the tab of the QTabWidget
self.tabs.removeTab(index)
def removeTab(self):
print('def removeTab(self): print')
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())
Since the other answer does not explain the cause of the problem and its code is confusing, I will explain the error in detail.
The error is that there can only be one cornerWidget, if you set a second cornerWidget it will replace the previous one, therefore only one QToolButton is observed. If you want to show several widgets then you have to use a container like a QWidget and place the other widgets there.
# ...
layout.addWidget(self.tabs)
button = QtWidgets.QToolButton()
button.setToolTip("Add New Tab")
button.clicked.connect(self.addNewTab)
button.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogYesButton))
button1 = QtWidgets.QToolButton()
button1.setToolTip("Remove")
button1.clicked.connect(self.addNewTab)
button1.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_BrowserStop))
container = QtWidgets.QWidget()
container.setContentsMargins(0, 0, 0, 0)
lay = QtWidgets.QHBoxLayout(container)
lay.setContentsMargins(0, 0, 0, 0)
lay.addWidget(button)
lay.addWidget(button1)
self.tabs.setCornerWidget(container, QtCore.Qt.TopRightCorner)
self.addNewTab()
Using PySide2, I have an interface which contains a QColumnView widget, for which I am trying to create functionality similar to that of a contact-lookup: select a person's name and the next column over then displays more options/details about that selection.
Currently, I populate the view when the UI loads and it displays the first level of options available to the user.
Desired functionality: When the user makes a selection, another column is added to the QColumnView which displays the options available based on the user's selection.
Current problem: When the user makes a selection, since I am unsure of how to add another column/level to the QColumnView, instead, the model for the QColumnView is updated and only the next level of options are shown - meaning the previous values are overwritten in the view, so to speak. While this is functional, it is not ideal, as having the previous column still in view, enables the user to see which category they are currently searching.
As seen here, the user has selected 'Address' which then brings up options associated with 'Address' in the column to the right.
Just to create a basic interface that incorporates a QColumnView, it would look something like this:
import sys
from PySide2 import QtWidgets, QtCore
import PySide2
class UserInput(object):
def setupUi(self, get_user_input=None):
# Basic shape
self.width = 425
get_user_input.setObjectName("get_user_input")
get_user_input.resize(425, self.width)
self.frame = QtWidgets.QFrame(get_user_input)
self.frame.setGeometry(QtCore.QRect(11, 10, 401, 381))
self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Sunken)
self.frame.setObjectName("frame")
# Creating the grid layout for most of the display elements
self.gridLayoutWidget = QtWidgets.QWidget(self.frame)
self.gridLayoutWidget.setGeometry(QtCore.QRect(10, 10, 381, 361))
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
self.get_user_input_layout = QtWidgets.QGridLayout(self.gridLayoutWidget)
self.get_user_input_layout.setContentsMargins(5, 5, 5, 5)
self.get_user_input_layout.setObjectName("get_user_input_layout")
# Grid layout for the buttons
self.buttonLayoutGrid = QtWidgets.QWidget(get_user_input)
self.buttonLayoutGrid.setGeometry(QtCore.QRect(10, 390, 401, 41))
self.buttonLayoutGrid.setObjectName("buttonLayoutGrid")
self.buttonLayout = QtWidgets.QGridLayout(self.buttonLayoutGrid)
self.buttonLayout.setContentsMargins(0, 0, 0, 0)
self.buttonLayout.setObjectName("buttonLayout")
# Buttons
self.buttonOK = QtWidgets.QPushButton(self.buttonLayoutGrid)
self.buttonOK.setObjectName("buttonOK")
self.buttonOK.setText("OK")
self.buttonLayout.addWidget(self.buttonOK, 0, 1, 1, 1)
self.buttonCancel = QtWidgets.QPushButton(self.buttonLayoutGrid)
self.buttonCancel.setObjectName("buttonCancel")
self.buttonCancel.setText("CANCEL")
self.buttonLayout.addWidget(self.buttonCancel, 0, 2, 1, 1)
#Column View Data
self.t = PySide2.QtWidgets.QColumnView()
self.t.setColumnWidths([10,10])
model = PySide2.QtGui.QStandardItemModel()
model.setColumnCount(1)
model.setHorizontalHeaderLabels(['Data Collections'])
self._dict = {'Test 1': [1, 2, 3], 'Test 2': [4, 5, 6], 'Test 3': [7, 8, 9]}
_data = []
for each in self._dict:
resultItem = PySide2.QtGui.QStandardItem()
insertText = each
resultItem.setText(str(insertText))
model.appendRow(resultItem)
self.t.setModel(model)
self.t.clicked.connect(self.column_view_clicked)
self.get_user_input_layout.addWidget(self.t, 2,0,1,1)
def column_view_clicked(self, row):
model = PySide2.QtGui.QStandardItemModel()
model.setColumnCount(1)
model.setHorizontalHeaderLabels(['Analysis Variables'])
_data = []
for i in self._dict[row.data()]:
resultItem = PySide2.QtGui.QStandardItem()
resultItem.setText(str(i))
model.appendRow(resultItem)
self.t.createColumn(row)
class UserInputPrompt(PySide2.QtWidgets.QDialog, UserInput):
def __init__(self, path_to_image=None):
app = PySide2.QtWidgets.QApplication(sys.argv)
super(UserInputPrompt, self).__init__()
self.setupUi(self)
super(UserInputPrompt, self).exec_()
def get_user_input(self):
print("Get user selection")
def main():
UserInputPrompt()
if __name__ == '__main__':
main()
When you run the above code, click on any of items on the left of the interface ('Test 1, 'Test 2' or 'Test 3') and the result will add a column to the right of the existing column but no data is currently being populated there - since I don't know how to do that at the moment.
What the OP points out as a requirement is just the functionality of ColumnView, the problem is that you are not passing the model with the data structured correctly. As you can see the structure is of a tree where there are parent nodes and child nodes, so you must create a model with that structure. In this case I have used a QStandardItemModel to handle the data as I show below:
import sys
from PySide2 import QtCore, QtGui, QtWidgets
def populateTree(children, parent):
for child in children:
child_item = QtGui.QStandardItem(child)
parent.appendRow(child_item)
if isinstance(children, dict):
populateTree(children[child], child_item)
class Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.frame = QtWidgets.QFrame(
frameShape=QtWidgets.QFrame.StyledPanel, frameShadow=QtWidgets.QFrame.Sunken
)
self.buttonOK = QtWidgets.QPushButton(self.tr("OK"))
self.buttonCancel = QtWidgets.QPushButton(self.tr("CANCEL"))
self.columnview = QtWidgets.QColumnView()
grid_layout = QtWidgets.QGridLayout(self)
grid_layout.addWidget(self.frame, 0, 0, 1, 2)
grid_layout.addWidget(self.buttonOK, 1, 0)
grid_layout.addWidget(self.buttonCancel, 1, 1)
lay = QtWidgets.QVBoxLayout(self.frame)
lay.addWidget(self.columnview)
data = {
"root1": {
"child11": ["subchild111", "subchild112"],
"child12": ["subchild121"],
},
"root2": {
"child21": ["subchild211"],
"child22": ["subchild221", "subchild222"],
},
}
self.model = QtGui.QStandardItemModel(self)
self.columnview.setModel(self.model)
populateTree(data, self.model.invisibleRootItem())
self.resize(1280, 480)
def main():
app = QtWidgets.QApplication(sys.argv)
w = Dialog()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I have created a button who creates buttons in a vertical layout, called "elementos". In each row, there is a label and a X button who should delete the row. There is a list with the text of the label called "puntos" MRE:
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QOpenGLWidget
import sys
from sys import argv, exit
class Renderizador(QOpenGLWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.puntos = []
self.num_elementos = 0
def borrar_punto(self):
self.puntos.pop()
ui.elementos.removeWidget(self.name)
ui.elementos.removeWidget(self.borrar)
self.name.deleteLater()
self.borrar.deleteLater()
self.num_elementos -= 1
def crear_punto(self):
do = ui.valor_do.value()
cota = ui.valor_cota.value()
alejamiento = ui.valor_alej.value()
self.puntos.append((ui.nombre.toPlainText(), do, cota, alejamiento))
self.name = QtWidgets.QLabel()
self.name.setText(ui.nombre.toPlainText() + "({}, {}, {})".format(do, cota, alejamiento))
self.borrar = QtWidgets.QPushButton()
self.borrar.setText("X")
self.borrar.clicked.connect(lambda: self.borrar_punto())
ui.elementos.addWidget(self.name, self.num_elementos, 0, 1, 1)
ui.elementos.addWidget(self.borrar, self.num_elementos, 1, 1, 1)
self.num_elementos += 1
self.update()
class UiVentana:
def __init__(self):
ventana.resize(1500, 1000)
ventana.setLayoutDirection(QtCore.Qt.LeftToRight)
ventana.setFixedSize(ventana.size())
self.widget_central = QtWidgets.QWidget(ventana)
self.gridLayoutWidget = QtWidgets.QWidget(self.widget_central)
self.gridLayoutWidget.setGeometry(QtCore.QRect(1000, 60, 280, 50))
self.Vistas = QtWidgets.QGridLayout(self.gridLayoutWidget)
self.Visor = Renderizador(self.widget_central)
self.Visor.setGeometry(QtCore.QRect(0, 0, 1000, 1000))
self.gridLayoutWidget_2 = QtWidgets.QWidget(self.widget_central)
self.gridLayoutWidget_2.setGeometry(QtCore.QRect(1004, 105, 300, 400))
self.Punto = QtWidgets.QGridLayout(self.gridLayoutWidget_2)
self.Punto.setContentsMargins(5, 5, 5, 5)
self.Punto.setVerticalSpacing(4)
self.texto_nombre = QtWidgets.QLabel(self.gridLayoutWidget_2)
self.texto_nombre.setText("Nombre:")
self.Punto.addWidget(self.texto_nombre, 3, 0, 1, 1)
self.crear_punto = QtWidgets.QPushButton(self.gridLayoutWidget_2)
self.crear_punto.setText("Crear")
self.crear_punto.clicked.connect(self.Visor.crear_punto)
self.Punto.addWidget(self.crear_punto, 3, 2, 1, 1)
self.texto_cota = QtWidgets.QLabel(self.gridLayoutWidget_2)
self.nombre = QtWidgets.QTextEdit(self.gridLayoutWidget_2)
self.nombre.setMaximumSize(QtCore.QSize(100, 24))
self.Punto.addWidget(self.nombre, 3, 1, 1, 1)
self.scroll = QtWidgets.QScrollArea()
self.scroll.setWidgetResizable(True)
self.scroll_widget = QtWidgets.QWidget()
self.scroll_widget.resize(200, 300)
self.elementos_widget = QtWidgets.QWidget()
self.vbox = QtWidgets.QVBoxLayout(self.scroll_widget)
self.vbox.setContentsMargins(0, 0, 0, 0)
self.vbox.addWidget(self.elementos_widget)
self.vbox.addStretch()
self.elementos = QtWidgets.QGridLayout()
self.elementos_widget.setLayout(self.elementos)
self.scroll.setWidget(self.scroll_widget)
self.scroll_widget.setLayout(self.elementos)
self.scroll.setWidget(self.scroll_widget)
self.Punto.addWidget(self.scroll, 4, 0, 1, 3)
self.valor_do = QtWidgets.QSpinBox(self.gridLayoutWidget_2)
self.Punto.addWidget(self.valor_do, 2, 0, 1, 1)
self.valor_alej = QtWidgets.QSpinBox(self.gridLayoutWidget_2)
self.Punto.addWidget(self.valor_alej, 2, 1, 1, 1)
self.valor_cota = QtWidgets.QSpinBox(self.gridLayoutWidget_2)
self.Punto.addWidget(self.valor_cota, 2, 2, 1, 1)
ventana.setCentralWidget(self.widget_central)
ventana.show()
def except_hook(cls, exception, traceback):
sys.__excepthook__(cls, exception, traceback)
if __name__ == "__main__":
app = QtWidgets.QApplication(argv)
ventana = QtWidgets.QMainWindow()
ui = UiVentana()
sys.excepthook = except_hook
exit(app.exec_())
The pop statement should delete from the list the corresponding info about the label. I get an out of index range error, but i get a "RuntimeError: wrapped C/C++ object of type QLabel has been deleted" error if I pop the last statement.
Full code: https://github.com/Jaime02/Proyecto-de-investigacion-2019-Dibujo-tecnico/blob/experimental/error (Lines 120-140)
Edit: The delete button only works once if two or more rows are created
Your code has at least the following errors:
If you are going to create an object within a loop, do not make it a member, for example in your case self.name and self.borrar are variables that will always point to the last QLabel and QPushButton, so when you delete only the last row will be deleted in a single occasion.
Your code is very messy since they are modifying variables in places where they are not due as they could cause problems such as tracking errors, for example the variable window and ui.
Considering the above I have rewritten your code implementing the logic of passing the widgets and deleting the widgets directly.
import sys
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets
class UiVentana(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UiVentana, self).__init__(parent)
self.puntos = []
self.visor = QtWidgets.QOpenGLWidget()
self.valor_do = QtWidgets.QSpinBox()
self.valor_cota = QtWidgets.QSpinBox()
self.valor_alej = QtWidgets.QSpinBox()
self.texto_nombre = QtWidgets.QLabel("Nombre")
self.nombre = QtWidgets.QLineEdit()
self.crear_punto = QtWidgets.QPushButton("Crear", clicked=self.crear_punto)
elementos_widget = QtWidgets.QWidget()
scroll_widget = QtWidgets.QWidget()
scroll = QtWidgets.QScrollArea(widgetResizable=True)
scroll.setWidget(scroll_widget)
vbox = QtWidgets.QVBoxLayout(scroll_widget)
vbox.addWidget(elementos_widget)
vbox.addStretch()
self.elementos = QtWidgets.QGridLayout(elementos_widget)
grid_layout = QtWidgets.QGridLayout()
grid_layout.addWidget(self.valor_do, 0, 0)
grid_layout.addWidget(self.valor_cota, 0, 1)
grid_layout.addWidget(self.valor_alej, 0, 2)
grid_layout.addWidget(self.texto_nombre, 1, 0)
grid_layout.addWidget(self.nombre, 1, 1)
grid_layout.addWidget(self.crear_punto, 1, 2)
grid_layout.addWidget(scroll, 2, 0, 1, 3)
for i in range(3):
grid_layout.setColumnStretch(i, 1)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QHBoxLayout(central_widget)
lay.addWidget(self.visor, stretch=1)
lay.addLayout(grid_layout)
self.resize(1280, 960)
def crear_punto(self):
do = self.valor_do.value()
cota = self.valor_cota.value()
alejamiento = self.valor_alej.value()
name = QtWidgets.QLabel(
"{}({}, {}, {})".format(self.nombre.text(), do, cota, alejamiento)
)
punto = (self.nombre.text(), do, cota, alejamiento)
borrar = QtWidgets.QPushButton("X")
wrapper = partial(self.borrar_punto, (name, borrar), punto)
borrar.clicked.connect(wrapper)
row = self.elementos.rowCount()
self.elementos.addWidget(name, row, 0)
self.elementos.addWidget(borrar, row, 1)
self.puntos.append(punto)
def borrar_punto(self, widgets, punto):
if self.puntos:
name, borrar = widgets
name.deleteLater()
borrar.deleteLater()
self.puntos.remove(punto)
print(self.puntos)
def except_hook(cls, exception, traceback):
sys.__excepthook__(cls, exception, traceback)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
sys.excepthook = except_hook
w = UiVentana()
w.show()
exit(app.exec_())
I'm new to PyQt and I'm trying to create a system which dynamically adds widgets when the user presses add.
I want the same self.comboBox widget to get added above the Add button. I will also make a remove button but I believe that will be no problem.
Moreover, I would like certain checkboxes to appear next to the self.combobox when the user chooses an option (aka option is not -Select-).
Finally, how can I store the user's choices in the checkboxes and the combobox? Do I declare a variable or what exactly?
My code was too much to read, so this instead:
class myWindow(QWidget):
def __init__(self):
super().__init__()
self.init()
self.organize()
def init(self):
self.label = QLabel("Label")
self.comboBox = QComboBox(self)
self.comboBox.addItem("-Select-")
self.comboBox.addItem("1")
self.comboBox.addItem("2")
self.comboBox.addItem("3")
self.addbtn = QPushButton("Add")
self.addbtn.clicked.connect(self.addComboBox)
def organize(self):
grid = QGridLayout(self)
self.setLayout(grid)
grid.addWidget(Label, 0, 0, 0, 2)
grid.addWidget(self.comboBox, 1, 2, 1, 3)
grid.addWidget(self.addbtn, 2, 2)
def addComboBox(self):
#Code to add a combo box just like self.comboBox above addbtn and below all existing comboBoxes.
What I want
Sorry. If I understand you correctly, your example might look something like this:
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class CheckableComboBox(QComboBox):
def __init__(self, parent = None):
super(CheckableComboBox, self).__init__(parent)
self.parent = parent
self.setView(QListView(self))
self.view().pressed.connect(self.handleItemPressed)
self.setModel(QStandardItemModel(self))
def handleItemPressed(self, index):
item = self.model().itemFromIndex(index)
if item.checkState() == Qt.Checked:
item.setCheckState(Qt.Unchecked)
else:
item.setCheckState(Qt.Checked)
self.on_selectedItems()
def checkedItems(self):
checkedItems = []
for index in range(self.count()):
item = self.model().item(index)
if item.checkState() == Qt.Checked:
checkedItems.append(item)
return checkedItems
def on_selectedItems(self):
selectedItems = self.checkedItems()
self.parent.lblSelectItem.setText("")
for item in selectedItems:
self.parent.lblSelectItem.setText("{} {} "
"".format(self.parent.lblSelectItem.text(), item.text()))
class ExampleWidget(QGroupBox):
def __init__(self, numAddWidget):
QGroupBox.__init__(self)
self.numAddWidget = numAddWidget
self.numAddItem = 1
self.setTitle("Title {}".format(self.numAddWidget))
self.initSubject()
self.organize()
def initSubject(self):
self.lblName = QLabel("Label Title {}".format(self.numAddWidget), self)
self.lblSelectItem = QLabel(self)
self.teachersselect = CheckableComboBox(self)
self.teachersselect.addItem("-Select {}-".format(self.numAddItem))
item = self.teachersselect.model().item(0, 0)
item.setCheckState(Qt.Unchecked)
self.addbtn = QPushButton("ComboBoxAddItem...", self)
self.addbtn.clicked.connect(self.addTeacher)
def organize(self):
grid = QGridLayout(self)
self.setLayout(grid)
grid.addWidget(self.lblName, 0, 0, 1, 3)
grid.addWidget(self.lblSelectItem, 1, 0, 1, 2)
grid.addWidget(self.teachersselect, 1, 2)
grid.addWidget(self.addbtn, 3, 2)
def addTeacher(self):
self.numAddItem += 1
self.teachersselect.addItem("-Select {}-".format(self.numAddItem))
item = self.teachersselect.model().item(self.numAddItem-1, 0)
item.setCheckState(Qt.Unchecked)
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.numAddWidget = 1
self.initUi()
def initUi(self):
self.layoutV = QVBoxLayout(self)
self.area = QScrollArea(self)
self.area.setWidgetResizable(True)
self.scrollAreaWidgetContents = QWidget()
self.scrollAreaWidgetContents.setGeometry(0, 0, 200, 100)
self.layoutH = QHBoxLayout(self.scrollAreaWidgetContents)
self.gridLayout = QGridLayout()
self.layoutH.addLayout(self.gridLayout)
self.area.setWidget(self.scrollAreaWidgetContents)
self.add_button = QPushButton("Add Widget")
self.layoutV.addWidget(self.area)
self.layoutV.addWidget(self.add_button)
self.add_button.clicked.connect(self.addWidget)
self.widget = ExampleWidget(self.numAddWidget)
self.gridLayout.addWidget(self.widget)
self.setGeometry(700, 200, 350, 300)
def addWidget(self):
self.numAddWidget += 1
self.widget = ExampleWidget(self.numAddWidget)
self.gridLayout.addWidget(self.widget)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyApp()
w.show()
sys.exit(app.exec_())