【问题标题】:Has `@pyqtSlot()` same effect on a nested function?`@pyqtSlot()` 对嵌套函数有同样的影响吗?
【发布时间】:2019-09-29 01:37:52
【问题描述】:

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()-object 所在的线程中运行?

 

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

【问题讨论】:

    标签: python pyqt pyqt5 qthread qt-slot


    【解决方案1】:

    在 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 函数直接在主线程中调用,因此它将在该线程中执行,并且由于 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 相同的线程上,甚至直接或间接调用它,因为它没有上下文。

    【讨论】:

    • 感谢@eyllanesc 的精彩回答!我捐赠了一些东西,你可以喝杯咖啡和蛋糕犒劳自己;-)
    • 嗨@eyllanesc,你能看看我最新的PyQt问题:stackoverflow.com/questions/56145023/…吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-15
    • 2011-12-18
    • 2012-05-27
    • 1970-01-01
    • 1970-01-01
    • 2018-07-11
    相关资源
    最近更新 更多