PyQt/PySide应用加载类时Signal/Slot异常需lambda触发问题
PySide信号槽无法触发问题底层原因解析
本质是Python对象垃圾回收机制和PySide信号槽的引用持有逻辑共同导致的异常,具体逻辑如下:
- 临时对象无引用导致被回收
你在App.__init__中直接创建Module(self)时,没有将实例赋值给App的实例属性,同时Module类的构造函数没有把传入的parent参数传给父类QWidget的构造,所以无论是Python侧还是Qt父子对象机制,都没有持有这个Module实例的强引用,App.__init__执行完成后,该实例会被Python垃圾回收器销毁。 - 普通信号槽连接不持有对象强引用
当你直接使用printbar.activated.connect(self.bar)绑定槽函数时,PySide的信号槽默认不会为槽函数的所属对象(即Module实例)增加引用计数,只会持有弱引用,一旦Module实例被回收,所有绑定在该实例上的信号槽连接都会自动失效,因此两个快捷键都无法触发。 - lambda闭包间接延长了对象生命周期
当你用lambda包装槽函数时,lambda作为闭包会捕获当前Module实例的self引用,而lambda本身会作为槽函数被QShortcut持有(QShortcut的父对象是主窗口,生命周期和应用一致,会一直持有QShortcut实例),相当于间接为Module实例增加了引用计数,避免了实例被回收。只要任意一个槽函数用lambda包装就会持有整个Module实例的引用,所以两个连接都能正常生效,和你观察到的「不限制绑定哪个、不限制绑定顺序」的现象完全吻合。
修复方案
你可以任选以下任意一种方案解决该问题:
- 将
Module实例绑定到App的实例属性上,让App持有实例的强引用:# 修改App.__init__中的代码 self.module = Module(self) - 修正
Module类的构造函数,传入父对象触发Qt父子引用机制,让父窗口持有Module实例:# 修改Module.__init__中的super调用 super().__init__(parent)
内容的提问来源于stack exchange,提问作者somethingvague
相关产品推荐
相关产品推荐

