@pyqtSlot()对嵌套函数有相同的作用吗?

时间:2019-05-11 10:44:01

标签: python pyqt pyqt5 signals-slots qthread

1。简介

我正在多线程应用程序上使用Python 3.7中的PyQt5,而我依赖于QThread

现在假设我有一个从QObject派生的类。在该类中,我定义了一个用@pyqtSlot注释的函数:

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import threading
...

class Worker(QObject):
    def __init__(self):
        super().__init__()
        return

    @pyqtSlot()
    def some_function(self):
        ...
        return

在其他一些代码中,我实例化Worker()并将其移至新线程,如下所示:

my_thread = QThread()
my_worker = Worker()
my_worker.moveToThread(my_thread)
my_thread.start()

QTimer.singleShot(100, my_worker.some_function)
return

通常,some_function()现在应该在my_thread中运行。那是因为:

  1. 我已将Worker()对象推到my_thread
  2. 当命令my_thread启动时,我实际上已经在该线程中诞生了一个新的Qt事件循环my_worker对象存在在此事件循环中。它的所有插槽都可以接收一个事件,该事件将在此事件循环中执行。
  3. some_function()已正确注释为@pyqtSlot()。单拍定时器挂接到此插槽并触发事件。感谢my_thread中的Qt-event-loop,该插槽有效地执行了my_thread中的代码。


2。我的问题

我的问题是关于嵌套函数(也称为“内部函数”)的。考虑一下:

class Worker(QObject):
    def __init__(self):
        super().__init__()
        return

    def some_function(self):
        ...
        @pyqtSlot()
        def some_inner_function():
            ...
            return
        return

如您所见,some_inner_function()被注释为@pyqtSlot。它的代码还会在Worker()对象所在的线程中运行吗?


3。旁注:如何挂钩内部函数

您可能想知道如何将某些东西连接到内部函数。好吧,请考虑以下几点:

class Worker(QObject):
    def __init__(self):
        super().__init__()
        return

    def some_function(self):
        @pyqtSlot()
        def some_inner_function():
            # Will this code run in `my_thread`?
            ...
            return
        # some_function() will run in the main thread if
        # it is called directly from the main thread.
        QTimer.singleShot(100, some_inner_function)
        return

如果直接从主线程调用some_function(),它将(不幸地)在主线程中运行。如果没有正确使用信号插槽机制,您将无法切换线程。

some_function()内的单拍定时器挂在some_inner_function()上并触发。内部函数是否将在my_thread中执行(假设Worker()对象已分配给my_thread)?

1 个答案:

答案 0 :(得分:2)

在Qt中,有关以下内容的规则如下:

  1. 如果直接调用可调用对象,它将在调用它的线程上运行。

  2. 如果可调用对象是间接调用的(通过qt信号QTimer::singleShot()QMetaObject::invokeMethod()),它将在它所属的上下文中执行。上下文是指QObject。

  3. 如果可调用对象不属于上下文,它将在间接调用它的线程中执行。

  4. 内部函数不属于上下文,因此,即使直接或间接调用它,它也将在调用它的线程中执行。

基于上述内容,让我们分析几种情况以验证以前的规则:

示例1

from PyQt5 import QtCore
import threading


class Worker(QtCore.QObject):
    def some_function(self):
        def some_inner_function():
            print("inner thread", threading.get_ident())
            QtCore.QThread.sleep(1)

        print("worker thread", threading.get_ident())
        some_inner_function()


if __name__ == "__main__":
    import sys

    app = QtCore.QCoreApplication(sys.argv)
    thread = QtCore.QThread()
    thread.start()
    my_worker = Worker()
    my_worker.moveToThread(thread)
    my_worker.some_function()
    print("main thread", threading.get_ident())
    sys.exit(app.exec_())

输出:

worker thread 140678349403776
inner thread 140678349403776
main thread 140678349403776

在这种情况下,因为直接调用了所有可调用对象,所以满足了规则1。

示例2

from PyQt5 import QtCore
import threading


class Worker(QtCore.QObject):
    def some_function(self):
        @QtCore.pyqtSlot()
        def some_inner_function():
            print("inner thread", threading.get_ident())
            QtCore.QThread.sleep(1)

        print("worker thread", threading.get_ident())
        QtCore.QTimer.singleShot(0, some_inner_function)


if __name__ == "__main__":
    import sys

    app = QtCore.QCoreApplication(sys.argv)
    thread = QtCore.QThread()
    thread.start()
    my_worker = Worker()
    my_worker.moveToThread(thread)
    my_worker.some_function()
    print("main thread", threading.get_ident())
    sys.exit(app.exec_())

输出:

worker thread 139721158932096
main thread 139721158932096
inner thread 139721158932096

在这种情况下,某些函数直接在主线程中调用,因此它将在该线程中执行,并且由于some_inner_function由some_function调用,因此它也将在该线程中执行。

示例3:

from PyQt5 import QtCore
import threading


class Worker(QtCore.QObject):
    def some_function(self):
        @QtCore.pyqtSlot()
        def some_inner_function():
            print("inner thread", threading.get_ident())
            QtCore.QThread.sleep(1)

        print("worker thread", threading.get_ident())
        QtCore.QTimer.singleShot(0, some_inner_function)


if __name__ == "__main__":
    import sys

    app = QtCore.QCoreApplication(sys.argv)
    thread = QtCore.QThread()
    thread.start()
    my_worker = Worker()
    my_worker.moveToThread(thread)
    QtCore.QTimer.singleShot(0, my_worker.some_function)
    print("main thread", threading.get_ident())
    sys.exit(app.exec_())

输出:

main thread 139934436517504
worker thread 139934378075904
inner thread 139934378075904

在这种情况下,some_function被间接调用,并且属于Worker上下文,因此它将在辅助线程上执行,因此some_inner_function将在辅助线程上执行。


最后,some_inner_function将与执行some_function的线程一起运行,甚至由于没有上下文而直接或间接调用它。