Python PyQt 正确使用emit() 和pyqtSignal()

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36434706/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 17:52:36  来源:igfitidea点击:

PyQt proper use of emit() and pyqtSignal()

pythonpyqtsignalssignals-slotspyqt5

提问by Max

I am reading through some documentation on PyQt5 to come up with a simple signal-slot mechanism. I have come to a halt due to a design consideration.

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

Consider the following code:

考虑以下代码:

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_())

To track the changes made to the slider, I simply print and log the changes made. What I do not like about the code is that I am required to call the sld.valueChangedslot thrice to send the same information to 3 different slots.

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

Is it possible to create my own pyqtSignalthat sends an integer to a single slot function. And in turn have the slot function emit the changes that need to be made?

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

  • Maybe I don't fully understand the purpose of emit()because there are no good examples of it's purpose in the PyQt Signal-Slot docs. All we're given is an example of how to implement an emitwith no parameters.
  • 也许我不完全理解的目的,emit()因为在PyQt Signal-Slot docs中没有很好的例子说明它的目的。我们给出的只是一个如何实现emit没有参数的示例。

What I would like to do is create a function that handles the emit function. Consider the following:

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

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_())

There are obviously some serious design flaws here. I cannot wrap my head around the order of function calls. And I am not implementing pyqtSignalcorrectly. I do however believe that correctly stating the following 3 points will help me produce a proper app:

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

  1. For a predefined signal: send the signal to the slot function. Slot can be reimplemented to use the signal values.
  2. Produce pyqtSignalobject with some parameters. It is not yet clear what the purpose of these parameters are and how they differ from 'emit' parameters.
  3. emitcan be reimplemented to send specific signal values to the slot function. It is also not yet clear why I would need to send different values from previously existing signal methods.
  1. 对于预定义的信号:将信号发送到槽函数。插槽可以重新实现以使用信号值。
  2. 生成pyqtSignal带有一些参数的对象。目前尚不清楚这些参数的目的是什么以及它们与“发射”参数有何不同。
  3. emit可以重新实现以将特定信号值发送到槽函数。目前还不清楚为什么我需要从以前存在的信号方法中发送不同的值。

Feel free to completely alter the code for what I am trying to do because I have not yet figured out if its in the realm of good style.

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

回答by Brendan Abel

You can define your own slot (any python callable) and connect that to the signal, then call the other slots from that one slot.

您可以定义自己的插槽(任何可调用的 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')

Also, if you want to define your own signals, they have to be defined as class variables

此外,如果您想定义自己的信号,则必须将它们定义为类变量

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

The arguments to pyqtSignaldefine the types of objects that will be emit'd on that signal, so in this case, you could do

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

self.my_signal.emit(1)

emit can be reimplemented to send specific signal values to the slot function. It is also not yet clear why I would need to send different values from previously existing signal methods.

可以重新实现发射以将特定信号值发送到槽函数。目前还不清楚为什么我需要从以前存在的信号方法中发送不同的值。

You generally shouldn't be emitting the built in signals. You should only need to emit signals that you define. When defining a signal, you can define different signatures with different types, and slots can choose which signature they want to connect to. For instance, you could do this

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

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

This will define a signal with two different signatures, and a slot could connect to either one

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

@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)

In practice, I rarely overload signal signatures. I would normally just create two separate signals with different signatures rather than overloading the same signal. But it exists and is supported in PyQt because Qt has signals that are overloaded this way (eg. QComboBox.currentIndexChanged)

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