为什么此信号/插槽代码不起作用

时间:2018-01-10 15:09:47

标签: python pyqt pyqt5 signals-slots

我有一个基本的信号/插槽代码,我想在一个小部件中发出信号,并在另一个小部件中连接该信号。

#!/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...

“返回信号”仅打印一次而不是两次,这表明虽然有多个SignalFactory个实例,但它们共享相同的selectedTextureToLoad个对象。这绝对是一个合适的static class member而不是实例变量。因此,信号对象在任何地方都是相同的,我仍然不明白为什么选项2不起作用。

1 个答案:

答案 0 :(得分:2)

这里没有真正的谜。信号对象的行为方式与类中定义的方法完全相同。

如果你输入一些这样的调试打印:

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,从类中访问时,信号和方法都是未绑定的对象。必须将方法绑定到实例,以便self可以作为第一个参数传递。同样,必须将信号对象绑定到实例,以确保连接的插槽仅接收来自发送它的特定实例的信号

因此,在您的示例中有两个信号,名为selectedTextureToLoad - 每个SignalFactory创建一个实例。您的示例无法正常工作,因为doSomeStuff广告位未连接到发出selectedTextureToLoad信号的特定约束信号对象

信号槽机制设计用于对象之间的通信。没有已知发送方的广播消息没有设施,因此必须始终在发射器和接收器之间进行显式连接。如果要发送更通用的信号,请创建SignalFactory的全局实例。