Qt - Сигналы и слоты — различия между версиями

Материал из Кафедра ИУ5 МГТУ им. Н.Э.Баумана - студенческое сообщество
Перейти к: навигация, поиск
(В конце исправил "Погроммирование".)
м (надо полагать, за годы существования портала это кто-нибудь уже исправил бы. Да и нетрудно видеть, что это действующая категория)
Строка 343: Строка 343:
 
* [http://www.youtube.com/watch?v=JtyCM4BTbYo C++ Qt 04 - Signals and Slots] (на английском языке).
 
* [http://www.youtube.com/watch?v=JtyCM4BTbYo C++ Qt 04 - Signals and Slots] (на английском языке).
  
[[Категория:Программирование]]
+
[[Категория:Погроммирование]]

Версия 12:40, 12 февраля 2016

Qt-logo.png

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

Как это работает в теории

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

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

Таким образом, чтобы соединить два объекта, нужно:

  1. создать у одного сигнал, а у второго слот;
  2. соединить сигнал первого и слот второго.

Это можно изобразить вот так:

Qt signls.and.slots pic1.png

На рисунке:

а) два изначальных объекта, ничем ни с кем не соединены;
б) у первого объекта появился сигнал, а у второго - слот. Теперь им есть чем соединяться, но они всё ещё ни с кем не соединены;
в) сигнал первого объекта соединён со слотом второго.

Каждый объект может иметь больше одного сигнала и больше одного слота. Соединяться могут также более двух объектов:

Qt signls.and.slots pic2.png

Как видно, кроме очевидных соединений, при отправке Объектом 4 сигнала выполнятся слот Объекта 3 и слот Объекта 1.

Примеры

Немного поясняющих картинок:

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

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

  • здарова();
  • сам_привет();
  • ну_привет();
  • хай();
  • пошёл_ты().

Эйб может подать сигнал привет().

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

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

Например, очевидной:

QString MudakenAngry::пошёл_ты()
{
    QString answer = "Да пошёл ты. Пива не принёс, ничего не принёс, ещё хочет чего-то. Вообще охренеть.";
    return answer;
}

или не очень:

QString MudakenAngry::пошёл_ты()
{
    QString answer = "Ну наконец-то! Где тебя носило? Пошли за пивом уже!";
    return answer;
}

Конечно, при написании кода лучше делать так, чтобы название и функционал совпадали по смыслу, потому второй вариант приведён лишь в качестве примера.

Как это сделать в Qt

QObject

Необходимое условие - при описании класса должен быть использован макрос Q_OBJECT, а сами классы должны так или иначе происходить от класса QObject:

  1. class MeClass : public QObject
  2. {
  3.     Q_OBJECT
  4.  
  5. public:
  6.     MeClass();
  7.  
  8.     ...
  9.  
  10. };

Сигналы

Создать сигнал можно так:

  1. class MeClass : public QObject
  2. {
  3.     Q_OBJECT
  4.  
  5. public:
  6.     MeClass();
  7.  
  8. signals:
  9.     void someSignal();
  10.  
  11.     ...
  12.  
  13. };

Теперь сигнал можно отправить в любом месте:

emit someSignal();

Слоты

Создать слот можно так:

  1. class MeClass : public QObject
  2. {
  3.     Q_OBJECT
  4.  
  5. public:
  6.     MeClass();
  7.  
  8. signals:
  9.     void someSignal();
  10.  
  11. public slots:
  12.     void someSlot();
  13.  
  14.     ...
  15.  
  16. };

Сигналы и слоты с параметрами

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

  1. // первый класс, отправитель
  2. class MeClass : public QObject
  3. {
  4.     Q_OBJECT
  5.  
  6. public:
  7.     MeClass();
  8.  
  9. signals:
  10.     // сигнал будет передавать переменную типа int
  11.     void someSignal(int value2send);
  12.  
  13.     ...
  14.  
  15. };
  16.  
  17. // второй класс, получатель
  18. class YaClass : public QObject
  19. {
  20.     Q_OBJECT
  21.  
  22. public:
  23.     YaClass();
  24.  
  25. public slots:
  26.     // слот будет принимать переменную типа int
  27.     void someSlot(int value2get);
  28.  
  29.     ...
  30.  
  31. };

Соединение

Функция соединения сигнала первого объекта и слота второго объекта имеет четыре параметра:

  1. отправитель сигнала;
  2. его сигнал;
  3. получатель сигнала;
  4. его слот.

Соединение сигнала meClass и слота yaClass:

  1. MeClass meClass();
  2. YaClass yaClass();
  3.  
  4. connect(meClass, SIGNAL(someSignal(int)), yaClass, SLOT(someSlot(int)));

Теперь при каждой отправке сигнала someSignal(int) объекта meClass будет выполняться слот someSlot(int) объекта yaClass.

Демонстрационный пример

Приложение состоит из главного окна Сигналы и слоты, Первого окна и Второго окна.

Qt signls.and.slots pic3.png

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

Главное окно также соединяется сигналом своего закрытия (уничтожения) со слотами закрытия Первого и Второго окон.

Проект приложения можно загрузить здесь.

Главное окно

mainwindow.h:

  1.     ...
  2.  
  3. protected:
  4.     // событие закрытия главного окна, будет посылать всем сигнал закрытия
  5.     /// это стандартная виртуальная функция класса, поэтому мы её лишь переопределяем
  6.     virtual void closeEvent(QCloseEvent *event);
  7.  
  8.     ...

mainwindow.cpp:

  1. ...
  2.  
  3. // реализация переопределённой функции
  4. void MainWindow::closeEvent(QCloseEvent *event)
  5. {
  6.     emit destroyed(); // отправить сигнал о закрытии (уничтожении) окна
  7. }
  8.  
  9. ...
  10.  
  11. // соединение сигнала от главного окна со слотом первого окна
  12. connect(this, SIGNAL(destroyed()), frst, SLOT(close()));
  13. // соединение сигнала от главного окна со слотом второго окна
  14. connect(this, SIGNAL(destroyed()), scnd, SLOT(close()));
  15. // слоты close() являются стандартными, потому объявлять в классах окон их не нужно
  16.  
  17. ...
  18.  
  19. // соединение сигнала от первого окна со слотом второго окна
  20. connect(frst, SIGNAL(sendMessage(QString)),
  21.     scnd, SLOT(receiveMessage(QString)));
  22.  
  23. // соединение сигнала от второго окна со слотом первого окна
  24. connect(scnd, SIGNAL(sendMessage(QString)),
  25.     frst, SLOT(receiveMessage(QString)));

Первое окно

frst.h:

  1.     ...
  2.  
  3. signals:
  4.     /// @brief сигнал отправки сообщения второму окну
  5.     /// @param msg2send - отправляемая в сигнале строка
  6.     void sendMessage(QString msg2send);
  7.  
  8. public slots:
  9.     /// @brief слот, в которой будет приходить сигнал от второго окна
  10.     /// @param msg2recieve - получаемая из сигнала второго окна строка
  11.     void receiveMessage(QString msg2recieve);
  12.  
  13.     ...

frst.cpp:

  1. ...
  2.  
  3. // обработчик нажатия кнопки отправки
  4. void Frst::on_btn_sendToSecond_clicked()
  5. {
  6.     // посылает сигнал, содержащий строку из поля ввода
  7.     emit sendMessage(ui->lineEdit_first->text());
  8. }
  9.  
  10. // слот получения сигнала
  11. void Frst::receiveMessage(QString msg2recieve)
  12. {
  13.     // принимает строку из сигнала и вставляет её в надпись
  14.     ui->label_received_first->setText(msg2recieve);
  15. }
  16.  
  17. ...

Второе окно

scnd.h:

  1.     ...
  2.  
  3. signals:
  4.     /// @brief сигнал отправки сообщения первому окну
  5.     /// @param msg2send - отправляемая в сигнале строка
  6.     void sendMessage(QString msg2send);
  7.  
  8. public slots:
  9.     /// @brief слот, в которой будет приходить сигнал от первого окна
  10.     /// @param msg2recieve - получаемая из сигнала первого окна строка
  11.     void receiveMessage(QString msg2recieve);
  12.  
  13.     ...

scnd.cpp:

  1. ...
  2.  
  3. // обработчик нажатия кнопки отправки
  4. void Scnd::on_btn_sendToFirst_clicked()
  5. {
  6.     // посылает сигнал, содержащий строку из поля ввода
  7.     emit sendMessage(ui->lineEdit_second->text());
  8. }
  9.  
  10. // слот получения сигнала
  11. void Scnd::receiveMessage(QString msg2recieve)
  12. {
  13.     // принимает строку из сигнала и вставляет её в надпись
  14.     ui->label_received_second->setText(msg2recieve);
  15. }
  16.  
  17. ...

Дополнительно

Более подробно про механизм сигналов и слотов можно прочитать в книгах по Qt и в следующих статьях:

Видео: