Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 插槽在哪个线程中执行,我可以将其重定向到另一个线程吗?_Python_Multithreading_Pyqt_Signals Slots_Qthread - Fatal编程技术网

Python 插槽在哪个线程中执行,我可以将其重定向到另一个线程吗?

Python 插槽在哪个线程中执行,我可以将其重定向到另一个线程吗?,python,multithreading,pyqt,signals-slots,qthread,Python,Multithreading,Pyqt,Signals Slots,Qthread,在进一步了解时,我对插槽是在哪个上下文中执行感到困惑,因此我编写了以下示例来测试它: from PyQt5.Qt import * # I know this is bad, but I want a small example import threading def slot_to_output_something ( something ): print( 'slot called by', threading.get_ident(), 'with', something )

在进一步了解时,我对插槽是在哪个上下文中执行感到困惑,因此我编写了以下示例来测试它:

from PyQt5.Qt import * # I know this is bad, but I want a small example
import threading

def slot_to_output_something ( something ):
    print( 'slot called by', threading.get_ident(), 'with', something )

class Object_With_A_Signal( QObject ):
    sig = pyqtSignal( str )

class LoopThread( QThread ):
    def __init__ ( self, object_with_a_signal ):
        self.object_with_a_signal = object_with_a_signal
        super().__init__()

    def run ( self ):
        print( 'loop running in', threading.get_ident() )
        import time
        for i in range( 5 ):
            self.object_with_a_signal.sig.emit( str( i ) )
            time.sleep( 1 )


print( 'main running in', threading.get_ident() )
app = QApplication( [] )
mainw = QMainWindow( None )
mainw.show()
obj = Object_With_A_Signal()

# connection in main-thread
obj.sig.connect(slot_to_output_something, Qt.QueuedConnection )
loop = LoopThread( obj )
loop.start()

app.exec()
输出:

主要运行于57474中
循环在57528中运行
57474用0调用插槽
57474用1调用插槽


到目前为止还不错,但现在我找到了他说的地方:

插槽将始终在调用线程中执行,除非创建
Qt::QueuedConnection
以在拥有插槽的对象所属的线程中运行插槽

插槽的所有权在Python中如何工作?在我的下一次尝试中,插槽连接到信号的线程是在发出信号时执行插槽的线程:

# connection in main-thread
# obj.sig.connect(slot_to_output_something, Qt.QueuedConnection )
# loop = LoopThread( obj )
# loop.start()

# connection in helper-thread
class Thread_In_Between( QThread ):
    def __init__ ( self, object_with_a_signal ):
        super().__init__()
        self.object_with_a_signal = object_with_a_signal

    def run ( self ):
        print( 'helper thread running in', threading.get_ident() )
        self.object_with_a_signal.sig.connect( slot_to_output_something, Qt.QueuedConnection)
        loop = LoopThread( self.object_with_a_signal )
        loop.start()
        loop.exec()  # without -> ERROR: QThread: Destroyed while thread is still running
        print( 'end helper thread' ) # never reached ??

helper_thread = Thread_In_Between( obj )
helper_thread.start()
输出:

主运行在65804中
辅助线程在65896中运行
循环在65900中运行
65896用0调用的插槽
65896用1调用插槽

所以。。我说得对吗是由线程执行的插槽,它们在其中连接,还是我刚刚想出了一个坏例子?


此外,GUI更改应该只在主线程中执行,但是如果我将这些行添加到代码中

# use QListwidget for output instead
lis = QListWidget( None )
print = lambda *args: lis.addItem( str( ' '.join( str( x ) for x in args ) ) )
mainw.setCentralWidget( lis )
输出被重定向到QListWidget中,但显示在主线程中没有调用它。是否有将插槽移动到另一个线程的选项(转移“所有权”-我刚刚发现)


使用pyqt执行被调用插槽(通过发射信号)的一般规则是什么?

编辑:

整个问题只是关于
QueuedConnection
BlockingQueuedConnection
。我知道在PyQt中有两种主要类型的插槽:包装的Qt插槽。以及普通python可调用对象

第一种类型包括由Qt定义的内置插槽,以及任何由Qt装饰的用户定义插槽。这些插槽将完全按照Qt所记录的那样工作,因此没有适用于它们的其他PyQt特定的“规则”。根据定义,它们必须是
QObject
子类的成员,这反过来意味着它们是。因此,您可以通过使用(例如)明确检查插槽是否属于此类型

对于第二种类型的插槽,PyQt创建一个内部代理对象,该对象包装python可调用对象,并提供信号插槽机制所需的Qt插槽。因此,这就提出了这个代理对象应该位于何处的问题。如果可调用对象由继承
QObject
的对象拥有,PyQt可以自动将代理移动到适当的线程。在伪代码中,它将执行如下操作:

if receiver:
    proxy.moveToThread(receiver.thread())
但是,如果没有合适的reveiver,代理将只停留在创建它的线程中

后一种情况适用于您的示例。
slot\u to\u output\u something
插槽只是一个没有所有者的模块级功能。PyQt找不到与其关联的接收器,因此内部代理将保留在建立连接的线程中。但是,如果将此插槽移动到具有_a_信号的对象_的成员,则会在主线程中调用它。这是因为带有信号的
Object\u继承
QObject
,其实例当前位于主线程中。这允许PyQt自动将内部代理移动到相应接收器的线程

因此,如果您想控制插槽的执行位置,请将其设置为
QObject
子类的成员,并在必要时使用显式地将其放置在适当的线程中。此外,可能建议应用
pyqtSlot
decorator,以避免出现任何尴尬的情况(有关详细信息,请参阅)

PS


上述第二类插槽的“规则”可能只适用于PyQt——PySide中的情况不太可能以同样的方式工作。而且可能也无法保证它们将以完全相同的方式与PyQt的所有以前或未来版本一起工作。因此,如果希望避免意外的行为更改,最好将
pyqtSlot
装饰器与将在不同线程之间连接的任何插槽一起使用。

为什么要在另一个线程中创建线程?循环被销毁,因为它是一个本地变量o-具有,插槽在接收器的线程中执行。@eyllanesc:我创建另一个线程,以查看是否他是执行插槽的线程,实际情况如何。此外,
loop
不会被销毁,因为代码不会经过
loop.exec()
行,在该行中它不会离开eventloop(但不确定为什么?)。@ekhumoro:它们是一组规则,用于确定谁是接收线程?