События и сигналы в PyQt5 [Урок №5]
Изучение PyQt5 PyQt, QMainWindow, QObject, QPushButton, QtCore, QtGui, QWidget, statusBar, примеры PyQt
В этой части руководства по программированию с PyQt5
мы изучим события и сигналы, встречающиеся в приложениях.
События
Все приложения с графическим интерфейсом являются событийно-ориентированными. События вызываются главным образом пользователем приложения. Однако, они могут быть вызваны другими средствами, к примеру подключением к Интернету, диспетчером окон или таймером. Когда мы вызываем метод exec_(), приложение входит в главный цикл. Главный цикл получает события и отправляет их объектам.
В событийной модели имеются три участника:
- Источник события;
- Объект события;
- Цель события.
Источник события – это объект, состояние которого меняется. Он вызывает события. Объект события (событие) внедряет состояние, которое меняется в источнике события. Цель события – это объект, которому требуется уведомление. Объект источника события поручает задачу обработки события цели события.
Чтобы начать работу с событиями, PyQt5 имеет уникальный механизм сигналов и слотов. Сигналы и слоты используют для связи между объектами. Сигнал срабатывает, когда происходит конкретное событие. Слот может быть чем-то, вызываемым средствами Python. Слот вызывается, когда срабатывает его сигнал.
Сигналы и слоты
Это простой пример, демонстрирующий сигналы и слоты в 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, могут имитировать сигналы. В следующем примере, мы увидим, как мы может имитировать пользовательский сигнал.
#!/usr/bin/python3
# -*- coding: utf-8 -*-
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, мы покрыли сигналы и слоты.
Здравствуйте! Спасибо за уроки, мне очень помогли, но здесь, в Событиях и Сигналах, первоначально я просто завис на уровне понимания из-за того, что в тексте используются прямые кальки с английского.
Читаю "Объекты, создаваемые из QObject, могут имитировать сигналы. В следующем примере, мы увидим, как мы может имитировать пользовательский сигнал" - понимаю "Объекты, создаваемые из QObject, могут ПОДРАЖАТЬ сигналам. В следующем примере, мы увидим, как мы может ПОДРАЖАТЬ пользовательскому сигналу." Долго ничего понять не мог - причём тут имитация? Причём тут подражание? Для чего требуется подражать сигналам?
Пока не посмотрел название метода - emit()! Ну, блин, ну есть же устоявшие слова в русском языке, производные от этого emit: эмиссия - излучение, эмиттер - излучатель, передающая антенна. И вот тогда всё встало на свои места - объект посылает сигналы, как радиопередатчик. Он так же, как и радиопередатчик не знает - примет ли кто его сигнал или нет и не ждёт ответа от принявшего сигнал. А вот слоты - это радиоприёмники, которые можно настроить на приём этих сигналов.
Они могут научить нас чему-либо кроме закрытия приложения?!
Виктор Смирнов, хорошо объяснил!
Прекрасная серия уроков, спасибо большое