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

Материал из Кафедра ИУ5 МГТУ им. Н.Э.Баумана, студенческое сообщество
Перейти к навигации Перейти к поиску
м (Откат правок 188.32.138.203 (обсуждение) к версии 81.23.104.106)
 
(не показано 18 промежуточных версий 7 участников)
Строка 1: Строка 1:
[[Файл:Qt-logo.png|right]]
[[Файл:Qt-logo.png|right|150px]]


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


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


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


[[Файл:Qt signls.and.slots pic1.png|800px|link=Файл:Qt signls.and.slots pic1.svg]]
[[Файл:Qt signls.and.slots pic1.png|800px]]


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


[[Файл:Qt signls.and.slots pic2.png|500px|link=Файл:Qt signls.and.slots pic2.svg]]
[[Файл:Qt signls.and.slots pic2.png|500px]]


Как видно, кроме очевидных соединений, при отправке Объектом 4 сигнала выполнятся слот Объекта 3 и слот Объекта 1.
Как видно, кроме очевидных соединений, при отправке Объектом 4 сигнала выполнятся слот Объекта 3 и слот Объекта 1.
=== Примеры ===
Немного поясняющих картинок:
<gallery widths="400px" heights="250">
Файл:Signals.and.slots.start.jpg|Один сигнал соединён с одинаковыми слотами разных объектов (наследников одного класса)
Файл:Signals.and.slots.abe.jpg|Один сигнал соединён с разными слотами разных объектов
</gallery>
Картинка с мудакенами может внести некоторую путаницу, потому её следует сопроводить разъяснением.
Каждый мудакен (из пяти слева) имеет набор слотов, будем считать, что у всех одинаковый (потому что все они унаследованы от одного класса <code>Мудакен</code>). То есть, у каждого из них есть следующие:
* <code>здарова()</code>;
* <code>сам_привет()</code>;
* <code>ну_привет()</code>;
* <code>хай()</code>;
* <code>пошёл_ты()</code>.
Эйб может подать сигнал <code>привет()</code>.
Можно было бы соединить его сигнал с каким-то одним слотом, одинаковым для всех пяти остальных мудакенов, к которым он обращается, но такая ситуация уже показана на картинке со стометровкой. Потому мы соединили сигнал Эйба с различными слотами ответов мудакенов.
Путаница могла возникнуть такая, что можно было подумать, что <code>пошёл_ты()</code> (ну и остальные) - это уже реакция на приветствие Эйба. Но это не так, это лишь ''имя реакции'' (имя слота), а реализация у неё может быть какой угодно.
Например, очевидный:
<syntaxhighlight lang="cpp">
QString MudakenAngry::пошёл_ты()
{
    QString answer = "Да пошёл ты. Пива не принёс, ничего не принёс, ещё хочет чего-то. Вообще охренеть.";
    return answer;
}
</syntaxhighlight>
или не очень:
<syntaxhighlight lang="cpp">
QString MudakenAngry::пошёл_ты()
{
    QString answer = "Ну наконец-то! Где тебя носило? Пошли за пивом уже!";
    return answer;
}
</syntaxhighlight>
Конечно, при написании кода лучше делать так, чтобы название и функционал совпадали по смыслу, потому второй вариант приведён лишь в качестве примера.


== Как это сделать в Qt ==
== Как это сделать в Qt ==
Строка 34: Строка 77:
Необходимое условие - при описании класса должен быть использован макрос <code>Q_OBJECT</code>, а сами классы должны так или иначе происходить от класса <code>QObject</code>:
Необходимое условие - при описании класса должен быть использован макрос <code>Q_OBJECT</code>, а сами классы должны так или иначе происходить от класса <code>QObject</code>:


<syntaxhighlight lang="cpp-qt" line highlight="3">
<syntaxhighlight lang="cpp" line highlight="3">
class MeClass : public QObject
class MeClass : public QObject
{
{
Строка 51: Строка 94:
Создать сигнал можно так:
Создать сигнал можно так:


<syntaxhighlight lang="cpp-qt" line highlight="9">
<syntaxhighlight lang="cpp" line highlight="9">
class MeClass : public QObject
class MeClass : public QObject
{
{
Строка 69: Строка 112:
Теперь сигнал можно отправить в любом месте:
Теперь сигнал можно отправить в любом месте:


<syntaxhighlight lang="cpp-qt">
<syntaxhighlight lang="cpp">
emit someSignal();
emit someSignal();
</syntaxhighlight>
</syntaxhighlight>
Строка 77: Строка 120:
Создать слот можно так:
Создать слот можно так:


<syntaxhighlight lang="cpp-qt" line highlight="12">
<syntaxhighlight lang="cpp" line highlight="12">
class MeClass : public QObject
class MeClass : public QObject
{
{
Строка 100: Строка 143:
Кроме простого соединения, сигналы и слоты позволяют передавать между объектами переменные. Для этого соединяемые сигнал и слот должны иметь параметр одного типа:
Кроме простого соединения, сигналы и слоты позволяют передавать между объектами переменные. Для этого соединяемые сигнал и слот должны иметь параметр одного типа:


<syntaxhighlight lang="cpp-qt" line highlight="10, 25">
<syntaxhighlight lang="cpp" line highlight="11, 27">
// первый класс, отправитель
// первый класс, отправитель
class MeClass : public QObject
class MeClass : public QObject
Строка 144: Строка 187:
Соединение сигнала <code>meClass</code> и слота <code>yaClass</code>:
Соединение сигнала <code>meClass</code> и слота <code>yaClass</code>:


<syntaxhighlight lang="cpp-qt" line highlight="4">
<syntaxhighlight lang="cpp" line highlight="4">
MeClass meClass();
MeClass meClass();
YaClass yaClass();
YaClass yaClass();
Строка 168: Строка 211:


<code>mainwindow.h:</code>
<code>mainwindow.h:</code>
<syntaxhighlight lang="cpp-qt" line>
<syntaxhighlight lang="cpp" line>
     ...
     ...


Строка 181: Строка 224:


<code>mainwindow.cpp:</code>
<code>mainwindow.cpp:</code>
<syntaxhighlight lang="cpp-qt" line>
<syntaxhighlight lang="cpp" line>
...
...


Строка 212: Строка 255:


<code>frst.h:</code>
<code>frst.h:</code>
<syntaxhighlight lang="cpp-qt" line>
<syntaxhighlight lang="cpp" line>
     ...
     ...


Строка 230: Строка 273:


<code>frst.cpp:</code>
<code>frst.cpp:</code>
<syntaxhighlight lang="cpp-qt" line>
<syntaxhighlight lang="cpp" line>
...
...


Строка 253: Строка 296:


<code>scnd.h:</code>
<code>scnd.h:</code>
<syntaxhighlight lang="cpp-qt" line>
<syntaxhighlight lang="cpp" line>
     ...
     ...


Строка 271: Строка 314:


<code>scnd.cpp:</code>
<code>scnd.cpp:</code>
<syntaxhighlight lang="cpp-qt" line>
<syntaxhighlight lang="cpp" line>
...
...


Строка 297: Строка 340:
* [http://www.developer.nokia.com/Community/Wiki/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%B7%D0%BC_%D1%81%D0%B8%D0%B3%D0%BD%D0%B0%D0%BB%D0%BE%D0%B2_%D0%B8_%D1%81%D0%BB%D0%BE%D1%82%D0%BE%D0%B2_%D0%B2_Qt NOKIA Developer - Механизм сигналов и слотов в Qt].
* [http://www.developer.nokia.com/Community/Wiki/%D0%9C%D0%B5%D1%85%D0%B0%D0%BD%D0%B8%D0%B7%D0%BC_%D1%81%D0%B8%D0%B3%D0%BD%D0%B0%D0%BB%D0%BE%D0%B2_%D0%B8_%D1%81%D0%BB%D0%BE%D1%82%D0%BE%D0%B2_%D0%B2_Qt NOKIA Developer - Механизм сигналов и слотов в Qt].


[[Категория:Qt]]
Видео:
* [http://www.youtube.com/watch?v=JtyCM4BTbYo C++ Qt 04 - Signals and Slots] (на английском языке).
 
[[Категория:Погроммирование]]

Текущая версия от 11:55, 12 марта 2018

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

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

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

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

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

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

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

На рисунке:

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

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

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

Примеры

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

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

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

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

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

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

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

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

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

или не очень:

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

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

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

QObject

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

class MeClass : public QObject
{
    Q_OBJECT

public:
    MeClass();

    ...

};

Сигналы

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

class MeClass : public QObject
{
    Q_OBJECT

public:
    MeClass();

signals:
    void someSignal();

    ...

};

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

emit someSignal();

Слоты

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

class MeClass : public QObject
{
    Q_OBJECT

public:
    MeClass();

signals:
    void someSignal();

public slots:
    void someSlot();

    ...

};

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

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

// первый класс, отправитель
class MeClass : public QObject
{
    Q_OBJECT

public:
    MeClass();

signals:
    // сигнал будет передавать переменную типа int
    void someSignal(int value2send);

    ...

};

// второй класс, получатель
class YaClass : public QObject
{
    Q_OBJECT

public:
    YaClass();

public slots:
    // слот будет принимать переменную типа int
    void someSlot(int value2get);

    ...

};

Соединение

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

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

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

MeClass meClass();
YaClass yaClass();

connect(meClass, SIGNAL(someSignal(int)), yaClass, SLOT(someSlot(int)));

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

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

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

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

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

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

Главное окно

mainwindow.h:

    ...

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

mainwindow.cpp:

...

// реализация переопределённой функции
void MainWindow::closeEvent(QCloseEvent *event)
{
    emit destroyed(); // отправить сигнал о закрытии (уничтожении) окна
}
     
...

// соединение сигнала от главного окна со слотом первого окна
connect(this, SIGNAL(destroyed()), frst, SLOT(close()));
// соединение сигнала от главного окна со слотом второго окна
connect(this, SIGNAL(destroyed()), scnd, SLOT(close()));
// слоты close() являются стандартными, потому объявлять в классах окон их не нужно

...

// соединение сигнала от первого окна со слотом второго окна
connect(frst, SIGNAL(sendMessage(QString)),
    scnd, SLOT(receiveMessage(QString)));

// соединение сигнала от второго окна со слотом первого окна
connect(scnd, SIGNAL(sendMessage(QString)),
    frst, SLOT(receiveMessage(QString)));

Первое окно

frst.h:

    ...

signals:
    /// @brief сигнал отправки сообщения второму окну
    /// @param msg2send - отправляемая в сигнале строка
    void sendMessage(QString msg2send);

public slots:
    /// @brief слот, в которой будет приходить сигнал от второго окна
    /// @param msg2recieve - получаемая из сигнала второго окна строка
    void receiveMessage(QString msg2recieve);
    
    ...

frst.cpp:

...

// обработчик нажатия кнопки отправки
void Frst::on_btn_sendToSecond_clicked()
{
    // посылает сигнал, содержащий строку из поля ввода
    emit sendMessage(ui->lineEdit_first->text());
}

// слот получения сигнала
void Frst::receiveMessage(QString msg2recieve)
{
    // принимает строку из сигнала и вставляет её в надпись
    ui->label_received_first->setText(msg2recieve);
}

...

Второе окно

scnd.h:

    ...

signals:
    /// @brief сигнал отправки сообщения первому окну
    /// @param msg2send - отправляемая в сигнале строка
    void sendMessage(QString msg2send);

public slots:
    /// @brief слот, в которой будет приходить сигнал от первого окна
    /// @param msg2recieve - получаемая из сигнала первого окна строка
    void receiveMessage(QString msg2recieve);
    
    ...

scnd.cpp:

...

// обработчик нажатия кнопки отправки
void Scnd::on_btn_sendToFirst_clicked()
{
    // посылает сигнал, содержащий строку из поля ввода
    emit sendMessage(ui->lineEdit_second->text());
}

// слот получения сигнала
void Scnd::receiveMessage(QString msg2recieve)
{
    // принимает строку из сигнала и вставляет её в надпись
    ui->label_received_second->setText(msg2recieve);
}

...

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

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

Видео: