【问题标题】:PyQt proper use of emit() and pyqtSignal()PyQt 正确使用 emit() 和 pyqtSignal()
【发布时间】:2016-07-25 21:16:48
【问题描述】:

我正在阅读有关 PyQt5 的一些文档,以提出一个简单的信号槽机制。由于设计考虑,我已经停下来了。

考虑以下代码:

import sys
from PyQt5.QtCore import (Qt, pyqtSignal)
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
    QVBoxLayout, QApplication)


class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()

    def printLabel(self, str):
        print(str)

    def logLabel(self, str):
        '''log to a file'''
        pass

    def initUI(self):

        lcd = QLCDNumber(self)
        sld = QSlider(Qt.Horizontal, self)

        vbox = QVBoxLayout()
        vbox.addWidget(lcd)
        vbox.addWidget(sld)

        self.setLayout(vbox)

        #redundant connections
        sld.valueChanged.connect(lcd.display)
        sld.valueChanged.connect(self.printLabel)
        sld.valueChanged.connect(self.logLabel)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Signal & slot')
        self.show()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

要跟踪对滑块所做的更改,我只需打印并记录所做的更改。我不喜欢代码的是我需要三次调用sld.valueChanged 插槽才能将相同的信息发送到 3 个不同的插槽。

是否可以创建我自己的pyqtSignal 将整数发送到单个槽函数。反过来让槽函数发出需要进行的更改?

  • 也许我不完全理解emit() 的用途,因为PyQt Signal-Slot docs 中没有很好的例子来说明它的用途。我们给出的只是一个如何实现不带参数的emit 的示例。

我想做的是创建一个处理发射函数的函数。考虑以下几点:

import sys
from PyQt5.QtCore import (Qt, pyqtSignal)
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
    QVBoxLayout, QApplication)


class Example(QWidget):

    def __init__(self):
        super().__init__()

        #create signal
        self.val_Changed = pyqtSignal(int, name='valChanged')

        self.initUI()

    def initUI(self):

        lcd = QLCDNumber(self)
        sld = QSlider(Qt.Horizontal, self)

        vbox = QVBoxLayout()
        vbox.addWidget(lcd)
        vbox.addWidget(sld)

        self.setLayout(vbox)

        sld.val_Changed.connect(self.handle_LCD)
        self.val_Changed.emit()

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Signal & slot')
        self.show()

    def handle_LCD(self, text):
        '''log'''
        print(text)
        '''connect val_Changed to lcd.display'''

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

这里显然存在一些严重的设计缺陷。我无法理解函数调用的顺序。而且我没有正确实施pyqtSignal。不过,我确实相信正确陈述以下 3 点将有助于我制作出合适的应用程序:

  1. 对于预定义的信号:将信号发送到槽函数。可以重新实现 Slot 以使用信号值。
  2. 使用一些参数生成pyqtSignal 对象。目前尚不清楚这些参数的用途是什么以及它们与“发射”参数有何不同。
  3. emit 可以重新实现以向槽函数发送特定的信号值。目前还不清楚为什么我需要发送与以前存在的信号方法不同的值。

请随意完全更改我正在尝试做的代码,因为我还没有弄清楚它是否属于良好的风格。

【问题讨论】:

    标签: python pyqt signals signals-slots pyqt5


    【解决方案1】:

    您可以定义自己的插槽(任何可调用的 python)并将其连接到信号,然后从该插槽调用其他插槽。

    class Example(QWidget):
    
        def __init__(self):
            super().__init__()
            self.initUI()
    
        def printLabel(self, str):
            print(str)
    
        def logLabel(self, str):
            '''log to a file'''
            pass
    
        @QtCore.pyqtSlot(int)
        def on_sld_valueChanged(self, value):
            self.lcd.display(value)
            self.printLabel(value)
            self.logLabel(value)
    
        def initUI(self):
    
            self.lcd = QLCDNumber(self)
            self.sld = QSlider(Qt.Horizontal, self)
    
            vbox = QVBoxLayout()
            vbox.addWidget(self.lcd)
            vbox.addWidget(self.sld)
    
            self.setLayout(vbox)
            self.sld.valueChanged.connect(self.on_sld_valueChanged)
    
    
            self.setGeometry(300, 300, 250, 150)
            self.setWindowTitle('Signal & slot')
    

    另外,如果你想定义自己的信号,它们必须定义为类变量

    class Example(QWidget):
        my_signal = pyqtSignal(int)
    

    pyqtSignal 的参数定义了在该信号上将被 emit'd 的对象类型,因此在这种情况下,您可以这样做

    self.my_signal.emit(1)
    

    emit 可以重新实现以将特定的信号值发送到插槽 功能。目前还不清楚为什么我需要发送不同的 来自先前存在的信号方法的值。

    您通常不应该发出内置信号。您应该只需要发出您定义的信号。定义信号时,可以定义不同类型的不同签名,插槽可以选择要连接的签名。例如,您可以这样做

    my_signal = pyqtSignal([int], [str])
    

    这将定义一个具有两个不同签名的信号,并且一个插槽可以连接到任何一个

    @pyqtSlot(int)
    def on_my_signal_int(self, value):
        assert isinstance(value, int)
    
    @pyqtSlot(str)
    def on_my_signal_str(self, value):
        assert isinstance(value, str)
    

    在实践中,我很少重载信号签名。我通常只会创建两个具有不同签名的单独信号,而不是重载相同的信号。但它存在并在 PyQt 中得到支持,因为 Qt 具有以这种方式重载的信号(例如 QComboBox.currentIndexChanged

    【讨论】:

    • 注释掉@pyqtSlot(int) 似乎不会影响输出。为什么会这样?
    • 看来装饰器的真正目的是为了重载。
    • 不只是为了重载。这个answer 很好地解释了它。如果你曾经跨线程发送信号,它也需要插槽装饰器。
    • 一个简单的旁注:因为my_signal 是类的一个属性,并且不与任何QWidget 关联。什么是创建类似self.lcd.my_signal.connect(on_my_signal) 的好 Qt 风格。
    • 如果您想向 LCD 小部件添加自定义信号,您需要对其进行子类化并将其添加为类属性。您也不应该从类中以外的任何地方发出信号,因此向非子类小部件添加信号实际上没有意义
    猜你喜欢
    • 2011-02-07
    • 1970-01-01
    • 2011-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-03
    • 2014-03-28
    相关资源
    最近更新 更多