我有一个由python定义的工作程序QObject
,它具有一个缓慢的work()
插槽,该插槽由QML UI调用(在我的实际UI中,该方法在{{1 }}随着用户浏览列表而动态变化,但是对于他的示例代码,我只是在窗口完成时调用它作为示例。
我想异步运行慢速FolderListModel
以防止UI阻塞。我想通过在QThread上移动Worker实例并在其中调用插槽来实现此目的,但这不起作用,因为UI仍被阻塞,等待work
的结果。
这是我到目前为止的尝试代码:
mcve.qml:
work()
mcve.py:
import QtQuick 2.13
import QtQuick.Window 2.13
Window {
id: window
visible: true
width: 800
height: 600
title: qsTr("Main Window")
Component.onCompleted: console.log(worker.work("I'm done!")) // not the actual usage, see note in the question
}
如何异步调用import sys
from PySide2.QtWidgets import QApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QUrl, QThread, QObject, Slot
from time import sleep
class Worker(QObject):
def __init__(self, parent=None):
super().__init__(parent)
@Slot(str, result=str)
def work(self, path):
sleep(5) # do something lengthy
return path
if __name__ == '__main__':
app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
workerThread = QThread()
worker = Worker()
worker.moveToThread(workerThread)
engine.rootContext().setContextProperty("worker", worker)
engine.load(QUrl.fromLocalFile('mcve.qml'))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
,以便仅在完成后才应用效果?而且,作为奖励,我在使用QThreads时在做什么/理解错了吗?
答案 0 :(得分:3)
# ...
import threading
class Worker(QObject):
@Slot(str, result=str)
def work(self, path):
print(threading.current_thread())
sleep(5) # do something lengthy
return path
# ...
输出:
<_MainThread(MainThread, started 140409078408832)>
qml: I'm done!
如您所见,“ work”方法在主线程中执行,导致其阻塞了GUI。
为什么在主线程中执行“工作”方法?在您所使用的QML中,在主线程中执行的方法或函数是在调用它的上下文中执行的线程。
那么您如何在QObject所在的线程中执行方法?那么您必须使用QMetaObject::invokeMethod()
异步执行该方法(对于PySide2,该方法无法用于错误),通过调用信号或使用QTimer::singleShot()
。
在这些情况下,最好创建一个桥(QObject)来调用在另一个线程中执行的函数/方法,并通过信号通知更改。
import sys
from time import sleep
from functools import partial
from PySide2 import QtCore, QtWidgets, QtQml
class Worker(QtCore.QObject):
resultChaged = QtCore.Signal(str)
@QtCore.Slot(str)
def work(self, path):
sleep(5) # do something lengthy
self.resultChaged.emit(path)
class Bridge(QtCore.QObject):
startSignal = QtCore.Signal(str)
resultChaged = QtCore.Signal(str, arguments=["result"])
def __init__(self, obj, parent=None):
super().__init__(parent)
self.m_obj = obj
self.m_obj.resultChaged.connect(self.resultChaged)
self.startSignal.connect(self.m_obj.work)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
engine = QtQml.QQmlApplicationEngine()
workerThread = QtCore.QThread()
workerThread.start()
worker = Worker()
worker.moveToThread(workerThread)
bridge = Bridge(worker)
engine.rootContext().setContextProperty("bridge", bridge)
engine.load(QtCore.QUrl.fromLocalFile("mcve.qml"))
if not engine.rootObjects():
sys.exit(-1)
ret = app.exec_()
workerThread.quit()
workerThread.wait()
sys.exit(ret)
import QtQuick 2.13
import QtQuick.Window 2.13
Window {
id: window
visible: true
width: 800
height: 600
title: qsTr("Main Window")
Component.onCompleted: bridge.startSignal("I'm done!")
Connections{
target: bridge
onResultChaged: console.log(result)
}
}