На способы применения сигналов и слотов в Qt есть несколько ограничений, но они не слишком существенные:
□ сигналы и слоты должны быть функциями-методами класса-потомка
QObject
;
□ при использовании множественного наследования
QObject
должен быть первым в списке класса;
□ оператор
Q_OBJECT
должен появляться первым в объявлении класса;
□ сигналы нельзя применять в шаблонах;
□ указатели на функцию не могут использоваться как аргументы в сигналах и слотах;
□ сигналы и слоты не могут переопределяться или обновляться до статуса
public
(общедоступный).
Поскольку вы должны писать ваши сигналы и слоты как потомков объекта
QObject
, логично создавать ваш интерфейс, расширяя и настраивая виджет, начиная с
QWidget
, базового виджета Qt, потомка виджета
QObject
. В комплекте Qt вы почти всегда будете создавать интерфейсы, расширяя такие виджеты, как
QMainWindow
.
Типичное определение класса в файле MyWindow.h для вашего GUI будет напоминать приведенное далее:
class MyWindow : public QMainWindow {
Q_OBJECT
public:
MyWindow();
virtual ~MyWindow();
signals:
void aSignal();
private slots:
void doSomething();
}
Ваш класс — наследник объекта
QMainWindow
, который определяет функциональные возможности главного окна в приложении. Аналогичным образом при создании диалогового окна вы определите подкласс
QDialog
. Первым указан оператор
Q_OBJECT
, действующий как метка для препроцессора, за которым следуют обычные объявления конструктора и деструктора. Далее даны определения сигнала и слота.
У вас есть один сигнал и один слот, оба без параметров. Для порождения сигнала
aSignal()
вам нужно всего лишь в любом месте программы вызвать функцию
emit
:
emit aSignal();
Это означает, что все остальное обрабатывается Qt. Вам даже не потребуется реализация
aSignal()
.
Для применения слотов их нужно связать с сигналом. Делается это соответствующим образом с помощью названного статического метода
connect
класса
QObject
:
<b>bool QObject::connect(const QObject * sender, const char* signal,</b>
<b> const QObject * receiver, const char * member);</b>
Просто передайте объект, владеющий сигналом (отправитель), функцию сигнала, объект, владеющий слотом (приемником), и в завершение укажите имя слота.
В примере MyWindow, если бы вы захотели связать сигнал
clicked
виджета
QPushButton
с вашим слотом
doSomething
, вы бы написали:
connect(button, SIGNAL(clicked()), this, SLOT(doSomething()));
Учтите, что необходимо применять макросы
SIGNAL
и
SLOT
для выделения функций сигналов и слотов. Как и в комплекте GTK+, вы можете связать ряд слотов с заданным сигналом и также связать слот с любым количеством сигналов с помощью множественных вызовов функции connect. Если она завершается аварийно, то возвращает
FALSE
.
Остается реализовать ваш слот в виде обычной функции-метода:
void MyWindow::doSomething() {
// Код слота
}
Выполните упражнение 17.2.
Упражнение 17.2. Сигналы и слоты
Теперь, зная основы использования сигналов и слотов, применим их в примере. Усовершенствуйте
QMainWindow
, вставьте в него кнопку и свяжите сигнал кнопки
clicked
со слотом.
1. Введите следующее объявление класса и назовите файл ButtonWindow.h:
#include <qmainwindow.h>
class ButtonWindow : public QMainWindow {
Q_OBJECT
public:
ButtonWindow(QWidget *parent = 0, const char *name = 0);
virtual ~ButtonWindow();
private slots:
void Clicked();
};
2. Далее следует реализация класса в файле ButtonWindow.cpp:
#include "ButtonWindow.moc"
#include <qpushbutton.h>
#include <qapplication.h>
#include <iostream>
3. В конструкторе вы задаете заголовок окна, создаете кнопку и связываете сигнал нажатия кнопки с вашим слотом.
setCaption
— метод объектов типа
QMainWindow
, который, что неудивительно, задает заголовок окна:
ButtonWindow::ButtonWindow(QWidget *parent, const char* name) : QMainWindow(parent, name) {
this->setCaption("This is the window Title");
QPushButton *button = new QPushButton("Click Me!", this, "Button1");
button->setGeometry(50, 30, 70, 20);
connect(button, SIGNAL(clicked()), this, SLOT(Clicked()));
}
4. Qt автоматически удаляет виджеты, поэтому ваш деструктор пуст:
ButtonWindow::~ButtonWindow() {}
5. Затем реализация слота:
void ButtonWindow::Clicked(void) {
std::cout << "clicked!\n";
}
6. И наконец, в функции
main
вы просто создаете экземпляр типа
ButtonWindow
, делаете его главным окном вашего приложения и отображаете окно на экране:
int main(int argc, char **argv) {
QApplication app(argc, argv);
ButtonWindow *window = new ButtonWindow();