如何通过长时间运行的插槽保持 UI 响应

GPh*_*ilo 3 python qt qthread qml pyside2

我有一个 python 定义的工作器QObject,它有一个work()由 QML UI 调用的慢速插槽(在我的实际 UI 中,FolderListModel当用户浏览列表时,该方法在每个项目上动态调用,但对于他的示例代码我'我只是在窗口完成时调用它作为示例)。

我想work异步运行慢速以防止 UI 阻塞。我想通过在 QThread 上移动 Worker 实例并在那里调用插槽来做到这一点,但这不起作用,因为 UI 仍然被阻塞,等待结果的work()到来。

这是我迄今为止尝试的代码:

mcve.qml:

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
}
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

如何work()异步调用,以便仅在完成后应用其效果?而且,作为奖励,我在使用 QThreads 时在做什么/理解错误?

eyl*_*esc 5

解释:

  • 当前执行的“工作”方法在哪里?好吧,如果你添加以下代码并检查你得到了什么:
# ...

import threading

class Worker(QObject):
    @Slot(str, result=str)
    def work(self, path):
        print(threading.current_thread())
        sleep(5)  # do something lengthy
        return path

# ...
Run Code Online (Sandbox Code Playgroud)

输出:

<_MainThread(MainThread, started 140409078408832)>
qml: I'm done!
Run Code Online (Sandbox Code Playgroud)

如您所见,“work”方法在主线程中执行,导致它阻塞 GUI。

  • 为什么在主线程中执行“work”方法?方法或函数在调用它的上下文中执行,在您的情况下,在主线程中执行的 QML 中。

  • 那么如何在 QObject 所在的线程中执行一个方法呢?好吧,您必须异步使用QMetaObject::invokeMethod()(此方法在 PySide2 中无法用于错误),通过信号调用或使用QTimer::singleShot().


解决方案:

在这些情况下,最好创建一个桥(QObject)来调用在另一个线程中执行的函数/方法,并通过信号通知更改。

<_MainThread(MainThread, started 140409078408832)>
qml: I'm done!
Run Code Online (Sandbox Code Playgroud)
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)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 只是一个完美的答案。 (2认同)