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:它们是一组规则,用于确定谁是接收线程?