БлогNot. QT: работаем с сигналами и слотами

Помощь дата->рейтинг Поиск Почта RSS канал Статистика nickolay.info Домой

QT: работаем с сигналами и слотами

Этот "классический" слегка доработанный пример на сигналы и слоты в QT показывает, как их соединять, как разрывать и возобновлять соединение. Сначала немного теории.

В QT реализована концепция функций обратного вызова (callback functions) - в результате действий пользователя вызываются обычные методы класса типа void. Чтобы сопоставить код с кнопкой, необходимо передать в функцию указатель на кнопку. Элементы графического интерфейса пользователя оказываются тесно связаны с функциональными частями программы. Для обеспечения связей сообщения и методов обработки используются макросы — карты сообщений. Примеры интерфейсов, где так сделано - Windows API, MFC.

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

Механизм сигналов и слотов основан на следующих принципах:

Особенности работы механизма сигналов и слотов следующие:

Сигналы (signals) - это методы, которые в состоянии осуществлять пересылку сообщений. Сигналы определяются в классе, как обычные методы, но без реализации. Они являются прототипами методов, содержащихся в заголовочном файле определения класса. Всю дальнейшую заботу о реализации кода для этих методов берет на себя препроцессор. Методы сигналов не должны возвращать каких-либо значений, поэтому перед именем метода всегда должно стоять void.

Сигнал не обязательно соединять со слотом. Если соединения не произошло, то он просто не будет обрабатываться. Подобное разделение отправляющих и получающих объектов исключает возможность того, что один из подсоединенных слотов каким-то образом сможет помешать объекту, отправившему сигналы. Библиотека предоставляет большое количество уже готовых сигналов для существующих элементов управления. В основном, для решения поставленных задач хватает этих сигналов, но иногда возникает необходимость реализации новых сигналов в своих классах.

class MySignal {
 Q_OBJECT
 //...
 signals:
  void doIt();
 //...
};

Препроцессор обеспечит примерно такую реализацию сигнала:

void MySignal::doIt() {
 QMetaObject::activate(this, &staticMetaObject, 0, 0);
}

Выслать сигнал можно при помощи ключевого слова emit. Ввиду того, что сигналы играют роль вызывающих методов, конструкция отправки сигнала emit doIt() приведет к обычному вызову метода doIt(). Сигналы могут отправляться из классов, которые их содержат. Например, в листинге выше сигнал doIt() может отсылаться только объектами класса MySignal, и никакими другими. Чтобы иметь возможность отослать сигнал программно из объекта этого класса, следует добавить метод sendSignal(), вызов которого заставит объект класса MySignal отправлять сигнал doIt():

class MySignal {
 Q_OBJECT
 public:
  void sendSignal() {
   emit doIt();
  }
 signals:
  void doIt();
};

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

class MySignal : public QObject {
 Q_OBJECT
 public:
  void sendSignal() {
   emit sendString("Information");
  }
 signals:
  void sendString(const QString&);
};

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

Слоты (slots) — это методы, которые присоединяются к сигналам. По сути, они являются обычными методами. Основное их отличие состоит в возможности принимать сигналы. Как и обычные методы, они определяются в классе как public, private или protected. Соответственно, перед каждой группой слотов должно стоять одно из ключевых слов private slots:, protected slots: или public slots:

В слотах нельзя использовать параметры по умолчанию, например slotMethod (int n = 8), или определять слоты как static. Классы библиотеки содержат целый ряд уже реализованных слотов. Но определение слотов для своих классов — это частая процедура.

class MySlot : public QObject {
 Q_OBJECT
 public:
 MySlot();
 public slots:
  void slot() {
   qDebug() << "I’m a slot";
  }
};

Внутри слота вызовом метода sender() можно узнать, от какого объекта был выслан сигнал. Он возвращает указатель на объект типа QObject. Например, в этом случае на консоль будет выведено имя объекта, выславшего сигнал:

void slot() {
 qDebug() << sender()->objectName();
}

Соединение объектов осуществляется при помощи статического метода connect(), который определен в классе QObject. В общем виде, вызов метода connect() выглядит следующим образом:

QObject::connect(const QObject* sender,
const char* signal,
const QObject* receiver,
const char* slot,
Qt::ConnectionType type = Qt::AutoConnection
);

Ему передаются пять следующих параметров:

  1. sender — указатель на объект, отправляющий сигнал;
  2. signal — это сигнал, с которым осуществляется соединение. Прототип (имя и аргументы) метода сигнала должен быть заключен в специальный макрос SIGNAL(method());
  3. receiver — указатель на объект, который имеет слот для обработки сигнала;
  4. slot — слот, который вызывается при получении сигнала. Прототип слота должен быть заключен в специальном макросе SLOT(method());
  5. type — управляет режимом обработки. Имеется три возможных значения:
    • Qt::DirectConnection — сигнал обрабатывается сразу вызовом соответствующего метода слота
    • Qt::QueuedConnection — сигнал преобразуется в событие и ставится в общую очередь для обработки
    • Qt::AutoConnection — это автоматический режим, который действует следующим образом: если отсылающий сигнал объект находится в одном потоке с принимающим его объектом, то устанавливается режим Qt::DirectConnection, в противном случае — режим Qt::QueuedConnection. Этот режим (Qt::AutoConnection) определен в методе connection() по умолчанию.

Как может быть осуществлено соединение объектов в программе:

void main() {
 QObject::connect(pSender, SIGNAL(signalMethod()),pReceiver, SLOT(slotMethod()));
}

Если вызов происходит из класса, унаследованного от QObject, тогда указание QObject:: можно опустить:

MyClass::MyClass() : QObject() {
 connect(pSender, SIGNAL(signalMethod()),pReceiver, SLOT(slotMethod()));
}

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

MyClass::MyClass() : QObject() {
 connect(pSender, SIGNAL(signalMethod()), SLOT(slot()));
}

void MyClass::slot() {
 qDebug() << "I’m a slot";
}

Иногда возникают ситуации, когда объект не обрабатывает сигнал, а просто передает его дальше. Для этого необязательно определять слот, который в ответ на получение сигнала (при помощи emit) отсылает свой собственный. Можно просто соединить сигналы друг с другом. Отправляемый сигнал должен содержаться в определении класса:

MyClass::MyClass() : QObject() {
 connect(pSender, SIGNAL(signalMethod()), SIGNAL(mySignal()));
}

Отправку сигналов заблокировать можно на некоторое время, вызвав метод blockSignals() с параметром true. Объект будет "молчать", пока блокировка не будет снята тем же методом blockSignals() с параметром false. При помощи метода signalsBlocked() можно узнать текущее состояние блокировки сигналов.

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

В качестве законченного примера приведём проект Counter. Он умеет увеличивать счётчик на QLabel по нажатию кнопки "Add", а также разрывать и восстанавливать обработку сигналов по нажатию кнопки "Connect"/"Disconnect". Когда соединение отсутствует, счётчик не увеличивается. После 10 увеличений счётчика приложение в любом случае завершается.

Файл counter.h
#ifndef COUNTER_H
#define COUNTER_H

#include <QObject>
#include <QLabel>
#include <QPushButton>

class Counter : public QObject
{
    Q_OBJECT
private:
 int Value;
 bool Connected;
public:
 Counter(QObject *parent=0);
 QLabel lbl;
 QPushButton cmd,cmd2;
public slots:
 void slotInc();
 void disconnector();
signals:
 void goodbye ();
 void counterChanged(int);
};

#endif // COUNTER_H
Файл counter.cpp
#include "counter.h"

Counter::Counter (QObject *parent) :  QObject(parent), Value(0) {
 QObject::connect(&cmd2, SIGNAL(clicked()),this, SLOT(disconnector()) );
 this->Connected = false;
 this->disconnector();
}

void Counter::slotInc() {
 if (this->Connected==true) {
  emit counterChanged(++this->Value);
  if (this->Value == 10) { emit goodbye(); } //ограничиваемся 10 нажатиями
 }
}

void Counter::disconnector() {
 if (this->Connected == true) {
  QObject::disconnect(&cmd, SIGNAL(clicked()),this, SLOT(slotInc()) );
  QObject::disconnect(this, SIGNAL(counterChanged(int)), &lbl, SLOT(setNum(int)));
                                       //метод setNum(int) есть в QLabel
  this->Connected = false;
  cmd2.setText("CONNECT");
 }
 else {
  QObject::connect(&cmd, SIGNAL(clicked()),this, SLOT(slotInc()) );
  QObject::connect(this, SIGNAL(counterChanged(int)), &lbl, SLOT(setNum(int)), Qt::DirectConnection );
  this->Connected = true;
  cmd2.setText("DISCONNECT");
 }
}
Файл main.cpp
#include <QApplication>
#include "counter.h"

int main(int argc, char *argv[]) {
 QApplication a(argc, argv);
 Counter counter;
 counter.lbl.setText("0");
 counter.lbl.move(100,100);
 counter.cmd.setText("ADD");
 counter.cmd.move(100,200);
 counter.cmd2.setText("DISCONNECT");
 counter.cmd2.move(100,300);

 counter.lbl.show();
 counter.cmd.show();
 counter.cmd2.show();
 QObject::connect(&counter, SIGNAL(goodbye()), &a, SLOT(quit()) );
 return a.exec();
}

Соединять сигналы со слотами, разумеется, не обязательно программно. В режиме дизайна формы нажмите клавишу F4 для доступа к интерфейсу управления сигналами и слотами. Там же можно добавить в список новые, заданные программистом слоты.

соединение сигналов и слотов в режиме дизайна формы
соединение сигналов и слотов в режиме дизайна формы

 Скачать этот пример в архиве .ZIP с проектом QT5 (2 Кб)


теги: c++ программирование qt

комментарии (0)

28.04.2015, 17:25; рейтинг: 17953

  свежие записипоиск по блогукомментироватьстатистика

Наверх Яндекс.Метрика
© PerS
вход