Get state of checkbox cellWidget in QTableWidget PyQT - python

I work on python plugin for QGIS. In this plugin I have created a QTableWidget with 3 columns. These columns are QCheckbox, QTableWidgetItem and QComboBox. I would like to retrieve the values contained in these 3 columns. For the moment I managed to get the values of QComboBox and QTableWidgetItem but I can't seem to get the value of the QCheckBox.
liste = ['Carte 1','Carte 2','Carte 3','Carte 4','Carte 5','Carte 6']
combo_box_options = ["A4 Paysage","A4 Portrait", "A3 Paysage","A3 Portrait"]
self.dlg_format = Dialog_format()
self.dlg_format.tableWidget.setRowCount(len(liste))
for index in range(len(liste)):
item = QTableWidgetItem(liste[index])
self.dlg_format.tableWidget.setItem(index, 1, item)
self.dlg_format.tableWidget.setColumnWidth(0, 20)
self.dlg_format.tableWidget.setColumnWidth(1, 350)
combo = QComboBox()
for t in combo_box_options:
combo.addItem(t)
self.dlg_format.tableWidget.setCellWidget(index, 2, combo)
widget = QWidget()
checkbox = QCheckBox()
checkbox.setCheckState(Qt.Checked)
playout = QHBoxLayout(widget)
playout.addWidget(checkbox)
playout.setAlignment(Qt.AlignCenter)
playout.setContentsMargins(0,0,0,0)
widget.setLayout(playout)
self.dlg_format.tableWidget.setCellWidget(index, 0, widget)
self.dlg_format.show()
result = self.dlg_format.exec_()
if result:
for index in range(len(liste)):
text = self.dlg_format.tableWidget.item(index, 1).text()
format = self.dlg_format.tableWidget.cellWidget(index, 2).currentText()
check = self.dlg_format.tableWidget.cellWidget(index, 0).checkState() #Does not work

The QWidget is what is set as cell widget, not the checkbox, and that widget obviously has no checkState attribute.
There are various possibilities for this scenario.
Make the checkbox an attribute of the widget:
widget = QWidget()
widget.checkbox = QCheckBox()
playout.addWidget(widget.checkbox)
# ...
check = self.dlg_format.tableWidget.cellWidget(index, 0).checkbox.checkState()
Make the checkbox's checkState function a reference of the widget (note: no parentheses!) so that you can access it with the existing cellWidget(index, 0).checkState():
checkbox = QCheckBox()
widget.checkState = checkbox.checkState
Since all happens within the same scope (the function), you can totally ignore the cellWidget and use a list of tuples that contains the widgets:
widgets = []
for index in range(len(liste)):
# ...
widgets.append((item, combo, checkbox))
# ...
if result:
for item, combo, checkbox in widgets:
text = item.text()
format = combo.currentText()
check = checkbox.checkState()
Note that:
checkState()
returns a Qt.CheckState enum, which results in 2 (Qt.Checked) for a checked box; if you need a boolean, use isChecked() instead;
you can use enumerate instead of range, since you are iterating through the list items anyway: for index, text in enumerate(liste):;
if you don't need to add item data and the contents of the combo are always the same, just use combo.addItems(combo_box_options);
setting the column width for every cycle is pointless, just do it once outside the for loop;
if you use QHBoxLayout(widget) there's no need for widget.setLayout(playout), as the widget argument on a layout already sets that layout on the widget;
instance attribute are created in order to make them persistent (it ensures that they are not garbage collected and allows future access); from your code it seems unlikely that you're going to use that dialog instance after that function returns, so making it a member of the instance (self.dlg_format) is unrequired and keeps resources unnecessarily occupied: the dialog would be kept in memory even after it's closed, and would be then deleted and overwritten as soon as it's created again; just make it a local variable (dlg_format = Dialog_format());

Related

How to get functional combobox in QTableView

As the title suggests I'm looking to get a combobox in a QTableView.
I've looked at several other questions that deal with comboboxes in tableviews, but they mostly concern comboboxes as editors and that is not what I'm looking for.
I would like the combobox to be visible at all times and get its data from the underlying model. It doesn't have to set data in the model.
I tried to adapt this example of a progress bar delegate: How to include a column of progress bars within a QTableView?
Leading to this code:
class ComboDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
combo = QStyleOptionComboBox()
combo.rect = option.rect
combo.currentText = 'hallo' # just for testing
combo.editable = True
combo.frame = False
QApplication.style().drawComplexControl(QStyle.CC_ComboBox, combo, painter)
But this gives me a greyed out, non functional combobox.
How do you get a functional combobox there?
As the name suggests, the paint() function only draws a combobox, it does not create one.
If you want a persistent widget set for an item, and that widget doesn't need to update the model, then you should use setIndexWidget().
A basic implementation on a static model could be like this:
class SomeWidget(QWidget):
def __init__(self):
# ...
self.table.setModel(someModel)
for row in range(someModel.rowCount()):
combo = QComboBox(editable=True)
combo.addItem('hallo')
self.table.setIndexWidget(someModel.index(row, 2), combo)
If the model can change at runtime (and is possibly empty at startup), then you need to connect to the rowsInserted signal:
class SomeWidget(QWidget):
def __init__(self):
# ...
self.updateCombos()
someModel.rowsInserted.connect(self.updateCombos)
def updateCombos(self):
for row in range(self.table.model().rowCount()):
index = someModel.index(row, 2)
if self.table.indexWidget(index):
continue
combo = QComboBox(editable=True)
combo.addItem('hallo')
self.table.setIndexWidget(index, combo)
Then you can access any combo based on the index row:
def getComboValue(self, row):
index = self.table.model().index(row, 2)
widget = self.table.indexWidget(index)
if isinstance(widget, QComboBox):
return widget.currentText()
Remember: whenever you're studying the documentation, you must also review the documentation of all inherited classes. In this case you should not only read the docs about QTableView, but also the whole inheritance tree: QTableView > QAbstractItemView > QAbstractScrollArea > QFrame > QWidget > QObject and QPaintDevice.

Python destroy only tkinter subclass widget

I wrote a subclass with tkinter widgets. In a for-loop i place few of them in a Frame. This Frame also contains a Label and a Entry.
Now i want to destroy all of my subclass widgets but NOT the Label and the Entry.
I tried it like this:
for child in self.frame.winfo_children():
if child.winfo_class() == "???":
[...]
But I wasnt able to figure out what i have to use, so i will use ??? as a placeholder for this.
I place them in a rule with this Code:
db.execute("SELECT * FROM UsedSystems")
rows = db.fetchall()
i = 0
for row in rows:
image_path = activepath+rows[i][0]
name = rows[i][1]
performance = rows[i][2]
project = rows[i][3]
date = rows[i][4]
self.e10 = CustomWidget(self.frame, image_path, name, performance, project, date)
self.e10.grid(row=1+i,column=0, columnspan=2)
i+=1
Try using the isinstance built-in function to check the class (as shown below):
for child in self.frame.winfo_children():
if not (isinstance (child, Label) or isinstance (child, Entry)):
child.destroy ()
This will destroy any widget if they are not a Label and Entry. However, it cannot distinguish between different Label widgets (for example) and will leave BOTH.

How to delete (or de-grid) Tkinter GUI objects stored in a list (Python)

I'm trying to make a program that creates some random amount of GUI objects in Tkinter and stores them in a list. Here (in the code below) I have a for loop that creates a random amount of radio buttons. Each time a radio button object is created it is stored in the list 'GUIobjects'. I do this because otherwise I have no way of accessing the GUI objects later on. What I need to know, now, is how to delete or de-grid the objects.
I have tried self.radioButton.grid_forget(), but this only de-grids the last object created. I'm not sure of there's a way to access each object in the list and use .grid_forget(). If there is, that would be an option.
For now all I need to know is how to delete or de-grid the GUI objects after I create all of them.
from tkinter import *
import random
class App(Tk):
def __init__(self):
Tk.__init__(self)
self.addButton()
def addButton(self)
GUIobjects = []
randInt = random.randint(3, 10)
self.radVar = IntVar()
for x in range(2, randInt):
self.radioButton = Radiobutton(self, text = "Button", variable = self.RadVar, value = x)
self.radioButton.grid(row = x)
print(GUIobjects)
# This is to show that one more object has been created than appears on the screen
self.radioButton.grid_forget()
# This de-grid's the last object created, but I need to de-grid all of them
def main():
a = App()
a.mainloop()
if __name__ == "__main__":
main()
As of right now, I try to de-grid the objects right after creating all of them. Once I find out how to de-grid each object, I will somehow need to make a button that de-grid's them (as compared to de-griding them right after they have been created). This button should be able to be put in a method other than 'addButtons' but still in the 'App' class.
Thanks!
You need to store references to each object. Create an empty list and append the reference to the list inside your loop.
self.radioButtons = []
for x in range(2, randInt):
self.radioButtons.append(Radiobutton(self, text = "Button", variable = self.RadVar, value = x))
self.radioButtons[-1].grid(row = x) # layout the most recent one
They won't be garbage collected unless you delete the reference as well.
for button in self.radioButtons:
button.grid_forget()
del self.radioButtons

PySide: How Could I trigger the current clicked QPushbutton, not other later added

I am new to PySide. In my program, I encountered a problem that when I click one button, it triggers other button later added. Thanks!
self.addContentButton = QtGui.QPushButton('Add')
self.addContentButton.clicked.connect(self.addContent)
def addContent(self):
'''
slot to add a row that include a lineedit, combobox, two buttons
'''
self.contentTabHBoxWdgt = QtGui.QWidget()
self.contentName = QtGui.QLineEdit('line edit')
self.conetentTypeBox = QtGui.QComboBox()
self.conetentTypeBox.addItem('elem1')
self.conetentTypeBox.addItem('elem2')
self.contentSave = QtGui.QPushButton('save',parent = self.contentTabHBoxWdgt)
self.contentSave.clicked.connect(self.contntSaveAct)
self.contentDelete = QtGui.QPushButton('delete',parent=self.contentTabHBoxWdgt)
self.contentDelete.clicked.connect(self.contntDel)
self.contentTabHBox = QtGui.QHBoxLayout()
self.contentTabHBox.addWidget(self.contentName)
self.contentTabHBox.addWidget(self.conetentTypeBox)
self.contentTabHBox.addWidget(self.contentSave)
self.contentTabHBox.addWidget(self.contentDelete)
self.contentTabHBoxWdgt.setLayout(self.contentTabHBox)
self.contentTabVBox.addWidget(self.contentTabHBoxWdgt)
def contntDel(self):
'''
slot to delete a row
'''
msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, '', 'Be sure to delete')
okBttn = msgBox.addButton('Yes', QtGui.QMessageBox.AcceptRole)
noBttn = msgBox.addButton('Cancel', QtGui.QMessageBox.RejectRole)
ret = msgBox.exec_()
if msgBox.clickedButton() == okBttn:
self.contentTabVBox.removeWidget(self.contentDelete.parentWidget());
When I Add one row and click its delete button, it does not work as expected.While I add two or three row , I click one delete button , it remove one row that is not the clicked delete button belong to. How could I achieve this function. Ths!
Your problem is because you aren't really taking advantage of object oriented programming properly.
All rows in your example call the same instance of the method contntDel. This method uses self.contentDelete which always contains a reference to the last row added.
What you need to do is separate out everything related to a row to a new class. When you add a row, create a new instance of this class and pass in the contentTabVBox. That way each row (or instance of the new class you will write) will have it's own delete method.
Without a complete code example, I can't provide a complete solution, but this should give you a rough idea:
class MyRow(object):
def __init__(self,contentTabVBox, rows):
self.contentTabVBox = contentTabVBox
self.my_list_of_rows = rows
self.addContent()
def addContent(self):
# The code for your existing addContent method here
def contntDel(self):
# code from your existing contntDel function here
# also add (if Ok button clicked):
self.my_list_of_rows.remove(self)
class MyExistingClass(??whatever you have here normally??):
def __init__(....):
self.addContentButton = QtGui.QPushButton('Add')
self.addContentButton.clicked.connect(self.addContent)
self.my_list_of_rows = []
def addContent(self):
my_new_row = MyRow(self.contentTabVBox,self.my_list_of_rows)
# You mustsave a reference to my_new_row in a list or else it will get garbage collected.
self.my_list_of_rows.append(my_new_row)
Hope that helps!

Updating dynamic QGridLayout - Python PyQt

I recently started studying Python and now I'm making a software with a GUI using PyQt Libraries.
Here's my problem:
I create a Scrollarea, I put in this scrollarea a widget which contains a QGridLayout.
sa = QtGui.QScrollArea()
sa_widget = QtGui.QWidget()
self.sa_grid.setSizeConstraint(QtGui.QLayout.SetMinAndMaxSize)
sa_widget.setLayout(self.sa_grid)
sa.setWidgetResizable(True)
sa.setWidget(sa_widget)
Then I add 10 QLabel (this is just an example of course, in this example I'm using a QGridLayout just like a Vertical Layout):
i = 0
while i<100:
i = i +1
add = QtGui.QLabel("Row %i" % i)
self.sa_grid.addWidget(add)
Then I create a button that calls the function "function_name", I want that this function deletes a row, so far this is what I've written:
tmp = QtGui.QWidget()
tmp = self.sa_grid.itemAt(0)
self.sa_grid.removeItem(tmp)
It deletes the first row and every x row of the gridlayout becomes row x-1, however, it doesn't delete the text "Row 1" so I see "Row 0" and "Row 1" on the same row.
Anyone can help me?
Thanks a lot in advance, Davide
Removing an item from a layout does not delete it. The item will just become a free-floating object with no associated layout.
If you want to get rid of the object completely, explicitly delete it:
def deleteGridWidget(self, index):
item = self.sa_grid.itemAt(index)
if item is not None:
widget = item.widget()
if widget is not None:
self.sa_grid.removeWidget(widget)
widget.deleteLater()

Categories

Resources