信号槽第一性原理:QObject、emit、connect 线程亲缘性
信号槽是 Qt 的"事件总线"
在 PySide6 里,信号槽(signal/slot)就是对象之间的解耦通信:A 对象发信号,B 对象的槽函数被调用,A 不需要知道 B 是谁。
三层组成:
- QObject:能发信号的都是它派生的。
- signal:某个对象声明一个信号,表示"我这儿有事发生了"。
- slot/connect:把某个对象的信号,连到另一个对象的函数上。
python
from PySide6.QtCore import Signal, QObject
class UserManager(QObject):
user_logged_in = Signal(str) # 声明一个带参数的信号
def login(self, name):
self.user_logged_in.emit(name) # 发信号
# 另一个对象 connect
manager = UserManager()
manager.user_logged_in.connect(lambda name: print(f"欢迎 {name}"))
manager.login("tom")
关键是"信号在哪线程 emit"
PySide6 信号跟线程关系很大:
- 若信号的发射线程 == 接收槽所在的线程,直接调用(DirectConnection)。
- 若跨线程 ,Qt 会走 QueuedConnection,把事件投递到接收对象所在线程的事件循环,槽函数在接收线程执行。
这就是它天然线程安全的秘密:你只管在哪个线程 emit,Qt 保证槽在正确的(接收者所在)线程跑。
我们项目的应用
UiBridge 就是一个装信号的 QObject:
python
class UiBridge(QObject):
result_received = Signal(dict)
worker 线程里 emit,主线程 connect 的 _handle_result 收到后刷新 UI。因为 emit 在 worker,槽在主线程,Qt 自动 queue 过去,UI 不会被 worker 线程直接操作,也就不会崩。
一句话
信号槽 = 对象间的解耦事件 + 自动的线程调度。理解"信号在哪个线程发、槽在哪个线程收",跨线程 UI 就不会出幺蛾子。