python - 在另一个线程中编辑抽象模型后如何正确更新 pyQt 中的视图?
问题描述
我正在尝试从另一个 threading.Thread 设置我的 QAbstractTableModel(连接到 QTableView)的 setData()。模型中的数据按预期更改,但视图不会自行更新(仅在单击触发视图更新的表视图后)。实施此类更新的最佳方式是什么?
我正在使用 pyqt 5.11.1 开发 Python 3.6。我试图从我的模型的 setData 方法发出 dataChanged(以及 layoutAboutToBeChanged、layoutChanged、editCompleted)信号——这些都不起作用。然后我想出了两个可能的解决方案 -
- 从 setData 发出 modelReset 或
- 在模型中制作 QTimer 并将其连接到为模型的所有索引发出 dataChanged 的方法
两者都按预期工作,但我认为这不是一个很好的解决方案,因为首先要更新整个表(我相信是这样)而且它不是真正健康的用例吗?除了显示数据的一些延迟之外,第二种解决方案只会给应用程序带来恒定的负载。
这是我的问题的最小(希望如此)可重现的例子
import sys
import threading
import time
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt as Qt
class CopterDataModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None):
super(CopterDataModel, self).__init__(parent)
self.data_contents = [[1, 2]]
def rowCount(self, n=None):
return len(self.data_contents)
def columnCount(self, n=None):
return 2
def data(self, index, role):
row = index.row()
col = index.column()
#print('row {}, col {}, role {}'.format(row, col, role)) #for debug
if role == Qt.DisplayRole:
return self.data_contents[row][col] or ""
@QtCore.pyqtSlot()
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid():
return False
if role == Qt.EditRole:
self.data_contents[index.row()][index.column()] = value
print("edit", value)
self.modelReset.emit() # working fine
#self.dataChanged.emit(index, index, [Qt.EditRole]) # NOT WORKING
else:
return False
return True
def flags(self, index):
roles = Qt.ItemIsSelectable | Qt.ItemIsEnabled
return roles
if __name__ == '__main__':
def timer():
idc = 1001
while True:
myModel.setData(myModel.index(0, 0), idc)
idc += 1
time.sleep(1)
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
tableView = QtWidgets.QTableView()
myModel = CopterDataModel(None)
tableView.setModel(myModel)
tableView.show()
t = threading.Thread(target=timer, daemon=True)
t.start()
app.exec_()
表视图的索引 (0, 0) 应该每秒更新一次,计数器递增(当我尝试发出 dataChanged 信号时不会发生这种情况,仅使用 modelReset)。(请注意,这只是线程的最小示例,在实际代码中具有更复杂的逻辑,并且数据不是“在计时器”时传入的)
来自https://github.com/Taar2/pyqt5-modelview-tutorial/blob/master/modelview_3.py的计时器调整也使其工作(上述解决方案的缺点)。
我希望信号以相同的方式工作,但由于某种原因它没有发生,并且视图不会使用从线程调用的 dataChanged 信号进行更新。
解决方案
直接从另一个线程访问模型是不好的,因为 QObjects 不是线程安全的,而是创建了一个 QObject,通过信号将数据发送到主线程,在这种情况下,为了一个简单的操作,我创建了 slot update_item接收行、列和数据。
import sys
import threading
import time
from PyQt5 import QtCore, QtGui, QtWidgets
class CopterDataModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None):
super(CopterDataModel, self).__init__(parent)
self.data_contents = [[1, 2]]
def rowCount(self, n=None):
return len(self.data_contents)
def columnCount(self, n=None):
return 2
def data(self, index, role):
row = index.row()
col = index.column()
# print('row {}, col {}, role {}'.format(row, col, role)) #for debug
if role == QtCore.Qt.DisplayRole:
return self.data_contents[row][col] or ""
def setData(self, index, value, role=QtCore.Qt.EditRole):
if not index.isValid():
return False
if role == QtCore.Qt.EditRole:
self.data_contents[index.row()][index.column()] = value
print("edit", value)
self.dataChanged.emit(
index, index, (QtCore.Qt.EditRole,)
) # NOT WORKING
else:
return False
return True
def flags(self, index):
return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
@QtCore.pyqtSlot(int, int, QtCore.QVariant)
def update_item(self, row, col, value):
ix = self.index(row, col)
self.setData(ix, value)
class SignalManager(QtCore.QObject):
fooSignal = QtCore.pyqtSignal(int, int, QtCore.QVariant)
if __name__ == "__main__":
def timer(obj):
idc = 1001
while True:
obj.fooSignal.emit(0, 0, idc)
idc += 1
time.sleep(1)
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
foo = SignalManager()
tableView = QtWidgets.QTableView()
myModel = CopterDataModel()
foo.fooSignal.connect(myModel.update_item)
tableView.setModel(myModel)
tableView.show()
t = threading.Thread(target=timer, args=(foo,), daemon=True)
t.start()
app.exec_()
推荐阅读
- sage - Sagemath 作为 python 库
- typescript - 打字稿:合并两种类型/接口并保持所有泛型有效
- javascript - 使用路径参数时 Next.js 应用程序 404
- .net-core - 随机出现的问题:使用 Hangfire 的“无法解析类型 (...) 的服务”
- ionic4 - Gif 图像在 ionic-5 闪屏中不起作用
- python - 在某个时间出现/某种格式的字符出现之前拆分
- java - Java Android,遍历所有getter
- json - 从 Go 中的流中打印/记录完整的非结构化 json
- kotlin - Kotlin 箭头库中 Either.fold() 的返回类型
- typescript - 在 Typescript 中为字典的某些键指定不同的类型