在了解更多相关知识的同时Qt 中的信号/槽机制 http://doc.qt.io/qt-5/signalsandslots.html,我很困惑插槽在哪个上下文中执行,所以我编写了以下示例来测试它:
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()
output:
主要运行于57474
循环运行在57528
由 57474 用 0 调用的槽
由 57474 用 1 调用的槽
...
到目前为止还不错 - 但现在我发现了塞巴斯蒂安·兰格的回答 https://stackoverflow.com/a/41437798/4464653他说:
你的槽将始终在调用线程中执行,除非你创建一个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()
output:
主要运行于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,但表明这不是在主线程中调用的。是否有一个选项可以将插槽移动到另一个线程(转移“所有权” - 我刚刚发现QObject::moveToThread https://doc.qt.io/qt-5/qobject.html#moveToThread)?
他们是关于使用 pyqt 执行调用槽(通过发出的信号)的一般规则吗?
EDIT:
整个问题只是关于QueuedConnection
or BlockingQueuedConnection
。我知道一个DirectConnection
和其他选项 https://doc.qt.io/qt-5/qt.html#ConnectionType-enum.