PyQt5 Signal Slot Decoder Example

I am currently creating a class creating pyqtSignal (int) and pyqtSlot (int). The difficulty lies in creating a signal that emits a specific value.

Suppose I want to do something similar to the following simple example:

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

My first question for the above code:

  • Why use the pyqtSlot () decorator at all when removing pyqtSlot(int) does not affect the code?
  • Can you give an example of when this was necessary?

For specific reasons, I would like to create my own signal using the pyqtSignal () factory and get decent documentation here . The only problem, however, is that a simple simple example does not provide a solid basis for emitting certain signals.

Here is what I am trying to do, but have lost myself:

  • Create a metaclass that allows you to implement many different subclasses of QWidget.
  • Provide the metaclass with its own signal that can be called from outside the class.

This is what I am going to do:

 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 

As you can see. I give the widget a name because I find it useful, and I'm also trying to instantiate a QWidget from a class to simplify the code.

This is how I would like the main class to create widgets from the first example:

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

Here you need to solve only two problems:

  • The template metaclass is not configured correctly, and I cannot create an instance of QWidget from the metaclass. Q_Type tells me that its type is PyQt5.QtCore.pyqtWrapperType when it should be PyQt5.QtWidgets.QSlider .
  • Even if I create newSignal through a template class, how do I get QSlider to send the modified value that I want to send. I believe that this is where emit() comes into play, but does not have enough knowledge about how this is useful. I know that I could make some kind of call to the self.sld.emit(35) function if I wanted the signal to pass the value 35 to the slot function. The question is not, but, but where should I implement this function?

I can completely abandon the solution and rethink the solution, feel free to correct my code so that I can make my signal emit the value of a slider.

+5
source share
1 answer

Why use the pyqtSlot () decorator at all when removing pyqtSlot (int) does not affect the code? Can you give an example of when this will be necessary?

Already reviewed in the previous question , but I will repeat the repeat again.

Although PyQt4 allows you to use any Python to use as a slot when connecting, it is sometimes necessary to explicitly mark Python as a Qt slot and provide a C ++ signature for This. PyQt4 provides a pyqtSlot () function decorator for this.

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

Also, as described in the previous question , you cannot create signals as instance variables, they must be class attributes .

It will never work

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

It should be

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

Create a metaclass that allows you to implement many different subclasses of QWidget.

You cannot define a metaclass for QObjects , since they already use their own metaclass (this is what allows signal magic to work). However, you can still subclass QObject factory using the type function.

 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) 

Ask the metaclass to create its own signal that can be called from outside the class.

In short, do not do this. You must not emit signals from outside the class. The factory example above is not very useful because you do not define your own methods for these classes to emit these signals. Typically, you use custom signals

 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 
+3
source

Source: https://habr.com/ru/post/1246589/


All Articles