向QAbstractListModel子类新增行时QML视图未更新如何解决
问题原因
- 你使用的Python标准库
threading.Timer运行在独立于Qt主线程的工作线程中,Qt的模型-视图更新机制强制要求所有修改模型、触发模型通知信号的操作必须在UI主线程执行。跨线程调用addPerson方法时,beginInsertRows和endInsertRows发出的更新信号无法被QML视图正确接收,因此界面不会刷新。 - 代码中
deletePerson方法还存在笔误:调用的是beginRemoveColumns,应该改为beginRemoveRows,否则删除操作也会出现异常。
修复方案
方案1:替换线程定时器为Qt原生QTimer(推荐,适配定时器场景)
Qt的QTimer默认运行在主线程,信号触发的槽函数也会在主线程执行,完美适配视图更新要求,修改model2.py代码如下:
from PyQt5.QtCore import QAbstractListModel, Qt, pyqtSignal, pyqtSlot, QModelIndex, QTimer # 移除threading.Timer导入 class PersonModel(QAbstractListModel): NameRole = Qt.UserRole + 1 AgeRole = Qt.UserRole + 2 personChanged = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.persons = [ {'name': 'jon', 'age': 20}, {'name': 'jane', 'age': 25} ] # 替换为Qt原生单触发定时器 self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.timeout.connect(self.foo) self.timer.start(5000) # 单位为毫秒,5秒对应5000 # 其余代码不变,修改deletePerson的笔误: @pyqtSlot(int) def deletePerson(self, row): # 将beginRemoveColumns改为beginRemoveRows self.beginRemoveRows(QModelIndex(), row, row) del self.persons[row] self.endRemoveRows()
方案2:子线程更新模型通用方案(适配设备异步返回数据场景)
如果你的实际业务是在独立子线程中接收设备返回的数据,不要直接在子线程调用模型修改方法,通过信号槽中转即可:
from PyQt5.QtCore import QAbstractListModel, Qt, pyqtSignal, pyqtSlot, QModelIndex import threading import time class PersonModel(QAbstractListModel): # 定义新增数据的中转信号 add_person_signal = pyqtSignal(str, int) NameRole = Qt.UserRole + 1 AgeRole = Qt.UserRole + 2 def __init__(self, parent=None): super().__init__(parent) self.persons = [ {'name': 'jon', 'age': 20}, {'name': 'jane', 'age': 25} ] # 绑定中转信号到模型新增方法,Qt会自动调度到主线程执行 self.add_person_signal.connect(self.addPerson) # 模拟设备数据接收子线程 threading.Thread(target=self.device_data_listener, daemon=True).start() def device_data_listener(self): # 模拟持续接收设备数据 while True: time.sleep(5) # 子线程只发信号,不直接操作模型 self.add_person_signal.emit('oops', 19) # 其余代码同之前修改后的版本
内容的提问来源于stack exchange,提问作者ning.wei
相关产品推荐
相关产品推荐

