Урок №9. Слоты, Сигналы и События в Qt5

  Дмитрий Бушуев  | 

  |

  Обновл. 16 Сен 2021  | 

 40804

 ǀ   10 

На этом уроке мы поговорим о слотах, сигналах и событиях в Qt5.

Модель событий в программах Qt5

Механизм сигналов и слотов является расширением языка программирования С++ в Qt5, который используется для установления связи между объектами. Если происходит какое-либо определенное событие, то при этом может генерироваться сигнал. Данный сигнал попадает в связанный с ним слот. В свою очередь, слот — это обычный метод в языке C++, который присоединяется к сигналу; он вызывается тогда, когда генерируется связанный с ним сигнал. Как видите, ничего сложного здесь нет.

Все графические приложения управляются событиями: всё, что происходит в приложении является результатом обработки тех или иных событий. Они являются важной частью любой графической программы. В большинстве случаев события генерируются пользователем приложения, но они также могут быть сгенерированы и другими средствами, например, подключением к интернету, оконным менеджером или таймером. При разработке программ в Qt5, задумываться о событиях приходится довольно редко, поскольку виджеты Qt5 генерируют сигналы, когда происходит нечто значительное. Сами же события приобретают значение в том случае, когда необходимо создать, например, новый виджет или расширить функционал существующего.

В модели событий есть 3 участника:

   источник события — это объект, состояние которого изменяется;

   объект события — это отслеживаемый параметр источника события (например, нажатие клавиши на клавиатуре или изменение размеров виджета);

   цель события — это объект, который должен быть уведомлен о произошедшем событии.

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

Щелчок мыши


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

Заголовочный файл — click.h:

Файл реализации — click.cpp:

Метод connect() соединяет сигнал со слотом. Когда мы нажимаем на кнопку Quit, генерируется сигнал щелчка кнопки мыши. qApp — это глобальный указатель на объект нашего приложения. Он определяется в заголовочном файле QApplication. Метод quit() вызывается при появлении сигнала щелчка мышкой:

Основной файл программы — main.cpp:

Результат выполнения программы:

Нажатие кнопки клавиатуры

В следующем примере мы рассмотрим способ реагирования на нажатие кнопки клавиатуры. Приложение завершит свое выполнение, если мы нажмем на клавишу Esc.

Заголовочный файл — keypress.h:

Файл реализации — keypress.cpp:

Одним из способов работы с событиями в Qt5 является переопределение обработчика событий. QKeyEvent — это класс, который содержит информацию о произошедшем событии. В нашем случае мы используем объект данного класса для определения того, что была нажата именно клавиша Esc:

Основной файл программы — main.cpp:

Класс QMoveEvent


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

Заголовочный файл — move.h:

Файл реализации — move.cpp:

Мы используем объект класса QMoveEvent для определения значений x и y:

Затем мы конвертируем целочисленные значения в строки:

И с помощью метода setWindowTitle() устанавливаем текст в заголовок окна:

Основной файл программы — main.cpp:

Результат выполнения программы:


Отключение сигналов

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

В заголовочном файле мы объявили два слота. Следует отметить, что slot не является ключевым словом в языке C++, а лишь расширением Qt5. Подобные расширения обрабатываются препроцессором фреймворка до выполнения компиляции кода. Когда в наших классах мы используем сигналы и слоты, то обязательно должны предоставить макрос Q_OBJECT в начале определения класса. В противном случае препроцессор будет выдавать сообщения об ошибках.

В следующем примере у нас есть кнопка и флажок. Флажок подключает и отключает слот от сигнала нажатия кнопок.

Заголовочный файл — disconnect.h:

Файл реализации — disconnect.cpp:

Подключаем сигналы к нашим пользовательским слотам:

Если мы делаем щелчок мышкой, то в окно терминала будет отправляться текст Button clicked:

Внутри слота onCheck() мы подключаем или отключаем слот onClick() от кнопки, в зависимости от полученного параметра состояния:

Основной файл программы — main.cpp:

Результат выполнения программы:


Таймер


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

В следующем примере мы попробуем отобразить в окне текущее местное время.

Заголовочный файл — timer.h:

Файл реализации — timer.cpp:

Для отображения времени мы используем виджет-метку:

Затем мы определяем текущее местное время и устанавливаем его в виджет-метку:

Запускаем таймер (при этом каждые 1000 мс генерируется событие таймера):

Для работы с событиями таймера необходимо переопределить метод timerEvent():

Основной файл программы — main.cpp:

Результат выполнения программы:


Заключение

На этом уроке мы узнали, что генерация события вызывается каким-либо действием со стороны пользователя (например, щелчок кнопкой мыши, изменение размеров окна и т.д.) или самой программой. При этом события являются механизмом оповещения более низкого уровня по сравнению с сигналами и слотами.

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (41 оценок, среднее: 4,73 из 5)
Загрузка...

Комментариев: 10

  1. Ayat:

    Как компьютер понимает, когда надо вызывать метод keyPressEvent()?
    Просто мы его негде не вызывали

    Я привык что в С++ для того чтобы метод начал работать, его имя нужно вызвать(написать) к примеру в main()-е. А тут мы просто написали метод, но нигде не вызывали его.
    Как компьютер понимает когда надо вызывать метод keyPressEvent(), т.е мы нажали на кнопку esc и как он понял что нам нужен именно этот метод?

    1. Сергей:

      Я сам пока учусь, но если я правильно понимаю, этот метод уже находиться внутри базового класс QWidget, но он там пуст. Система его вызывает автоматически при появлении события. И остаётся его только переопределить в дочернем классе.

      1. Ayat:

        Точно, спасибо))

      2. dn:

        Разве не лучше было бы явно биндить функцию к некому делегату, как я понял в QT все виджеты получают уведомление о каждом нажатии клавиш, а это скорее всего делается путем итерации по всем виджетам, а их может быть 50+ в одном окне

  2. Jane:

    Скажите, пожалуйста, как правильно в "Отключение сигналов" задать QTextStream out(stdout), чтобы при запуске через консоль печатало "Button clicked" в эту же консоль? Ожидалось, что будет печатать. Приложение запускается, чек-бокс работает, а при нажатии на кнопку не выводит ничего(

    1. Фото аватара Дмитрий Бушуев:

      Честно говоря — без понятия, никогда таким вопросом не задавался 🙂

  3. Александр:

    Подскажите по нескольким базовым вопросам:
    1. В файле keypress.h что значит class KeyPress : public QWidget {?
    2. В файле keypress.cpp что значит void KeyPress::keyPressEvent(QKeyEvent *event) {?
    3. Тут же откуда взялась qApp что она означает? в выражении qApp->quit();

    1. Фото аватара Дмитрий Бушуев:

      1. Это значит, что мы берем класс QWidget и создаем от него потомка (класс KeyPress). Такой приём носит название "Наследование". Благодаря ему класс потомок наделяется всеми свойствами, которые есть у предка. Подробнее об этом можно почитать здесь:
      https://ravesli.com/urok-153-nasledovanie-vvedenie/
      https://ravesli.com/urok-154-bazovoe-nasledovanie-v-c/

      2. void KeyPress::keyPressEvent(QKeyEvent *event) — это функция-обработчик события нажатия кнопки.

      3. Практически в самом начале статье есть ответ на ваш вопрос:
      "[…] qApp — это глобальный указатель на объект нашего приложения. Он определяется в заголовочном файле QApplication[…]".

      Взялось оно вот отсюда:

      1. Александр:

        1. А в файле "keypress.cpp"

        это просто конструктор, со списком инициализации членов?
        Как раз сейчас повторяю основы ООП 120 урок, до 153 еще не дошел! =)

        2. keyPressEvent — она описана в QT, в каком-нибудь QMoveEvent?
        Как туда перейти, чтобы посмотреть? Раньше вроде можно было F12 нажать и перейти к определению функции? Или я что-то путаю?

        1. Фото аватара Дмитрий Бушуев:

          1. Да, он самый 🙂
          2. https://imgur.com/a/nVejnzz

Добавить комментарий для Сергей Отменить ответ

Ваш E-mail не будет опубликован. Обязательные поля помечены *