我应该如何在 PyQt5 的 Table Widgets 中连接 CheckBox 单击信号?

Tod*_*ddP 2 python pyqt qtablewidget python-3.x pyqt5

我想将各种小部件添加到表格小部件中的各种单元格,并且我想在这些小部件的值发生更改时触发命令。我可以根据需要将小部件放入表格中,但是我在连接信号时遇到问题,因此我知道哪个小部件生成了信号。

下面是一个简单的例子来解释这个问题,只使用复选框:

屏幕截图:TableWidget、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_()
Run Code Online (Sandbox Code Playgroud)

基于table.cellChanged.connect我天真地期望cellChanged更改复选框时的信号。然而,不产生该信号。itemChanged信号也不行。我确实可以看到clicked信号,但这不是很有用,因为不清楚哪个复选框产生了信号。

解决问题的一种方法是checkbox_clicked为每个复选框创建一个不同的函数,但这似乎不太优雅。

我的问题是:

  1. 为什么更改复选框时既不生成cellChanged也不itemChanged生成信号?

  2. 应该如何连接信号才能知道哪个复选框生成了clicked信号?

eyl*_*esc 6

  1. 为什么更改复选框时既不生成 cellChanged 也不生成 itemChanged 信号?

因为当你使用setCellWidget()一个QTableWidgetItem不创建,如果我们检查的文件cellChangeditemChanged

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

每当由行和列指定的单元格中的项目数据发生更改时,就会发出此信号。

void QTableWidget::itemChanged(QTableWidgetItem *item)

每当item的数据发生变化时,就会发出此信号。


  1. 应该如何连接信号才能知道哪个复选框产生了点击信号?

获取的方式是间接的,首先要知道的是,通过setCellWidget()方法添加widget的时候viewport(), 的QTableWidget被设置为父级。

另外应该知道的另一件事是,被访问的小部件的位置pos()是相对于父级的,也就是说,在我们的例子中是相对于viewport().

有一个非常有用的方法被调用sender(),它返回发出信号的对象,在这种情况下它将返回QCheckBox.

由于小部件相对于 的位置viewport()是已知的,QModelIndex因此通过indexAt()方法访问它,QModelIndex具有单元格的信息。

以上所有内容都在以下示例中实现:

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_())
Run Code Online (Sandbox Code Playgroud)

另一种方法是通过 lambda 方法或 partial.functions 直接传递新参数。

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_())
Run Code Online (Sandbox Code Playgroud)

如果您想了解更多有关如何通过 connect() 传递额外参数的信息,您可以查看此答案