[英]Why can I emit dataChanged(), but not layoutChanged() in a PySide2 table model?
I am new to Qt.我是 Qt 的新手。 Currently I am trying to learn how to update a table model from a different thread and then how to get an immediate display update for it.目前,我正在尝试学习如何从不同的线程更新表 model,然后如何为其获取即时显示更新。 I read the documentation and found the dataChanged()
and layoutChanged()
signals.我阅读了文档并找到了dataChanged()
和layoutChanged()
信号。 While dataChanged()
works fine, any attempt to emit layoutChanged()
fails with:虽然dataChanged()
工作正常,但任何发出layoutChanged()
的尝试都会失败:
'QObject::connect: Cannot queue arguments of type 'QList<QPersistentModelIndex>' (Make sure 'QList<QPersistentModelIndex>' is registered using qRegisterMetaType().)
Searching for this particular error didn't give me anything that I could turn into working code.搜索这个特定的错误并没有给我任何可以变成工作代码的东西。 I am not using any QList
or QPersistentModelIndex
explicitly, but of course that can be implicitly used due to the constructs that I chose.我没有显式使用任何QList
或QPersistentModelIndex
,但由于我选择的构造,当然可以隐式使用它们。
What am I doing wrong?我究竟做错了什么?
class TimedModel(QtCore.QAbstractTableModel):
def __init__(self, table, view):
super(TimedModel, self).__init__()
self.table = table
self.view = view
self.setHeaderData(0, Qt.Horizontal, Qt.AlignLeft, Qt.TextAlignmentRole)
self.rows = 6
self.columns = 4
self.step = 5
self.timer = Thread(
name = "Timer",
target = self.tableTimer,
daemon = True)
self.timer.start()
self.random = Random()
self.updated = set()
@staticmethod
def encode(row, column):
return row << 32 | column
def data(self, index, role):
if role == Qt.DisplayRole or role == Qt.EditRole:
return f'Data-{index.row()}-{index.column()}'
if role == Qt.ForegroundRole:
encoded = TimedModel.encode(index.row(), index.column())
return QBrush(Qt.red if encoded in self.updated else Qt.black)
return None
def rowCount(self, index):
return self.rows
def columnCount(self, index):
return self.columns
def headerData(self, col, orientation, role):
if orientation == Qt.Vertical:
# Vertical
return super().headerData(col, orientation, role)
# Horizontal
if not 0 <= col < self.columns:
return None
if role == Qt.DisplayRole:
return f'Data-{col}'
if role == Qt.TextAlignmentRole:
return int(Qt.AlignLeft | Qt.AlignVCenter)
return super().headerData(col, orientation, role)
def tableTimer(self):
while True:
time.sleep(5.0)
randomRow = self.random.randint(0, self.rows)
randomColumn = self.random.randint(0, self.columns)
encodedRandom = TimedModel.encode(randomRow, randomColumn)
if encodedRandom in self.updated:
self.updated.remove(encodedRandom)
else:
self.updated.add(encodedRandom)
updatedIndex = self.createIndex(randomRow, randomColumn)
self.dataChanged.emit(updatedIndex, updatedIndex)
'''this here does not work:'''
self.layoutAboutToBeChanged.emit()
self.rows += self.step
self.layoutChanged.emit()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.timedTable = QTableView()
self.model = TimedModel(self.timedTable, self)
self.timedTable.setModel(self.model)
headerView = self.timedTable.horizontalHeader()
headerView.setStretchLastSection(True)
self.setCentralWidget(self.timedTable)
self.setGeometry(300, 300, 1000, 600)
self.setWindowTitle('Timed Table')
self.show()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.name = "Timed Table Application"
window = MainWindow()
window.show()
app.exec_()
The following code:以下代码:
self.layoutAboutToBeChanged.emit()
self.rows += self.step
self.layoutChanged.emit()
create new model elements that have QPersistentModelIndex
associated that are not thread-safe and that Qt monitors its creation to warn its misuse as in this case since modifying that element is unsafe since it implies modifying the GUI from another thread (Read here for more information).创建新的 model 元素,这些元素具有关联的QPersistentModelIndex
不是线程安全的,并且 Qt 监视其创建以警告其滥用,因为在这种情况下修改该元素是不安全的,因为它意味着从另一个线程修改 GUI(阅读此处了解更多信息) .
So you see that message warning that what you are trying to do is unsafe.因此,您会看到该消息警告您尝试执行的操作不安全。
Instead dataChanged
only emits a signal, does not create any element belonging to Qt, and you have been lucky that the modification of "self.updated" has not generated bottlenecks since you modify a property that belongs to the main thread from a secondary thread without use guards as mutexes.相反, dataChanged
只发出一个信号,不会创建任何属于 Qt 的元素,而且您很幸运,“self.updated”的修改没有产生瓶颈,因为您从辅助线程修改了属于主线程的属性而没有使用守卫作为互斥锁。
Qt points out that the GUI and the elements that the GUI uses should only be updated in the GUI thread, and if you want to modify the GUI with information from another thread, then you must send that information, for example, using the signals that are thread- safe: Qt 指出 GUI 和 GUI 使用的元素只能在 GUI 线程中更新,如果您想使用来自另一个线程的信息修改 GUI,那么您必须发送该信息,例如,使用是线程安全的:
import random
import sys
import threading
import time
from PySide2 import QtCore, QtGui, QtWidgets
class TimedModel(QtCore.QAbstractTableModel):
random_signal = QtCore.Signal(object)
def __init__(self, table, view):
super(TimedModel, self).__init__()
self.table = table
self.view = view
self.setHeaderData(
0, QtCore.Qt.Horizontal, QtCore.Qt.AlignLeft, QtCore.Qt.TextAlignmentRole
)
self.rows = 6
self.columns = 4
self.step = 5
self.updated = set()
self.random_signal.connect(self.random_slot)
self.timer = threading.Thread(name="Timer", target=self.tableTimer, daemon=True)
self.timer.start()
@staticmethod
def encode(row, column):
return row << 32 | column
def data(self, index, role):
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
return f"Data-{index.row()}-{index.column()}"
if role == QtCore.Qt.ForegroundRole:
encoded = TimedModel.encode(index.row(), index.column())
return QtGui.QBrush(
QtCore.Qt.red if encoded in self.updated else QtCore.Qt.black
)
return None
def rowCount(self, index):
return self.rows
def columnCount(self, index):
return self.columns
def headerData(self, col, orientation, role):
if orientation == QtCore.Qt.Vertical:
# Vertical
return super().headerData(col, orientation, role)
# Horizontal
if not 0 <= col < self.columns:
return None
if role == QtCore.Qt.DisplayRole:
return f"Data-{col}"
if role == QtCore.Qt.TextAlignmentRole:
return QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter
return super().headerData(col, orientation, role)
def tableTimer(self):
while True:
time.sleep(5.0)
randomRow = random.randint(0, self.rows)
randomColumn = random.randint(0, self.columns)
encodedRandom = TimedModel.encode(randomRow, randomColumn)
self.random_signal.emit(encodedRandom)
@QtCore.Slot(object)
def random_slot(self, encodedRandom):
if encodedRandom in self.updated:
self.updated.remove(encodedRandom)
else:
self.updated.add(encodedRandom)
self.layoutAboutToBeChanged.emit()
self.rows += self.step
self.layoutChanged.emit()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.timedTable = QtWidgets.QTableView()
self.model = TimedModel(self.timedTable, self)
self.timedTable.setModel(self.model)
headerView = self.timedTable.horizontalHeader()
headerView.setStretchLastSection(True)
self.setCentralWidget(self.timedTable)
self.setGeometry(300, 300, 1000, 600)
self.setWindowTitle("Timed Table")
self.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
app.name = "Timed Table Application"
window = MainWindow()
window.show()
app.exec_()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.