Qt/C++ - Урок 024. Сигналы и слоты в Qt5

РуководствоQtQt5, пример, example, сигнал, сигналы Qt, сигналы слоты Qt, сигналы слоты Qt5, слот, слоты Qt3706

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

Введение

При программировании GUI, когда изменяется один из виджетов, мы зачастую хотим, чтобы другие виджеты были об этом уведомлены. В общем случае, мы хотим, чтобы объекты могли взаимодействовать друг с другом. Например, если пользователь нажал кнопку Закрыть , мы вероятно захотим, чтобы объект window вызвал функцию close().

Другие инструменты разработки обеспечивают подобный функционал использованием callback. callback являет указателем на функцию, и если Вы хотите выполнить функцию, которая уведомит Вас о каком-либо событии, то Вы передаёте указатель на другую функцию, то есть callback. Работающая функция вызывает callback тогда, когда это уместно. В то время как существуют фреймворки, которые успешно используют callback функции, тем не менее callback являются неинтуитивным способом, который может вызывать проблемы с обеспечением корректности возвращаемых аргументов.

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

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

сигналы и слоты в Qt

Сигналы и слоты являются типо-безопасным механизмом. Сигнатура сигнала должа совпадать с сигнатурой принимающего слота (Хотя фактически, слот может иметь сигнатуру короче, чем сигнал, но слот принимает сигнал, поскольку игнорирует лишние аргументы). Поскольку сигнатуры совместимы, то компилятор может помочь определить несоответствия при использовании синтаксиса основанного на указателях. Тогда как при синтаксисе, основанном на макросах SIGNAL и SLOT возможно определить несоответствие типов только в runtime процессе. Сигналы и слоты слабо связаны: класс, который вызывает сигнал знает только слот, который принимает сигнал. Механизм сигналов и слотов в Qt обеспечивается, если Вы подключили сигнал к слоту, который будет вызываться с параметрами сигнала в нужным момент. Сигналы и слоты могут иметь несколько аргументов и типов. И они являются полностью типо-безопасными.

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

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

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

Вместе, сигналы и слоты создают мощный механизм компонентного программирования.

Сигналы

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

Когда сигнал вызван, слот подключенный к нему обычно выполняется незамедлительно, просто как нормальная функция. Это возможно потому, что механизм сигналов и слотов является независимым от каких-либо циклов в GUI. Выполнение кода следует вызывать директивой emit , которая вызовет все слоты. В тех ситуациях, когда используются очереди подключений, код будет запускать сигнал, а слоты будут выполнены несколько позже.

Если несколько слотов подключены к одному сигналу, то слоты будут вызваны один за другим, в том порядке, как они подключены, когда будет вызван сигнал.

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

Примечание: По нашему опыту сигналы и слоты являются более используемыми, если они не используются специальных типов. Если QScrollBar::valueChanged () является специальным типом, таким как гипотетический QScrollBar::Range, он может подключаться только к слоту разработанному специально для QScrollBar . Подключение различных виджетов вместе может быть невозможным.

Слоты

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

Также слоты могут выполняться как обычные функции, они подчиняются обычным правилам С++, когда вызываются непосредственно. Однако, как слоты, они могут быть вызваны другими компонентами, несмотря на их уровень доступа, через сигнал-слотовое подключение. Это означает, что сигнал испускается из одного из классов и может быть передан в приватный слот, который будет вызван из этого несвязанного класса.

Вы можете также определить слоты как виртуальные, которые мы находим довольно полезными в практике.

По сравнению с callback сигналы и слоты немного медленнее из-за той гибкости, что они предоставляют, хотя различия в реальном приложении незначительны. В основном, вызов сигнала, который подключается к нескольким слотам, приблизительно в десять раз медленнее, чем вызов не виртуальной функции. Это накладные расходы, из-за которых требуется находить объект соединения путём перебора всех слотов и сигналов и сравнения сигнатур для безопасного вызова функции-слота. В то время как десять невиртуальных функций вызываются меньшими накладными расходами, чем несколько операций new и delete. Как только вы выполняете строку, вектор или список операция за сценой, требующей операций new и delete, накладные расходы сигналов и слотов очень малы по сравнению с полной стоимостью вызова функции. Это верно, когда вы делаете системный вызов в слот или косвенно вызываете более, чем десять функций. Простота и гибкость механизма сигналов и слотов является неважными накладными расходами, которые ваши пользователи не заметят.

Заметьте, что другие библиотеки определяют переменные, называемые сигналы и слоты и могут вызывать ошибки и предупреждения, когда компилируется приложение, основанное на Qt. Решение этих проблем применение директивы #undef для препроцессора.

Подключение сигнала к слоту

До пятой версии Qt подключение сигнала к слоту записывалось посредством макросов, тогда как в пятой версии стала применяться запись, основанная на указателях.

Запись с макросами:

connect(button, SIGNAL(clicked()), this, SLOT(slotButton()));

Запись на основе указателей:

connect(button, &QPushButton::clicked, this, &MainWindow::slotButton);

Преимущество второго варианта заключается в том, что имеется возможность определить несоответствие сигнатур и неверное наименование слота или сигнала ещё на стадии компиляции проекта, а не в процессе тестирования приложения.

Пример использования сигналов и слотов

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

Структура проекта

Структура проекта

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

mainwindow.h

Итак, действо следующее: три кнопки - три слота, один сигнал на все три кнопки, который подаётся в слотах кнопок и передаёт номер кнопки в один общий слот, который выдаёт сообщение с номером кнопки.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPushButton>
#include <QMessageBox>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

signals:
    void signalFromButton(int buttonID);    // Сигнал для передачи номер нажатой кнопки

private:
    Ui::MainWindow *ui;

private slots:
    void slotButton1();     // Слоты-обработчики нажатий кнопок
    void slotButton2();
    void slotButton3();

    // Слоты вызывающий сообщение с номеро нажатой кнопки
    void slotMessage(int buttonID);
};

#endif // MAINWINDOW_H

mainwindow.cpp

А в этом файле настроена логика, описанная в предыдущих абзацах. Просто осмотрите программный код и переходите к просмотру видео, там подробно показан весь процесс, продемонстрировано приложение, а также показано, что будет, если произвести написание кода с различным ошибками.

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    /* Объявляем и инициализируем кнопки
     * */
    QPushButton *but_1 = new QPushButton(this);
    QPushButton *but_2 = new QPushButton(this);
    QPushButton *but_3 = new QPushButton(this);

    /* Устанавливаем номера кнопок
     * */
    but_1->setText("1");
    but_2->setText("2");
    but_3->setText("3");

    /* Добавляем кнопки на слой с вертикальной ориентацией
     * */
    ui->verticalLayout->addWidget(but_1);
    ui->verticalLayout->addWidget(but_2);
    ui->verticalLayout->addWidget(but_3);

    /* Подключаем к кнопкам индивидуальные слоты
     * */
    connect(but_1, SIGNAL(clicked()), this, SLOT(slotButton1()));
    connect(but_2, SIGNAL(clicked()), this, SLOT(slotButton2()));
    connect(but_3, SIGNAL(clicked()), this, SLOT(slotButton3()));

    /* Подключаем сигнал с передачей номера кнопки к слоту вывода сообщения
     * */
    connect(this, &MainWindow::signalFromButton, this, &MainWindow::slotMessage);
}

MainWindow::~MainWindow()
{
    delete ui;
}

/* Слоты для обработки нажатия кнопок
 * */
void MainWindow::slotButton1()
{
    emit signalFromButton(1);
}

void MainWindow::slotButton2()
{
    emit signalFromButton(2);
}

void MainWindow::slotButton3()
{
    emit signalFromButton(3);
}

/* Слоты вывода сообщения
 * */
void MainWindow::slotMessage(int buttonID)
{
    QMessageBox::information(this,
                             "Уведомление о нажатой кнопке",
                             "Нажата кнопка под номером " + QString::number(buttonID));
}

Видеоурок

Реклама

Комментарии

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
Последние комментарии
  • EVILEG
  • 24 апреля 2017 г. 20:44

Подключение вашего Qt приложения к сервисам Google, используя OAuth 2.0

У меня пока мыслей на этот счёт нет ((

Подключение вашего Qt приложения к сервисам Google, используя OAuth 2.0

Пробовал играться с шарком, либо я криво смотрел, либо почему-то POST запросы на oauth.yandex.ru не летят, хотя должны постом лететь, я и исходники QOAuth2AuthorizationCodeFlow ковырял на пред...

  • EVILEG
  • 24 апреля 2017 г. 13:39

Подключение вашего Qt приложения к сервисам Google, используя OAuth 2.0

Возможно, стоит Wireshark`ом запросы посмотреть. В чём отличие идёт аякс запроса от запроса из библиотеки. Возможно, что не хватает какой-нибудь заголовочной информации.

Сейчас обсуждают на форуме
  • Arrow
  • 1 мая 2017 г. 1:00

Callback функции

Первый раз пытаюсь работать с Callback функциями. Помогите понять, что и где я не так делаю. Вот код: ReverseString.h #ifndef REVERSESTRING_H#define REVERSESTRING_H#includ...

  • EVILEG
  • 30 апреля 2017 г. 10:22

QMenu

Вам не кажется, что вы увлеклись со скриншотами? Добавляйте голый программный код в сообщения через специальный диалог. Это кнопка с двумя фигурными скобками. Иногда требуется повторить код у ...

  • CJIaBiK
  • 29 апреля 2017 г. 21:07

QPushButton

Спасибо

  • CJIaBiK
  • 29 апреля 2017 г. 17:57

QWebEngineView

спасибо помогло

  • EVILEG
  • 29 апреля 2017 г. 17:47

Ошибка

Такое случается, когда добавляете новые файлы, а объектный файл, в данном случае mainwindow.obj, не пересобирается как положено. Приходится чистить сборку.