События и сигналы в PyQt5

В этой части учебника PyQt5, мы изучим события и сигналы, встречающиеся в приложениях.

События

Все приложения с графическим интерфейсом являются событийно-ориентированными. События вызываются главным образом пользователем приложения. Однако, они могут быть вызваны другими средствами, к примеру подключением к Интернету, диспетчером окон или таймером. Когда мы вызываем метод exec_(), приложение входит в главный цикл. Главный цикл получает события и отправляет их объектам.

В модели событий имеются три участника:

  • Источник события
  • Объект события
  • Цель события

Источник события – это объект, состояние которого меняется. Он вызывает событие. Событие инкапсулирует изменение состояния в источнике события. Цель события – это объект, которому требуется уведомление. Объект источника события делегирует задачу обработки события цели события.

Для работы с событиями PyQt5 имеет уникальный механизм сигналов и слотов. Сигналы и слоты используются для связи между объектами. Сигнал срабатывает тогда, когда происходит конкретное событие. Слот может быть любой функцией. Слот вызывается, когда срабатывает его сигнал.

Сигналы и слоты

Это простой пример, демонстрирующий сигналы и слоты в PyQt5.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QWidget, QLCDNumber, QSlider,
    QVBoxLayout, QApplication)


class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()


    def initUI(self):

        lcd = QLCDNumber(self)
        sld = QSlider(Qt.Horizontal, self)

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

        self.setLayout(vbox)
        sld.valueChanged.connect(lcd.display)

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

В этом примере, мы показываем QtGui.QLCDNumber и QtGui.QSlider. Мы меняем число lcd путём перемещения ползунка регулятора.

sld.valueChanged.connect(lcd.display)

Здесь мы присоединяем сигнал valueChanged слайдера к слоту display числа lcd.

Отправитель – объект, который посылает сигнал. Получатель – объект, который получает сигнал. Слот – это метод, который реагирует на сигнал.

Сигнал и слот

Переопределение обработчика события

События в PyQt5 часто обрабатываются путём переопределения обработчиков.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QApplication


class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()


    def initUI(self):

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Event handler')
        self.show()


    def keyPressEvent(self, e):

        if e.key() == Qt.Key_Escape:
            self.close()


if __name__ == '__main__':

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

В этом примере, мы переопределяем обработчик события keyPressEvent().

def keyPressEvent(self, e):

    if e.key() == Qt.Key_Escape:
        self.close()

Если мы нажимаем клавишу Esc, то приложение завершается.

Отправитель события

Иногда удобно знать, какой именно виджет является отправителем сигнала. Для этого PyQt5 имеет метод sender().

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QMainWindow, QPushButton, QApplication


class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()


    def initUI(self):

        btn1 = QPushButton("Button 1", self)
        btn1.move(30, 50)

        btn2 = QPushButton("Button 2", self)
        btn2.move(150, 50)

        btn1.clicked.connect(self.buttonClicked)
        btn2.clicked.connect(self.buttonClicked)

        self.statusBar()

        self.setGeometry(300, 300, 290, 150)
        self.setWindowTitle('Event sender')
        self.show()


    def buttonClicked(self):

        sender = self.sender()
        self.statusBar().showMessage(sender.text() + ' was pressed')


if __name__ == '__main__':

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

В нашем примере у нас есть две кнопки. В методе buttonClicked() мы определяем, какую из кнопок мы нажали с помощью метода sender().

btn1.clicked.connect(self.buttonClicked)
btn2.clicked.connect(self.buttonClicked)

Обе кнопки подключаются к одному слоту.

def buttonClicked(self):

    sender = self.sender()
    self.statusBar().showMessage(sender.text() + ' was pressed')

Мы определяем источник сигнала с помощью метода sender(). В строке состояния приложения, мы показываем метку нажатой кнопки.

Отправитель события

Отправка сигналов

Объекты, создаваемые из QObject, могут посылать сигналы. В следующем примере, мы увидим, как мы может послать пользовательский сигнал.

import sys
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import QMainWindow, QApplication


class Communicate(QObject):

    closeApp = pyqtSignal()


class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.initUI()


    def initUI(self):

        self.c = Communicate()
        self.c.closeApp.connect(self.close)

        self.setGeometry(300, 300, 290, 150)
        self.setWindowTitle('Emit signal')
        self.show()


    def mousePressEvent(self, event):

        self.c.closeApp.emit()


if __name__ == '__main__':

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

Мы создаём новый сигнал, названный closeApp. Этот сигнал отправляется во время события нажатия кнопки мыши. Сигнал присоединяется к слоту close() класса QMainWindow.

class Communicate(QObject):

    closeApp = pyqtSignal()

Сигнал создаётся с помощью pyqtSignal() как атрибут внешнего класса Communicate.

self.c = Communicate()
self.c.closeApp.connect(self.close)

Пользовательский сигнал closeApp присоединяется к слоту close() класса QMainWindow.

def mousePressEvent(self, event):

    self.c.closeApp.emit()

Когда мы кликаем на окне курсором мыши, посылается сигнал closeApp. Приложение завершается.

В этой части руководства PyQt5, мы рассмотрели сигналы и слоты.

Для вставки кода на Python в комментарий заключайте его в теги <pre><code class="python3">Ваш код</code></pre>
Опечатка в тексте:
Послать сообщение об ошибке автору?