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

2024-09-28 03:21:14 发布

您现在位置:Python中文网/ 问答频道 /正文

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

#!/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的注释,代码将不起作用,控制台只显示“正在发射…”。这有点烦人,因为在我看来,这是在一个地方配置信号并从另一个地方发射信号的最干净的方式,而不引入耦合。

我是不是错过了一些基本的东西?你知道吗

编辑:

如果像这样修改代码,可以通过添加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,而不是一个实例变量。因此,signal对象在任何地方都是相同的,我仍然不明白为什么选项2不起作用。你知道吗


Tags: fromimportself信号init部件defsys
1条回答
网友
1楼 · 发布于 2024-09-28 03:21:14

这里没有真正的神秘。信号对象的行为方式与类中定义的方法完全相同。你知道吗

如果你把一些调试打印像这样:

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可以作为第一个参数传递。同样,signal对象必须绑定到实例,以确保连接的插槽仅从发送它的特定实例接收信号。你知道吗

因此,在您的示例中,有两个名为selectedTextureToLoad的信号-一个用于创建的SignalFactory的每个实例。您的示例不起作用,因为doSomeStuff插槽未连接到发出selectedTextureToLoad信号的特定绑定信号对象。你知道吗

信号槽机制用于对象之间的通信。如果没有已知的发送者,就没有广播消息的工具,因此必须始终在发送者和接收者之间建立明确的连接。如果要发送更通用的信号,请创建SignalFactory的全局实例。你知道吗

相关问题 更多 >

    热门问题