How should I connect CheckBox clicked signals in Table Widgets in PyQt5?

我的未来我决定 提交于 2021-01-27 21:08:51

问题


I would like to add various widgets to various cells in a Table Widget, and I would like to trigger commands when those widgets' values are changed. I can get the widgets into the table as desired, but I'm having problems connecting signals so that I know which widget has generated the signal.

Below is a simple example explaining the problem, using just checkboxes:

from PyQt5 import QtWidgets, QtGui, QtCore


class Main(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        # create table:
        self.table = QtWidgets.QTableWidget()
        [self.table.insertRow(i) for i in [0,1,2]]
        [self.table.insertColumn(i) for i in [0,1]]
        # set values for first column:
        self.table.setItem(0, 0, QtWidgets.QTableWidgetItem('A') )
        self.table.setItem(1, 0, QtWidgets.QTableWidgetItem('B') )
        self.table.setItem(2, 0, QtWidgets.QTableWidgetItem('C') )
        # add checkboxes to second column:
        cb0  = QtWidgets.QCheckBox( parent=self.table )
        cb1  = QtWidgets.QCheckBox( parent=self.table )
        cb2  = QtWidgets.QCheckBox( parent=self.table )
        self.table.setCellWidget(0, 1, cb0)
        self.table.setCellWidget(1, 1, cb1)
        self.table.setCellWidget(2, 1, cb2)
        # connect table signals:
        self.table.cellChanged.connect(self.cell_changed)
        self.table.itemChanged.connect(self.item_changed)
        # connect checkbox signals:
        cb0.clicked.connect(self.checkbox_clicked)
        cb1.clicked.connect(self.checkbox_clicked)
        cb2.clicked.connect(self.checkbox_clicked)
        # show:
        self.setCentralWidget(self.table)
        self.setWindowTitle('TableWidget, CheckBoxes')
        self.show()

    def cell_changed(self, row, col):
        print(row, col)
    def checkbox_clicked(self, checked):
        print(checked)
    def item_changed(self, item):
        print(item)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    main = Main()
    app.exec_()

Based on table.cellChanged.connect I would naively expect a cellChanged signal when the checkboxes are changed. However this signal is not generated. Nor is the itemChanged signal. I can indeed see the clicked signals, but that is not very useful because it is unclear which checkbox has produced the signal.

One way to solve the problem is to create a different checkbox_clicked function for each checkbox, but that hardly seems elegant.

My questions are:

  1. Why is neither a cellChanged nor an itemChanged signal generated when a checkbox is changed?

  2. How should signals be connected in order to know which checkbox has generated the clicked signal?


回答1:


  1. Why is neither a cellChanged nor an itemChanged signal generated when a checkbox is changed?

because when you use setCellWidget() a QTableWidgetItem is not created, and if we check the documentation of cellChanged and itemChanged:

void QTableWidget::cellChanged(int row, int column)

This signal is emitted whenever the data of the item in the cell specified by row and column has changed.

void QTableWidget::itemChanged(QTableWidgetItem *item)

This signal is emitted whenever the data of item has changed.


  1. How should signals be connected in order to know which checkbox has generated the clicked signal?

The way to obtain is indirectly, the first thing to know is that when the widget is added through the setCellWidget() method, the viewport() of the QTableWidget is set as a parent.

Also another thing that should be known is that the position of a widget that is accessed through pos() is relative to the parent, that is, in our case relative to viewport().

There is a very useful method called sender() that returns the object that emits the signal, in this case it will return the QCheckBox.

As the position of the widget with respect to the viewport() is known, its QModelIndex is accessed through the indexAt() method, the QModelIndex has the information of the cell.

All of the above is implemented in the following example:

from PyQt5 import QtWidgets, QtGui, QtCore


class Main(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        # create table:
        self.table = QtWidgets.QTableWidget()
        self.table.setRowCount(3)
        self.table.setColumnCount(2)
        for i, letter in enumerate("ABC"):
            self.table.setItem(i, 0, QtWidgets.QTableWidgetItem(letter))
        for i in range(self.table.rowCount()):
            ch = QtWidgets.QCheckBox(parent=self.table)
            ch.clicked.connect(self.onStateChanged)
            self.table.setCellWidget(i, 1, ch)
        self.setCentralWidget(self.table)
        self.setWindowTitle('TableWidget, CheckBoxes')
        self.show()

    def onStateChanged(self):
        ch = self.sender()
        print(ch.parent())
        ix = self.table.indexAt(ch.pos())
        print(ix.row(), ix.column(), ch.isChecked())


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    main = Main()
    sys.exit(app.exec_())

Another way to do it is through lambda methods or partial.functions where we pass directly new parameters.

from PyQt5 import QtWidgets, QtGui, QtCore


class Main(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        # create table:
        self.table = QtWidgets.QTableWidget()
        self.table.setRowCount(3)
        self.table.setColumnCount(2)
        for i, letter in enumerate("ABC"):
            self.table.setItem(i, 0, QtWidgets.QTableWidgetItem(letter))
        for i in range(self.table.rowCount()):
            ch = QtWidgets.QCheckBox(parent=self.table)
            ch.clicked.connect(lambda checked, row=1, col=i: self.onStateChanged(checked, row, col))
            self.table.setCellWidget(i, 1, ch)
        self.setCentralWidget(self.table)
        self.setWindowTitle('TableWidget, CheckBoxes')
        self.show()

    def onStateChanged(self, checked, row, column):
        print(checked, row, column)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    main = Main()
    sys.exit(app.exec_())

If you want to know more information how to pass extra parameters through connect() you can review this answer.



来源:https://stackoverflow.com/questions/48057638/how-should-i-connect-checkbox-clicked-signals-in-table-widgets-in-pyqt5

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!