我应该如何在 PyQt5 的 Table Widgets 中连接 CheckBox clicked 信号?
How should I connect CheckBox clicked signals in Table Widgets in PyQt5?
我想将各种小部件添加到 Table 小部件中的各个单元格,并且我想在这些小部件的值更改时触发命令。我可以根据需要将小部件放入 table,但我在连接信号时遇到问题,因此我知道哪个小部件生成了信号。
下面是一个解释问题的简单示例,仅使用复选框:
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_()
基于 table.cellChanged.connect
我会天真地期望当复选框被更改时会出现 cellChanged
信号。但是,不会生成此信号。 itemChanged
信号也不是。我确实可以看到 clicked
信号,但这不是很有用,因为不清楚哪个复选框产生了信号。
解决该问题的一种方法是为每个复选框创建一个不同的 checkbox_clicked
函数,但这看起来并不优雅。
我的问题是:
为什么更改复选框时既不生成 cellChanged
也不生成 itemChanged
信号?
如何连接信号才能知道哪个复选框生成了 clicked
信号?
- Why is neither a cellChanged nor an itemChanged signal generated when
a checkbox is changed?
因为当您使用 setCellWidget()
时,不会创建 QTableWidgetItem
,如果我们检查 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.
- How should signals be connected in order to know which checkbox has generated the clicked signal?
获取的方式是间接的,首先要知道的是,通过setCellWidget()
方法添加widget时,QTableWidget
的viewport()
设置为parent.
还应该知道的另一件事是,通过 pos()
访问的小部件的位置是相对于 parent 的,也就是说,在我们的例子中是相对于 viewport()
.
有一个非常有用的方法叫做sender()
,它是return发出信号的object,在这种情况下它会return QCheckBox
.
由于widget相对于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_())
另一种方法是通过 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_())
如果您想了解更多信息,了解如何通过 connect() 传递额外参数,您可以查看 。
对复选框使用 stateChanged
信号。
我对该代码的看法:
- 在某些情况下,对于某些逻辑操作,引用复选框小部件会很有帮助。
- 尽可能使用循环
- 在 PyQt 中使用显式导入 - class 名称是唯一的并且更具可读性
例如:
from PyQt5.QtWidgets import QMainWindow, QTableWidgetItem, QCheckBox, QApplication
from typing import Dict
class Main(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# create table:
self.table = QTableWidget()
self.table.insertColumn(0)
self.table.insertColumn(1)
self._items: Dict[QTableWidgetItem, QCheckBox] = {}
for i, tag in enumerate(['A', 'B', 'C']):
self.table.insertRow(i)
item = QTableWidgetItem(tag)
cb = QCheckBox(parent=self.table)
self._items[item] = cb
# set values for first column:
self.table.setItem(i, 0, item)
# add checkboxes to second column:
self.table.setCellWidget(i, 1, cb)
# connect cb signals:
self._items[item].stateChanged.connect(self.checkbox_clicked)
# connect table signals:
self.table.cellChanged.connect(self.cell_changed)
# 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 = QApplication(sys.argv)
main = Main()
app.exec_()
我想将各种小部件添加到 Table 小部件中的各个单元格,并且我想在这些小部件的值更改时触发命令。我可以根据需要将小部件放入 table,但我在连接信号时遇到问题,因此我知道哪个小部件生成了信号。
下面是一个解释问题的简单示例,仅使用复选框:
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_()
基于 table.cellChanged.connect
我会天真地期望当复选框被更改时会出现 cellChanged
信号。但是,不会生成此信号。 itemChanged
信号也不是。我确实可以看到 clicked
信号,但这不是很有用,因为不清楚哪个复选框产生了信号。
解决该问题的一种方法是为每个复选框创建一个不同的 checkbox_clicked
函数,但这看起来并不优雅。
我的问题是:
为什么更改复选框时既不生成
cellChanged
也不生成itemChanged
信号?如何连接信号才能知道哪个复选框生成了
clicked
信号?
- Why is neither a cellChanged nor an itemChanged signal generated when a checkbox is changed?
因为当您使用 setCellWidget()
时,不会创建 QTableWidgetItem
,如果我们检查 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.
- How should signals be connected in order to know which checkbox has generated the clicked signal?
获取的方式是间接的,首先要知道的是,通过setCellWidget()
方法添加widget时,QTableWidget
的viewport()
设置为parent.
还应该知道的另一件事是,通过 pos()
访问的小部件的位置是相对于 parent 的,也就是说,在我们的例子中是相对于 viewport()
.
有一个非常有用的方法叫做sender()
,它是return发出信号的object,在这种情况下它会return QCheckBox
.
由于widget相对于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_())
另一种方法是通过 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_())
如果您想了解更多信息,了解如何通过 connect() 传递额外参数,您可以查看
对复选框使用 stateChanged
信号。
我对该代码的看法:
- 在某些情况下,对于某些逻辑操作,引用复选框小部件会很有帮助。
- 尽可能使用循环
- 在 PyQt 中使用显式导入 - class 名称是唯一的并且更具可读性
例如:
from PyQt5.QtWidgets import QMainWindow, QTableWidgetItem, QCheckBox, QApplication
from typing import Dict
class Main(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# create table:
self.table = QTableWidget()
self.table.insertColumn(0)
self.table.insertColumn(1)
self._items: Dict[QTableWidgetItem, QCheckBox] = {}
for i, tag in enumerate(['A', 'B', 'C']):
self.table.insertRow(i)
item = QTableWidgetItem(tag)
cb = QCheckBox(parent=self.table)
self._items[item] = cb
# set values for first column:
self.table.setItem(i, 0, item)
# add checkboxes to second column:
self.table.setCellWidget(i, 1, cb)
# connect cb signals:
self._items[item].stateChanged.connect(self.checkbox_clicked)
# connect table signals:
self.table.cellChanged.connect(self.cell_changed)
# 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 = QApplication(sys.argv)
main = Main()
app.exec_()