PyQt5信号槽装饰器examp

2024-04-29 12:24:40 发布

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

我目前正在创建一个类,该类生成pyqtSignal(int)和pyqtSlot(int)。困难在于创建一个发出特定值的信号。

假设我要生成类似于以下简单示例的内容:

import sys
from PyQt5.QtCore import (Qt, pyqtSignal, pyqtSlot)
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)

    @pyqtSlot(int)
    def on_sld_valueChanged(self, value):
        self.lcd.display(value)
        self.printLabel(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')
        self.show()

if __name__ == '__main__':

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

我对上述代码的第一个问题是:

  • 当删除pyqtSlot(int)对代码没有影响时,为什么要使用pyqtSlot()修饰符?
  • 你能举例说明什么时候有必要吗?

出于特定的原因,我想使用pyqtSignal()工厂生成自己的信号,并获得了像样的文档here。然而,唯一的问题是,非常simple example没有给出如何发射特定信号的坚实基础。

以下是我试图做的,但却发现自己迷失了方向:

  1. 创建一个元类,允许实现许多不同类型的QWidget子类。
  2. 让元类产生自己的信号,可以从类外部调用。

这就是我想要的:

from PyQt5.QtWidgets import QPushButton, QWidget
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QSlider

def template(Q_Type, name: str, *args):
    class MyWidget(Q_Type):

        def __init__(self) -> None:
            super().__init__(*args)
            self._name = name

        def setSignal(self,type):
            self.signal = pyqtSignal(type)

        def callSignal(self):
            pass

    return MyWidget

如你所见。我给小部件起了个名字,因为我觉得这很有用,我还尝试从类中实例化QWidget来简化代码。

这是我希望主类根据第一个示例生成小部件的方式:

import sys
from PyQt5.QtCore import (Qt, pyqtSignal, pyqtSlot)
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)

    @pyqtSlot(int)
    def sld_valChanged(self, value):
        self.lcd.display(value)
        self.printLabel(value)

    def initUI(self):

        #instantiate the QWidgets through template class
        self.lcd = template(QLCDNumber,'lcd_display')
        self.sld = template(QSlider, 'slider', Qt.Horizontal)

        #create signal
        #self.sld.setSignal(int)

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

        self.setLayout(vbox)

        #connect signal - this won't send the value of the slider
        #self.sld.signal.connect(self.sld_valChanged)
        self.sld.valueChanged.connect(self.sld_valChanged)

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

这里只需要解决两个问题:

  1. 模板元类设置不正确,我无法从元类中实例化QWidget。Q_Type告诉我它的类型是PyQt5.QtCore.pyqtWrapperType,而它应该是PyQt5.QtWidgets.QSlider
  2. 即使我正在通过模板类创建一个newSignal,我如何让QSlider发送我希望它发送的更改值。我相信这就是emit()发挥作用的地方,但对于它如何有用却没有足够的知识。 我知道如果我想让信号把值35传递给slot函数,我可以进行某种函数调用self.sld.emit(35)。问题不是如何实现,而是在哪里实现这个功能?

我可能完全偏离了基础和过度思考的解决方案,请随意更正我的代码,以便我可以让我的信号发射值的滑块。


Tags: fromimportselflcd信号valuedefpyqt5
1条回答
网友
1楼 · 发布于 2024-04-29 12:24:40

Why use the pyqtSlot() decorator at all when removing pyqtSlot(int) has no effect on the code? Can you give an example of when it would be necessary?

已经在您的previous question中解决了,但我将再次重复。

Although PyQt4 allows any Python callable to be used as a slot when connecting signals, it is sometimes necessary to explicitly mark a Python method as being a Qt slot and to provide a C++ signature for it. PyQt4 provides the pyqtSlot() function decorator to do this.

Connecting a signal to a decorated Python method also has the advantage of reducing the amount of memory used and is slightly faster.

此外,正如previous question中所述,不能将信号创建为实例变量,它们必须是类属性。

这是行不通的

def setSignal(self,type):
    self.signal = pyqtSignal(type)

一定是

class MyWidget(QWidget):
    signal = pyqtSignal(int)

Create a metaclass that allows for the implementation of many different types of QWidget subclasses.

您不能为QObjects定义一个元类,因为它们已经使用了自己的元类(这就是允许信号魔术工作的原因)。但是,您仍然可以使用^{}函数生成一个QObject子类工厂。

def factory(QClass, cls_name, signal_type):
    return type(cls_name, (QClass,), {'signal': pyqtSignal(signal_type)})

MySlider = factory(QSlider, 'MySlider', int)
self.sld = MySlider(Qt.Horizontal)
self.sld.signal.connect(self.on_signal)

Have the metaclass produce its own signal that can be called from outside the class.

总之,不要这样做。你不应该从课外发出信号。上面的工厂示例不是很有用,因为您没有在这些类上定义自定义方法来发出这些信号。通常,这是如何利用自定义信号的

class MyWidget(QWidget):
    colorChanged = pyqtSignal()

    def setColor(self, color):
        self._color = color
        self.colorChanged.emit()


class ParentWidget(QWidget):

    def __init__(self):
        ...
        self.my_widget = MyWidget(self)
        self.my_widget.colorChanged.connect(self.on_colorChanged)
        # This will cause the colorChanged signal to be emitted, calling on_colorChanged
        self.my_widget.setColor(Qt.blue)

    def on_colorChanged(self):
        # do stuff

相关问题 更多 >