我试图了解如何使用QThreads运行后台处理数据的后台服务,而不会冻结UI。虽然我在python中想出了如何做到这一点,但似乎QML的工作方式不同。
我设置了一个显示问题的小示例应用程序(代码后继续解释):
import threading
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQuick import QQuickView
import os
class MyBackgroundProcessingClass(QObject):
def __init__(self, **kwargs):
return super().__init__(**kwargs)
@pyqtSlot()
def doInitOnTheThread(self):
# allocate lots of resources
print('doInitOnTheThread() executing in thread {}'.format(threading.get_ident()))
@pyqtSlot(int)
def myProcessingFunctionThatTakesALotOfTime(self, the_parameter):
# process for a long time
print('myProcessingFunctionThatTakesALotOfTime() executing in thread {}'.format(threading.get_ident()))
if __name__ == '__main__':
print('GUI thread has ID {}'.format(threading.get_ident()))
myApp = QApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
the_thread = QThread()
my_background_processing_instance = MyBackgroundProcessingClass()
my_background_processing_instance.moveToThread(the_thread)
the_thread.start()
QTimer.singleShot(0, my_background_processing_instance.doInitOnTheThread) #sending a signal to the thread to do the init in its own context
view.engine().rootContext().setContextProperty("myExternalInstance", my_background_processing_instance)
view.setSource(QUrl('example.qml'))
view.show()
myApp.exec_()
os.system("pause")
import QtQuick 2.0
import QtQuick.Controls 1.2
Button {
signal myCustomSignalWithParameters(int param)
text: "Click me!"
onClicked: {
myCustomSignalWithParameters(42)
}
Component.onCompleted: {
myCustomSignalWithParameters.connect(myExternalInstance.myProcessingFunctionThatTakesALotOfTime)
}
}
GUI thread has ID 18408
doInitOnTheThread() executing in thread 11000
myProcessingFunctionThatTakesALotOfTime() executing in thread 18408
PyQt多线程信令允许我通过将方法连接到GUI线程发出的信号来在不同的线程中运行方法。这是有效的,正如doInitOnTheThread
在第二个线程上执行的事实所示。但是,如果我在QML中重复相同的模式,则该方法将在GUI线程中运行。
如何让它在后台线程中运行?一个(可怕的,IMO)可能的解决方案是在python中创建另一个类,它充当QML和python之间的代理,其实例将在GUI线程的上下文中。然后,我将QML信号注册到代理和代理插槽中的插槽中。实现我将从python发出一个信号,然后将其正确传递给另一个线程。
有更合理的解决方案吗?或者QML真的无法将信号对象移动到其他线程吗?