1, Introduction to signal slot mechanism
1. Introduction to signal slot
Signal slot is the core mechanism of Qt and the communication mechanism of objects in PyQt programming. In Qt, QObject object and all controls inherited from QWidget in PyQt support signal slot mechanism. When the signal is transmitted, the connected slot function will be executed automatically. In PyQt5, the signal and slot function pass through object signal. Connect () method.
The characteristics of signal slot are as follows:
A. One signal can connect multiple slots
B. One signal can be connected to another
C. Signal parameters can be any Python type
D. One slot can monitor multiple signals
E. The connection mode between signal and slot can be synchronous connection or asynchronous connection.
F. The connection between signal and slot can cross threads
G. The signal can be disconnected
When writing a class, first define the signal and slot of the class, and connect the signal and slot in the class to realize the data transmission between objects. When the event or state changes, a signal will be sent, which will trigger the execution of the slot function associated with the event or signal. The mechanism of signal slot is as follows:
2. Define signal
The built-in signal of PyQt is automatically defined, using PyQt5 QtCore. pyqtSignal function can create a signal for QObject object, and use pyqtSignal function to define the signal as the attribute of the class.
class pyqtSignal: def __init__(self, *types, name: str = ...) -> None: ...
The types parameter indicates the type of the parameter when defining the signal. The name parameter indicates the name of the signal. The attribute name of the class is used by default.
Use pyqtSignal function to create one or more overloaded unbound signals as class properties. Signals can only be defined in subclasses of QObject. Signals must be defined at the time of class creation and cannot be added dynamically as class attributes after class creation. When the pyqtSignal function is used to define a signal, the signal can pass multiple parameters and specify the type of signal passing parameters. The parameter types are standard Python data types, including string, date, boolean type, number, list, dictionary and tuple.
from PyQt5.QtCore import pyqtSignal, QObject class StandardItem(QObject): # Define the signal. The signal has two parameters. The types of the two parameters are STR and STR respectively, and the signal name is dataChanged data_changed = pyqtSignal(str, str, name="dataChanged") # Update information and send signal def update(self): self.dataChanged.emit("old status", "new status")
3. Operation signal
Use the connect function to bind the signal to the slot function, use the disconnect function to unbind the signal from the slot function, and use the emit function to transmit the signal.
QObject.signal.connect(self, slot, type=None, no_receiver_check=False)
Establish the connection between the signal and the slot function. Type is the connection type.
QObject.signal.disconnect(self, slot=None)
Disconnect the signal from the slot
emit(self, *args)
Send signal, args is a variable parameter.
import sys from PyQt5.QtCore import pyqtSignal, QObject, QCoreApplication class StandardItem(QObject): # Define the signal. The signal has two parameters. The types of the two parameters are STR and STR respectively, and the signal name is dataChanged data_changed = pyqtSignal(str, str, name="dataChanged") # Update information and send signal def update(self): self.dataChanged.emit("old status", "new status") # Define slot function def onDataChanged(self, old, new): print(old) print(new) if __name__ == "__main__": app = QCoreApplication(sys.argv) item = StandardItem() item.dataChanged.connect(item.onDataChanged) item.update() sys.exit(app.exec_()) # OUTPUT: # old status # new status
2, Signal and slot applications
1. Built in signal and slot function
The built-in signal is the signal automatically defined by the QObject object, and the built-in slot function is the slot function automatically defined by the QObject object signal. The connect function connects the built-in signal of the QObject object to the slot function of the QObject object.
2. Built in signal and custom slot function
import sys from PyQt5.QtWidgets import QWidget, QApplication, QPushButton class MainWindow(QWidget): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("MainWindow Demo") self.resize(800, 600) button = QPushButton("close", self) # Connect built-in signal and custom slot button.clicked.connect(self.onClose) # Custom slot function def onClose(self): self.close() if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
When the button is clicked, the built-in clicked signal of the button is triggered to execute the bound custom slot function onClose.
3. Custom signal and built-in slot function
import sys from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import QWidget, QApplication, QPushButton class MainWindow(QWidget): closeSignal = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("MainWindow Demo") self.resize(800, 600) button = QPushButton("close", self) # Connect built-in signal and custom slot button.clicked.connect(self.onClose) # Connect the custom signal closeSignal with the built-in slot function close self.closeSignal.connect(self.close) # Custom slot function def onClose(self): # Send custom signal self.closeSignal.emit() if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
Connect the built-in signal clicked to the user-defined slot function onClose, send the user-defined signal closeSignal in the user-defined slot function onClose, and connect the user-defined signal closeSignal with the built-in slot function close.
4. Custom signal and custom slot function
import sys from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import QWidget, QApplication, QPushButton class MainWindow(QWidget): closeSignal = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("MainWindow Demo") self.resize(800, 600) button = QPushButton("close", self) # Connect built-in signal and custom slot button.clicked.connect(self.onClicked) # Connect the custom signal closeSignal with the built-in slot function close self.closeSignal.connect(self.onClose) # Custom slot function def onClicked(self): # Send custom signal self.closeSignal.emit() # Custom slot function def onClose(self): self.close() if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
3, Advanced application of signal slot
1. Custom signal slot
Signal objects are usually defined through class variables__ init__ Define a custom signal before the function.
class TestObject(QObject): # Define parameterless signals noParametersSignal = pyqtSignal() # A signal that defines a parameter of type int oneParameterSignal = pyqtSignal(int) # A signal that defines an overloaded version of a parameter. The parameter type can be int or str oneParameterOverloadSignal = pyqtSignal([int], [str]) # Defines the signal of overloaded version of two parameters. The parameter type is int,str or int,int twoParametersOverloadSignal = pyqtSignal([int, str], [int, int]) # Define a list parameter type signal oneParameterSignalList = pyqtSignal(list) # Define a signal of dict parameter type oneParameterSignalDict = pyqtSignal(dict) The slot function definition of the class is the same as the ordinary method definition of the class. class TestObject(QObject): # Define parameterless signals noParametersSignal = pyqtSignal() # A signal that defines a parameter of type int oneParameterSignal = pyqtSignal(int) # A signal that defines an overloaded version of a parameter. The parameter type can be int or str oneParameterOverloadSignal = pyqtSignal([int], [str]) # Defines the signal of overloaded version of two parameters. The parameter type is int,str or int,int twoParametersOverloadSignal = pyqtSignal([int, str], [int, int]) # Define a slot function without parameters def onNoParameterSlot(self): pass # Define a slot function with a parameter. The parameter is integer nIndex def onOneParameterSlot(self, nIndex): pass # Define a slot function with two parameters. The parameters are integer nIndex and string sStatus def onTwoParametersSlot(self, nIndex, sStatus): pass
Connect the signal and slot function through the connect method. The signal and slot function can belong to the same QObject object or different QObject objects.
test = TestObject() test.noParametersSignal.connect(test.onNoParameterSlot) test.oneParameterSignal.connect(test.onOneParameterSlot) test.twoParametersOverloadSignal.connect(test.onTwoParametersSlot)
The signal is sent through the emit method.
def update(self): self.noParametersSignal.emit() self.oneParameterSignal.emit(100) self.oneParameterOverloadSignal("Hello, PyQt5") self.twoParametersOverloadSignal(100, "Hello, PyQt5")
2. Signal slot pass custom parameters
The number of parameters sent by the signal in Qt must be greater than or equal to the number of parameters of the slot function. PyQt uses user-defined parameter transmission to solve the problem that there are more slot function parameters than signal parameters. You can pass custom parameters to the slot function by using Lambda expression or partial function of functools. The type of custom parameters can be any type of Python.
import sys from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QHBoxLayout from functools import partial class MainWindow(QWidget): def __init__(self, parent=None): super().__init__(parent) button1 = QPushButton("Button1", self) button2 = QPushButton("Button2", self) layout = QHBoxLayout() layout.addWidget(button1) layout.addWidget(button2) self.setLayout(layout) self.setWindowTitle("MainWindow Demo") self.resize(800, 600) # lambda button1.clicked.connect(lambda: self.onButtonClicked(1)) button2.clicked.connect(lambda: self.onButtonClicked(2)) # partial button1.clicked.connect(partial(self.onButtonClicked, 1)) button2.clicked.connect(partial(self.onButtonClicked, 2)) # Custom slot function def onButtonClicked(self, n): print("Button {0} is Clicked".format(n)) if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
3. Signal slot and decorator
In PyQt, you can define signal and slot functions through Python decorator. The usage method is as follows:
@PyQt5.QtCore.pyqtSlot(bool) def on_Sender object name_Name of transmitted signal(self, parameter): pass
The sender object name is the object name set for the QObject object using setObjectName. The connectSlotsByName function connected to the slot function through the signal name is as follows:
QtCore.QMetaObject.connectSlotsByName(self, QObject)
connectSlotsByName is used to connect some signals of QObject descendant objects to the corresponding slot function of some QObject objects according to their names.
import sys from PyQt5 import QtCore from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QHBoxLayout class MainWindow(QWidget): def __init__(self, parent=None): super().__init__(parent) button1 = QPushButton("Button1", self) button1.setObjectName("Button1") button2 = QPushButton("Button2", self) button2.setObjectName("Button2") layout = QHBoxLayout() layout.addWidget(button1) layout.addWidget(button2) self.setLayout(layout) self.setWindowTitle("MainWindow Demo") self.resize(800, 600) QtCore.QMetaObject.connectSlotsByName(self) @QtCore.pyqtSlot() def on_Button1_clicked(self): print("Button1 is clicked") @QtCore.pyqtSlot() def on_Button2_clicked(self): print("Button2 is clicked") if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
4, Event handling mechanism
1. Difference between event mechanism and signal slot mechanism
PyQt provides high-level signal slot mechanism and low-level event processing mechanism for event processing. Signal slot mechanism is a high-level encapsulation of event processing mechanism. When using the control, you do not need to consider the event processing mechanism, but only need to care about the signal slot; For custom derived controls, we must consider the event handling mechanism and re implement the corresponding event handling function according to the behavior requirements of the control.
2. Method of event handling
PyQt provides five kinds of event processing and filtering methods:
(1) Re implement the event handler
Common event handling functions, such as paintEvent, mouseMoveEvent, mousePressEvent, mouserereleaseevent, etc.
(2) Re implement QObject Event event distribution function
When adding new events, you need to re implement QObject Event method, and add the distribution route of new events.
(3) Install event filter
If the installEventFilter method is called on a QObject object, an event filter is installed for the QObject object. All events of the QObject object will be passed to the eventFilter function of the event filter first. Some events can be discarded or modified in the eventFilter function of the event filter. The user-defined event processing mechanism is used for the events of interest, and the default event processing mechanism is used for other events. The event filtering mechanism will filter all events of QObject. Therefore, if there are many events to be filtered, the program performance will be affected.
(4) Installing event filters in QApplication
Installing the event filter on the QApplication object will filter all events of all QObject objects and get the event first. That is, before the event is sent to any other event filter, it will be sent to the event filter of QApplication first.
(5) Restart the notify method of QApplication
PyQt uses the notify method of QApplication object to distribute events. The only way to capture events before any event filter is to re implement the notify method of QApplication.
3. Event handling instance
The QDialog dialog box will automatically exit when the ESC key is pressed, and use event processing and filtering to process the pressing of ESC key.
(1) Re implement the event handler
import sys from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtCore import Qt class Dialog(QDialog): def __init__(self, parent=None): super().__init__(parent) # Re implement keyPressEvent def keyPressEvent(self, event): if event.key() != Qt.Key_Escape: QDialog.keyPressEvent(self, event) if __name__ == "__main__": app = QApplication(sys.argv) dialog = Dialog() dialog.exec_() sys.exit(app.exec_())
(2) Re implement the event function
import sys from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeyEvent class Dialog(QDialog): def __init__(self, parent=None): super().__init__(parent) # Re implement keyPressEvent def event(self, event): if event.type() == QKeyEvent.KeyPress and event.key() == Qt.Key_Escape: return True else: return QDialog.event(self, event) if __name__ == "__main__": app = QApplication(sys.argv) dialog = Dialog() dialog.exec_() sys.exit(app.exec_())
(3) QObject install event filter
import sys from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeyEvent class Dialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.installEventFilter(self) def eventFilter(self, watched, event): if event.type() == QKeyEvent.KeyPress and event.key() == Qt.Key_Escape: return True else: return QDialog.eventFilter(self, watched, event) if __name__ == "__main__": app = QApplication(sys.argv) dialog = Dialog() dialog.exec_() sys.exit(app.exec_())
(4) QApplication install event filter
import sys from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeyEvent class Dialog(QDialog): def __init__(self, parent=None): super().__init__(parent) def eventFilter(self, watched, event): if event.type() == QKeyEvent.KeyPress and event.key() == Qt.Key_Escape: return True else: return QDialog.eventFilter(self, watched, event) if __name__ == "__main__": app = QApplication(sys.argv) dialog = Dialog() app.installEventFilter(dialog) dialog.exec_() sys.exit(app.exec_())