PySide2中如何通过Python QThread用列表更新QML ListView
问题核心错误
你的代码无法运行是3个逻辑问题导致的:
- 重复创建
Worker1实例:Main类初始化时生成了一个self.worker1,主函数中又新建了一个worker1暴露给QML。点击按钮启动的是Main内部的Worker线程,QML的ListView绑定的是主函数里从未启动过的Worker的model,两个对象完全独立,数据无法同步。 - Qt模型线程归属违规:
QStringListModel属于UI相关对象,必须存活在主线程,放在QThread子类中跨线程操作极易触发随机崩溃。 - 属性通知逻辑失效:
model属性绑定的notify信号和实际模型更新操作不匹配,QML无法感知模型数据变化,自然不会刷新视图。
你不需要额外把self.hosts变量传到其他类,修正实例归属和线程逻辑即可正常运行。
修正后的Python代码
import os from pathlib import Path import sys from PySide2.QtGui import QGuiApplication from PySide2.QtQml import QQmlApplicationEngine from PySide2.QtCore import QObject, Slot, Signal, Property, QThread, QStringListModel class Worker1(QThread): # 线程仅负责发送计算完成的列表数据,不持有、不操作任何UI模型 updateListView = Signal(list) def run(self): # 测试用业务逻辑 myList = ["A1", "A2", "A3", "A4", "B1", "B2", "B3"] hosts = [] for i in myList: if "A" in i: hosts.append(i) # 跨线程发送结果,Qt会自动把信号排队到主线程执行 self.updateListView.emit(hosts) class Main(QObject): modelChanged = Signal() def __init__(self, parent=None): super(Main, self).__init__(parent) # 模型放在主线程实例中,仅在主线程操作 self._model = QStringListModel() self.worker1 = Worker1() # 绑定线程更新信号到主线程的模型更新槽 self.worker1.updateListView.connect(self.updateModel) @Property(QObject, notify=modelChanged) def model(self): return self._model @Slot() def startCheck(self): # 防止重复点击启动多个相同线程 if not self.worker1.isRunning(): self.worker1.start() @Slot(list) def updateModel(self, hosts): self._model.setStringList(hosts) # 数据更新后发送通知,触发QML视图刷新 self.modelChanged.emit() if __name__ == "__main__": app = QGuiApplication(sys.argv) engine = QQmlApplicationEngine() main = Main() # 仅需暴露main实例给QML,无需单独暴露worker对象 engine.rootContext().setContextProperty("main", main) engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml")) if not engine.rootObjects(): sys.exit(-1) sys.exit(app.exec_())
修正后的QML代码
import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Window 2.2 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Main Program") Button { text: qsTr("Update") anchors.top: parent.top anchors.topMargin: 21 anchors.horizontalCenter: parent.horizontalCenter onClicked: main.startCheck() } ListView{ id: listView width: 200 height: 150 anchors.top: parent.top anchors.topMargin: 55 anchors.horizontalCenter: parent.horizontalCenter // 绑定主线程main对象中的model model: main.model delegate: Text { text: model.display color: "black" width: listView.width horizontalAlignment: Text.AlignHCenter } } }
关键改动说明
- 移除重复的Worker1实例:全局仅保留一个Worker对象挂在Main类下,保证按钮启动的线程、信号绑定的线程是同一个对象。
- 调整模型归属:把
QStringListModel从QThread子类移到主线程的Main类中,所有模型操作都在主线程执行,符合Qt UI相关对象仅能在主线程操作的强制规则,避免跨线程崩溃。 - 修复属性通知:模型数据更新后主动触发
modelChanged信号,QML绑定的ListView会自动响应数据变化刷新内容。 - 线程逻辑解耦:Worker线程只负责执行耗时计算,计算完成后通过跨线程信号把结果抛给主线程,线程本身不操作任何UI相关对象,符合Qt多线程开发规范。
- 增加线程运行状态判断:防止重复点击按钮启动多个相同线程造成资源泄漏。
- 移除了原QML中无效的
contentWidth: 0配置,避免列表内容显示不全。
内容的提问来源于stack exchange,提问作者rolando_cser
相关产品推荐
相关产品推荐

