Нейл Мэтью - Основы программирования в Linux
Выполнив приложение, вы должны получить окно Qt (рис. 17.2).
$ ./qtl
Рис. 17.2
Как это работает
В отличие от GTK+ здесь нет вмещающего в себя все заголовочного файла qt.h, поэтому вы должны явно включать заголовочные файлы всех используемых объектов.
Первый объект, с которым вы встречаетесь, — QApplication. Это главный объект Qt, который вы должны сформировать, передав ему в самом начале аргументы командной строки. У каждого приложения Qt должен быть один и только один объект типа QApplication, который вы должны создать перед тем, как делать что-то еще. QApplication имеет дело с внутренними встроенными операциями Qt, такими как обработка событий, размещение строк и управление внешним представлением.
Вы будете применять два метода QApplication: setMainWidget, который создает главный виджет вашего приложения, и exec, который запускает выполнение цикла отслеживания событий. Метод exec не возвращает управление до тех пор, пока либо не будет вызван метод QApplication::quit(), либо не будет закрыт главный виджет.
QMainWindow — базовый виджет окна в Qt, который поддерживает меню, панель инструментов и строку состояния. Он будет играть важную роль в этой главе, по мере того, как вы научитесь расширять его возможности и вставлять в него виджеты, формирующие интерфейс.
Далее мы обсудим механизм программирования, управляемого событиями, и вы вставите в приложение виджет PushButton.
Сигналы и слоты
Как вы видели в главе 16, сигналы и их обработка — главные механизмы, используемые приложениями GUI для реагирования на ввод пользователя, и ключевые функции библиотек GUI. Механизм обработки сигналов комплекта Qt состоит из сигналов и слотов или приемников, называемых сигналами и функциями обратного вызова в комплекте инструментов GTK+ или событиями и обработчиками событий в языке программирования Java.
ПримечаниеИмейте в виду, что сигналы Qt отличаются от сигналов UNIX, обсуждавшихся в главе 11.
Вот как устроено программирование, управляемое событиями: графический интерфейс пользователя состоит из меню, панелей инструментов, кнопок, полей ввода и множества других элементов GUI, называемых виджетами. Когда пользователь взаимодействует с виджетом, например, активизирует пункт меню или вводит какой-то текст в поле ввода, виджет порождает именованный сигнал, такой как clicked, text_changed или key_pressed. Как правило, вам захочется сделать что-то в ответ на действие пользователя, например, сохранить документ или выйти из приложения, и вы выполняете это, связав сигнал с функцией обратного вызова или слотом на языке Qt.
Применение сигналов и слотов довольно специфично — Qt определяет два новых соответствующим образом описанных псевдоключевых слова, signals и slots для обозначения в вашем программном коде классов сигналов и слотов. Это замечательно с точки зрения читаемости и сопровождения программного кода, но вы вынуждены пропускать свой код через отдельный этап препроцессорной обработки для поиска и замены этих псевдоключевых слов дополнительным кодом на языке С++.
ПримечаниеТаким образом, программный код с использованием Qt — не настоящий программный код на С++. Порой это становится проблемой для некоторых разработчиков. См. документацию Qt на Web-сайте http://doc.trolltech.com/, чтобы понять причину применения этих новых псевдоключевых слов в С++. Более того, применение сигналов и слотов не так уж отличается от Microsoft Foundation Classes (MFC, библиотека базовых классов Microsoft) в ОС Windows, в которой также используется модифицированное определение языка С++.
На способы применения сигналов и слотов в 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:
bool QObject::connect(const QObject * sender, const char* signal,
const QObject * receiver, const char * member);
Просто передайте объект, владеющий сигналом (отправитель), функцию сигнала, объект, владеющий слотом (приемником), и в завершение укажите имя слота.
В примере 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();
app.setMainWidget(window);
window->show();
return app.exec();
}
7. Прежде чем вы сможете откомпилировать данный пример, необходимо запустить препроцессор для заголовочного файла. Программа этого препроцессора называется Meta Object Compiler (moc, компилятор метаобъекта) и должна быть включена в пакет комплекта Qt. Выполните moc для файла ButtonWindow.h, сохранив результат в файле ButtonWindow.moc: