PyQt5信号槽装饰器的例子

Max*_*Max 7 python qt pyqt decorator signals-slots

我目前正在创建一个生成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_())
Run Code Online (Sandbox Code Playgroud)

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

  • 为什么在删除时pyqtSlot(int)根本不使用pyqtSlot()装饰器对代码没有影响?
  • 你能举例说明什么时候需要吗?

对于具体原因,我想生产使用pyqtSignal()工厂我自己的信号和我给出体面的文件在这里.然而,唯一的问题是,这个非常简单的例子没有为如何发出特定信号提供坚实的基础.

这是我想要做但却发现自己迷失了:

  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
Run Code Online (Sandbox Code Playgroud)

如你看到的.我给小部件命名,因为我发现它很有用,我也尝试从类中实例化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_())
Run Code Online (Sandbox Code Playgroud)

这里只需解决两个问题:

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

我可能完全偏离并过度思考解决方案,随意纠正我的代码,以便我可以让我的信号发出滑块的值.

Bre*_*bel 7

在删除pyqtSlot(int)对代码无影响时,为什么要完全使用pyqtSlot()装饰器?您能否举例说明何时有必要?

先前的问题已解决,但我会再次重申。

尽管PyQt4允许在连接信号时将任何可调用的Python用作插槽,但有时有必要将Python方法显式标记为Qt插槽并为其提供C ++签名。PyQt4提供了pyqtSlot()函数装饰器来执行此操作。

将信号连接到经过修饰的Python方法还具有减少使用的内存量的优点,并且速度稍快。

同样,如上一个问题所述您不能将信号创建为实例变量,它们必须是类attribute

这永远都行不通

def setSignal(self,type):
    self.signal = pyqtSignal(type)
Run Code Online (Sandbox Code Playgroud)

肯定是

class MyWidget(QWidget):
    signal = pyqtSignal(int)
Run Code Online (Sandbox Code Playgroud)

创建一个元类,该元类允许实现许多不同类型的QWidget子类。

您无法为定义元类QObjects,因为它们已经使用了自己的元类(这是使信号魔术起作用的原因)。但是,您仍然可以QObject使用该type函数创建子类工厂。

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)
Run Code Online (Sandbox Code Playgroud)

让元类产生自己的信号,可以从类外部调用该信号。

简而言之,不要这样做。您不应该从课堂之外发出信号。上面的工厂示例不是很有用,因为您没有在这些类上定义自定义方法来发出那些信号。通常,这就是您使用自定义信号的方式

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
Run Code Online (Sandbox Code Playgroud)