QML:向生活在不同QThread中的对象发送信号

时间:2017-07-17 14:14:58

标签: python qt pyqt qml pyqt5

我试图了解如何使用QThreads运行后台处理数据的后台服务,而不会冻结UI。虽然我在python中想出了如何做到这一点,但似乎QML的工作方式不同。

我设置了一个显示问题的小示例应用程序(代码后继续解释):

example.py

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")

example.qml

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真的无法将信号对象移动到其他线程吗?

0 个答案:

没有答案