События

Обработка событий

Любое взаимодействие пользователя с приложением Qt, будь то клики мышкой или нажатия клавиш клавиатуры, осуществляется в форме событий (events). Кроме пользователя события могут быть порождены операционной системой, самим приложением или другими событиями. Чтобы как-то реагировать на эти события, приложение должно следить за их возникновениями и как-то их обрабатывать (event handling). В частности, метод exec приложения запускает событийный цикл (event loop), которая и организовывает всё взаимодействие с графическим интерфейсом.

../_images/event-loop.png

Fig. 2 Картинка позаимствована отсюда.

Каждое взаимодействие с приложением порождает объект типа QtCore.QEvent или производного от него типа. Затем событие помещается в очередь событий (event queue). Событийный цикл на каждой итерации проверяет эту очередь и, если находится ожидающее событие, передаёт управление соответствующему обработчику (event handler).

Например, щелчок мышью на виджет состоит из двух событий: зажатие кнопки мыши QEvent.MouseButtonPress и отпуск кнопки мыши QEvent.MouseButtonRelease. Оба из них генерируют объект события типа QMouseEvent. Чтобы их обработать особым образом, необходимо переопределить у виджета соответствующие обработчики.

В качестве примера реализуем обработчик события нажатия клавишей мыши на главное окно. Для этого необходимо перегрузить метод mousePressEvent класса QMainWindow. Перегрузим его так, чтобы он печатал в консоль информацию о том, где и какая конкретно кнопка была нажата. Информацию об этом можно получить из объекта QMouseEvent, который этот метод получает в качестве параметра.

import sys

from PySide6.QtWidgets import QApplication, QMainWindow


class MainWindow(QMainWindow):
    def mousePressEvent(self, event):
        button_name = event.button().name.decode()
        pos = event.position() 
        x, y = pos.x(), pos.y()
        print(f"{button_name:12} mouse button was pressed at x={int(x):3d} and y={int(y):3d}")


app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec()

При нажатии мышкой в любом месте внутри окна в консоли должно печататься сообщение.

Распространение события на родительский виджет

У QEvent есть флаг accept. Обработчик события может выставить значение этого флага в False методом QEvent.ignore, чтобы сигнализировать, что этот виджет не хочет обрабатывать это событие. В таком случае обработка такого события передаётся родительскому (parent) виджету.

Для демонстрации этого определим кнопку, которая будет игнорировать нажатие клавиши мышки. Для этого расширим класс QPushButton и перегрузим у него обработчик события mousePressEvent вызовом метода ignore у объекта события.

class IgnoringButton(QPushButton):
    def mousePressEvent(self, event):
        event.ignore()

Также определим кнопку, которая при нажатии мышкой на ней будет печатать в консоли соответствующее сообщение.

class AcceptingButton(QPushButton):
    def mousePressEvent(self, event):
        event.accept()
        print("A mouse button was pressed at the accepting button")

Добавим эти кнопки на окно из предыдущего примера и получим нечто следующее.

import sys

from PySide6.QtWidgets import QApplication, QMainWindow, QHBoxLayout, QWidget, QPushButton


class IgnoringButton(QPushButton):
    def mousePressEvent(self, event):
        event.ignore()


class AcceptingButton(QPushButton):
    def mousePressEvent(self, event):
        event.accept()
        print("A mouse button was pressed at the accepting button")


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        widget = QWidget(parent=self)
        self.setCentralWidget(widget)

        layout = QHBoxLayout()
        layout.addWidget(AcceptingButton("Accept"))
        layout.addWidget(IgnoringButton("Ignore"))
        widget.setLayout(layout)

    def mousePressEvent(self, event):
        print("A mouse button was pressed at the main window")


app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec()

Запустив это код вы увидите следующее окно.

../_images/ignore_event.png

Нажав на кнопку Accept вы увидите в консоли “A mouse button was pressed at the accepting button”, а нажав в любом другом месте, в том числе и на кнопку Ignore, вы увидите другое сообщение “A mouse button was pressed at the main window”.

Виды событий

Мышь

Пока обсуждались только события мыши, каждый из которых создаёт QMouseEvent:

  • QEvent.MouseButtonDblClick — двойной щелчок клавишей мыши;

  • QEvent.MouseButtonPress — нажатие клавиши мыши;

  • QEvent.MouseButtonRelease — отпуск клавиши мыши;

  • QEvent.MouseMove — перемещение указателя мыши;

Клавиатура

Другая пара событий связанна с нажатием и отпуском клавиш клавиатуры, которым соответствует QKeyEvent:

  • QEvent.KeyPressEvent — нажатие клавиши клавиатуры;

  • QEvent.KeyReleaseEvent — отпуск клавиши клавиатуры;

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

import sys

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QMainWindow


class MainWindow(QMainWindow):
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()


app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec()

Ещё

Существует ещё огромное количество событий операционной системы, самого Qt и другие.