PyQt - Slot Connection



Managing signal-slot connection is one of the important feature of PyQt which enables communication between different component of the application. The connectSlotsByName() function is used in PyQt to establish these signal-slot connection automatically if the code is generated by pyuic5. In the case of overloaded signal, we cannot automatically bind the signal-slot connection so in that case manual intervention is required.

Automatic Signal-Slot Connections with connectSlotsByName()

The connectSlotsByName() function in PyQt can automatically establish connection of signals to slots based on a simple naming convention. When signals emitted by GUI elements match the names of slots in the Python code, PyQt automatically establishes the connections. This convention simplifies event handling and reduces the need for explicit connection statements.

Problem with automatic signal slot connection

In case of overloaded signals with same signal name but with different parameters type, it will be difficult to connect the slot automatically to one signal. Suppose,you have a QSpinBox widget, which emits two signals upon value changes: valueChanged(int) and valueChanged(const QString &). If a slot named on_spinbox_valueChanged is implemented, it will be connected to both variations of the signal by default. Consequently, the slot will be invoked twice upon each value change event, once with an integer argument and once with a string argument.

Handling Overloaded Signals with pyqtSlot()

In order to resolve the issue arising due to overloaded signal, pyqtSlot() decorator can be used to specify the exact siganl to which the slot should be connected. By annotating slot definitions with pyqtSlot(), developers can ensure precise signal-slot mapping, enhancing code clarity and functionality.

For example, to connect a slot exclusively to the integer variant of the valueChanged signal emitted by a QSpinBox, the slot definition can be annotated as follows −

@pyqtSlot(int)
def on_spinbox_valueChanged(self, i):
   # Slot implementation for integer variant.
   pass

In this case, the slot will only respond to signals with an integer argument, ignoring the string variant. This process of using pyqtSlot() decorator enables tight control over signal handling, aligning with the application's requirements.

Example 1: Handling Integer Variant Signal

from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox
from PyQt5.QtCore import pyqtSlot

class MainWindow(QMainWindow):
   def __init__(self):
      super().__init__()
      self.spinBox = QSpinBox(self)
      self.spinBox.valueChanged.connect(self.on_spinbox_valueChanged)

   @pyqtSlot(int)
   def on_spinbox_valueChanged(self, value):
      print("Integer value changed:", value)

if __name__ == "__main__":
   app = QApplication([])
   window = MainWindow()
   window.show()
   app.exec_()

Output

Handling Integer Variant Signal
Integer value changed: 5

Example 2: Handling String Variant Signal

from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox
from PyQt5.QtCore import pyqtSlot

class MainWindow(QMainWindow):
   def __init__(self):
      self.spinBox = QSpinBox(self)
      self.spinBox.valueChanged[str].connect(self.on_spinbox_valueChanged)

   @pyqtSlot(str)
   def on_spinbox_valueChanged(self, value):
      print("String value changed:", value)

if __name__ == "__main__":
   app = QApplication([])
   window = MainWindow()
   window.show()
   app.exec_()

Output

Handling Value Variant Signal
String value changed: 1
String value changed: 2
String value changed: 3
String value changed: 4
String value changed: 5

Differentiating Signal Variants with Custom Slot Names

When both the variants of the overloaded signal need to be handled, but with distinct slot implementations, custom slot names can be assigned using the name parameter of the pyqtSlot() decorator. This approach allows multiple slots to be connected to the same signal where each slot can handle a specific signal variant.

Example

@pyqtSlot(int, name='on_spinbox_valueChanged')
def spinbox_int_value(self, i):
   # Slot implementation for integer variant.
   pass

@pyqtSlot(str, name='on_spinbox_valueChanged')
def spinbox_qstring_value(self, s):
   # Slot implementation for string variant.
   pass

Here, two slots are defined, each annotated with the pyqtSlot() decorator and assigned distinct names. Despite sharing the same signal source, these slots will be connected separately, ensuring that each variant of the valueChanged signal is routed to the appropriate slot.

Example: Handling Both Signal Variants with Custom Slot Names

from PyQt5.QtWidgets import QApplication, QMainWindow, QSpinBox
from PyQt5.QtCore import pyqtSlot

class MainWindow(QMainWindow):
   def __init__(self):
      super().__init__()
      self.spinBox = QSpinBox(self)
      self.spinBox.valueChanged[int].connect(self.spinbox_int_value)
      self.spinBox.valueChanged[str].connect(self.spinbox_qstring_value)

   @pyqtSlot(int, name='on_spinbox_valueChanged')
   def spinbox_int_value(self, value):
      print("Integer value changed:", value)

   @pyqtSlot(str, name='on_spinbox_valueChanged')
   def spinbox_qstring_value(self, value):
      print("String value changed:", value)

if __name__ == "__main__":
   app = QApplication([])
   window = MainWindow()
   window.show()
   app.exec_()

Output

Handling Both Int String Variant
String value changed: 1
Integer value changed: 1
String value changed: 2
Integer value changed: 2
String value changed: 3
Integer value changed: 3
String value changed: 4
Integer value changed: 4
String value changed: 5
Integer value changed: 5
Advertisements