为什么这个 signal/slot 代码不起作用
Why isn't this signal/slot code working
我有一个基本的 signal/slot 代码,我想在一个小部件中发出一个信号,然后在另一个小部件中连接该信号。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import QMainWindow, QAction, QApplication, QWidget, QPushButton, qApp, QLabel, QHBoxLayout, QVBoxLayout, QSplitter, QFileDialog
from PyQt5.QtGui import QIcon, QPixmap, QPainter, QImage
from PyQt5.QtCore import QSize, Qt, pyqtSignal, QObject
import sys, random
import qdarkstyle
from os import path
class SignalFactory(QObject):
selectedTextureToLoad = pyqtSignal()
class Application(QMainWindow):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
self.mainArea = MainArea()
# Option 1 - Uncomment below / Works but strong coupling between widgets
# self.signals.selectedTextureToLoad.connect(self.mainArea.doSomeStuff)
self.setCentralWidget(self.mainArea)
self.setGeometry(300, 300, 800, 400)
self.show()
def emitStuff(self):
print("Emitting...")
self.signals.selectedTextureToLoad.emit()
class MainArea(QWidget):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
# Option 2 - Uncomment below / Does not work
#self.signals.selectedTextureToLoad.connect(self.doSomeStuff)
def doSomeStuff(self):
print("Receiving...")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Application()
ex.emitStuff()
sys.exit(app.exec_())
如果我取消对选项 1 的注释,代码会起作用,并且会收到信号。但是,这两个小部件之间存在耦合。在这种情况下没问题,因为一个小部件是另一个小部件的父部件,自然会跟踪其子部件。但在更复杂的情况下,这意味着跟踪许多小部件只是为了配置信号,这并不好。
如果我取消注释选项 2,代码将不起作用,并且控制台仅显示 "Emitting..."。这有点烦人,因为在我看来,这是在一个地方配置信号并从另一个地方发出信号的最干净的方式,而不引入耦合。
我是不是漏掉了一些基本的东西?
编辑:
如果你像这样修改代码,通过添加returnASignal
函数
from PyQt5.QtWidgets import QMainWindow, QAction, QApplication, QWidget, QPushButton, qApp, QLabel, QHBoxLayout, QVBoxLayout, QSplitter, QFileDialog
from PyQt5.QtGui import QIcon, QPixmap, QPainter, QImage
from PyQt5.QtCore import QSize, Qt, pyqtSignal, QObject
import sys, random
import qdarkstyle
from os import path
def returnASignal():
print('Returning a signal')
return pyqtSignal()
class SignalFactory(QObject):
selectedTextureToLoad = returnASignal()
class Application(QMainWindow):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
self.mainArea = MainArea()
# Option 2 - Uncomment below / Works but strong coupling between widgets
# self.signals.selectedTextureToLoad.connect(self.mainArea.doSomeStuff)
self.setCentralWidget(self.mainArea)
self.setGeometry(300, 300, 800, 400)
self.show()
def emitStuff(self):
print("Emitting...")
self.signals.selectedTextureToLoad.emit()
class MainArea(QWidget):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
# Option 1 - Uncomment below / Does not work
self.signals.selectedTextureToLoad.connect(self.doSomeStuff)
def doSomeStuff(self):
print("Receiving...")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Application()
ex.emitStuff()
sys.exit(app.exec_())
当 运行 时,控制台显示:
Returning a signal
Emitting...
"Returning a signal"只打印了一次,没有打印两次,这说明虽然有多个SignalFactory
实例,但它们都共享同一个selectedTextureToLoad
对象。这绝对是一个正确的 static class member 而不是实例变量。所以信号对象到处都是一样的,我还是不明白为什么方案2不行。
这里没有真正的神秘之处。信号对象的行为方式与 classes.
中定义的方法完全相同
如果你像这样输入一些调试打印:
class SignalFactory(QObject):
selectedTextureToLoad = returnASignal()
print(selectedTextureToLoad)
def foo(self): pass
print(foo)
class Application(QMainWindow):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
print(self.signals.selectedTextureToLoad)
print(self.signals.foo)
...
class MainArea(QWidget):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
print(self.signals.selectedTextureToLoad)
print(self.signals.foo)
和 运行 你的例子,它会产生这样的输出:
Returning a signal
<unbound PYQT_SIGNAL []>
<function SignalFactory.foo at 0x7f2a57b1c268>
<bound PYQT_SIGNAL selectedTextureToLoad of SignalFactory object at 0x7f2a57b96828>
<bound method SignalFactory.foo of <__main__.SignalFactory object at 0x7f2a57b96828>>
<bound PYQT_SIGNAL selectedTextureToLoad of SignalFactory object at 0x7f2a57b96948>
<bound method SignalFactory.foo of <__main__.SignalFactory object at 0x7f2a57b96948>>
Emitting...
如您所见,从实例访问时信号和方法都是 bound objects,从 class 访问时都是未绑定对象。方法必须绑定到实例,以便 self
可以作为第一个参数传递。同样,信号对象必须绑定到实例以确保连接的槽仅接收信号来自发送它的特定实例。
因此在您的示例中有 两个 信号名为 selectedTextureToLoad
- 每个创建的 SignalFactory
实例一个。您的示例不起作用,因为 doSomeStuff
插槽未连接到发出 selectedTextureToLoad
信号的 特定绑定信号对象 。
信号槽机制是为对象之间的通信而设计的。没有已知发送者就无法广播消息,因此必须始终在发送者和接收者之间建立显式连接。如果要发送更通用的信号,请创建 SignalFactory
.
的全局实例
我有一个基本的 signal/slot 代码,我想在一个小部件中发出一个信号,然后在另一个小部件中连接该信号。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import QMainWindow, QAction, QApplication, QWidget, QPushButton, qApp, QLabel, QHBoxLayout, QVBoxLayout, QSplitter, QFileDialog
from PyQt5.QtGui import QIcon, QPixmap, QPainter, QImage
from PyQt5.QtCore import QSize, Qt, pyqtSignal, QObject
import sys, random
import qdarkstyle
from os import path
class SignalFactory(QObject):
selectedTextureToLoad = pyqtSignal()
class Application(QMainWindow):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
self.mainArea = MainArea()
# Option 1 - Uncomment below / Works but strong coupling between widgets
# self.signals.selectedTextureToLoad.connect(self.mainArea.doSomeStuff)
self.setCentralWidget(self.mainArea)
self.setGeometry(300, 300, 800, 400)
self.show()
def emitStuff(self):
print("Emitting...")
self.signals.selectedTextureToLoad.emit()
class MainArea(QWidget):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
# Option 2 - Uncomment below / Does not work
#self.signals.selectedTextureToLoad.connect(self.doSomeStuff)
def doSomeStuff(self):
print("Receiving...")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Application()
ex.emitStuff()
sys.exit(app.exec_())
如果我取消对选项 1 的注释,代码会起作用,并且会收到信号。但是,这两个小部件之间存在耦合。在这种情况下没问题,因为一个小部件是另一个小部件的父部件,自然会跟踪其子部件。但在更复杂的情况下,这意味着跟踪许多小部件只是为了配置信号,这并不好。
如果我取消注释选项 2,代码将不起作用,并且控制台仅显示 "Emitting..."。这有点烦人,因为在我看来,这是在一个地方配置信号并从另一个地方发出信号的最干净的方式,而不引入耦合。
我是不是漏掉了一些基本的东西?
编辑:
如果你像这样修改代码,通过添加returnASignal
函数
from PyQt5.QtWidgets import QMainWindow, QAction, QApplication, QWidget, QPushButton, qApp, QLabel, QHBoxLayout, QVBoxLayout, QSplitter, QFileDialog
from PyQt5.QtGui import QIcon, QPixmap, QPainter, QImage
from PyQt5.QtCore import QSize, Qt, pyqtSignal, QObject
import sys, random
import qdarkstyle
from os import path
def returnASignal():
print('Returning a signal')
return pyqtSignal()
class SignalFactory(QObject):
selectedTextureToLoad = returnASignal()
class Application(QMainWindow):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
self.mainArea = MainArea()
# Option 2 - Uncomment below / Works but strong coupling between widgets
# self.signals.selectedTextureToLoad.connect(self.mainArea.doSomeStuff)
self.setCentralWidget(self.mainArea)
self.setGeometry(300, 300, 800, 400)
self.show()
def emitStuff(self):
print("Emitting...")
self.signals.selectedTextureToLoad.emit()
class MainArea(QWidget):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
# Option 1 - Uncomment below / Does not work
self.signals.selectedTextureToLoad.connect(self.doSomeStuff)
def doSomeStuff(self):
print("Receiving...")
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Application()
ex.emitStuff()
sys.exit(app.exec_())
当 运行 时,控制台显示:
Returning a signal
Emitting...
"Returning a signal"只打印了一次,没有打印两次,这说明虽然有多个SignalFactory
实例,但它们都共享同一个selectedTextureToLoad
对象。这绝对是一个正确的 static class member 而不是实例变量。所以信号对象到处都是一样的,我还是不明白为什么方案2不行。
这里没有真正的神秘之处。信号对象的行为方式与 classes.
中定义的方法完全相同如果你像这样输入一些调试打印:
class SignalFactory(QObject):
selectedTextureToLoad = returnASignal()
print(selectedTextureToLoad)
def foo(self): pass
print(foo)
class Application(QMainWindow):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
print(self.signals.selectedTextureToLoad)
print(self.signals.foo)
...
class MainArea(QWidget):
def __init__(self):
super().__init__()
self.signals = SignalFactory()
print(self.signals.selectedTextureToLoad)
print(self.signals.foo)
和 运行 你的例子,它会产生这样的输出:
Returning a signal
<unbound PYQT_SIGNAL []>
<function SignalFactory.foo at 0x7f2a57b1c268>
<bound PYQT_SIGNAL selectedTextureToLoad of SignalFactory object at 0x7f2a57b96828>
<bound method SignalFactory.foo of <__main__.SignalFactory object at 0x7f2a57b96828>>
<bound PYQT_SIGNAL selectedTextureToLoad of SignalFactory object at 0x7f2a57b96948>
<bound method SignalFactory.foo of <__main__.SignalFactory object at 0x7f2a57b96948>>
Emitting...
如您所见,从实例访问时信号和方法都是 bound objects,从 class 访问时都是未绑定对象。方法必须绑定到实例,以便 self
可以作为第一个参数传递。同样,信号对象必须绑定到实例以确保连接的槽仅接收信号来自发送它的特定实例。
因此在您的示例中有 两个 信号名为 selectedTextureToLoad
- 每个创建的 SignalFactory
实例一个。您的示例不起作用,因为 doSomeStuff
插槽未连接到发出 selectedTextureToLoad
信号的 特定绑定信号对象 。
信号槽机制是为对象之间的通信而设计的。没有已知发送者就无法广播消息,因此必须始终在发送者和接收者之间建立显式连接。如果要发送更通用的信号,请创建 SignalFactory
.