Во-первых, мы включаем <QtGui> - заголовочный файл, который содержит определения классов графического интерфейса Qt. Qt состоит из нескольких модулей, каждый из которых находится в своей собственной библиотеке. Наиболее важными модулями являются QtCore, QtGui, QtNetwork, QtOpenGL, QtSql, QtSvg и QtXml. Заголовочный файл <QtGui> содержит определение всех классов, входящих в модули QtCore и QtGui. Включив этот заголовочный файл, мы можем не беспокоиться о включении каждого отдельного класса.
В filedialog.h вместо включения <QDialog> и использования предварительных объявлений для классов QCheckBox, QLabel, QLineEdit и QPushButton мы могли бы просто включить <QtGui>. Однако включение такого большого заголовочного файла, взятого из другого заголовочного файла, обычно свидетельствует о плохом стиле кодирования, особенно при разработке больших приложений.
03 FindDialog::FindDialog(QWidget *parent)
04 : QDialog(parent)
05 {
06 label = new QLabel(tr("Find &what:"));
07 lineEdit = new QLineEdit;
08 label->setBuddy(lineEdit);
09 caseCheckBox = new QCheckBox(tr("Match &case"));
10 backwardCheckBox = new QCheckBox(tr("Search backward"));
11 findButton = new QPushButton(tr("&Find"));
12 findButton->setDefault(true);
13 findButton->setEnabled(false);
14 closeButton = new QPushButton(tr("Close"));
В строке 4 конструктору базового класса передается указатель на родительский виджет (параметр parent). Затем мы создаем дочерние виджеты. Функция tr() переводит строковые литералы на другие языки. Она объявляется в классе QObject и в каждом подклассе, содержащем макрос Q_OBJECT. Любое строковое значение, которое пользователь будет видеть на экране, полезно преобразовывать функцией tr(), даже если вы не планируете в настоящий момент переводить ваше приложение на какой-нибудь другой язык. Перевод приложений Qt на другие языки рассматривается в главе 17.
Мы используем знак амперсанда ('&') для задания клавиш быстрого доступа. Например, в строке 11 создается кнопка Find, которая может быть активирована нажатием пользователем сочетания клавиш Alt+F на платформах, поддерживающих клавиши быстрого доступа. Амперсанды могут также применяться для управления фокусом: в строке 6 мы создаем текстовую метку с клавишей быстрого доступа (Alt+W), а в строке 8 мы устанавливаем строку редактирования в качестве "партнера" этой текстовой метки. Партнером (buddy) называется виджет, на который передается фокус при нажатии клавиши быстрого доступа текстовой метки. Поэтому при нажатии пользователем сочетания клавиш Alt+W (клавиша быстрого доступа текстовой метки) фокус переходит на строку редактирования (которая является партнером текстовой метки).
В строке 12 мы делаем кнопку Find используемой по умолчанию, вызывая функцию setDefault(true). Кнопка, для которой задан режим использования по умолчанию, будет срабатывать при нажатии пользователем клавиши Enter (ввод). В строке 13 мы устанавливаем кнопку Find в неактивный режим. В неактивном режиме виджет обычно имеет серый цвет и не реагирует на действия пользователя.
15 connect(lineEdit, SIGNAL(textChanged(const QString &)),
16 this, SLOT(enableFindButton(const QString &)));
17 connect(findButton, SIGNAL(clicked()),
18 this, SLOT(findClicked()));
19 connect(closeButton, SIGNAL(clicked()),
20 this, SLOT(close()));
Закрытый слот enableFindButton(const QString &) вызывается при всяком изменении значения в строке редактирования. Закрытый слот findClicked() вызывается при нажатии пользователем кнопки Find. Само диалоговое окно закрывается при нажатии пользователем кнопки Close (закрыть). Слот close() наследуется от класса QWidget, и по умолчанию он делает виджет невидимым (но не удаляет его). Программный код слотов enableFindButton() и findClicked() мы рассмотрим позднее.
Поскольку QObject является одним из прародителей FindDialog, мы можем не указывать префикс QObject:: перед вызовами connect().
21 QHBoxLayout *topLeftLayout = new QHBoxLayout;
22 topLeftLayout->addWidget(label);
23 topLeftLayout->addWidget(lineEdit);
24 QVBoxLayout *leftLayout = new QVBoxLayout;
25 leftLayout->addLayout(topLeftLayout);
26 leftLayout->addWidget(caseCheckBox);
27 leftLayout->addWidget(backwardCheckBox);
28 QVBoxLayout *rightLayout = new QVBoxLayout;
29 rightLayout->addWidget(findButton);
30 rightLayout->addWidget(closeButton);
31 rightLayout->addStretch();
32 QHBoxLayout *mainLayout = new QHBoxLayout;
33 mainLayout->addLayout(leftLayout);
34 mainLayout->addLayout(rightLayout);
35 setLayout(mainLayout);
Затем для размещения виджетов в окне мы используем менеджеры компоновки (layout managers). Менеджеры компоновки могут содержать как виджеты, так и другие менеджеры компоновки. Используя различные вложенные комбинации менеджеров компоновки QHBoxLayout, QVBoxLayout и QGridLayout, можно построить очень сложные диалоговые окна.

Рис. 2.2. Менеджеры компоновки диалогового окна поиска данных.
Для диалогового окна поиска мы используем два менеджера горизонтальной компоновки QHBoxLayout и два менеджера вертикальной компоновки QVBoxLayout (см. рис. 2.2). Внешний менеджер компоновки является главным; он устанавливается в FindDialog в строке 35 и ответственен за всю область, занимаемую диалоговым окном. Остальные три менеджера компоновки являются внутренними. Показанная в нижнем правом углу на рис. 2.2 маленькая "пружинка" является пустым промежутком ("распоркой"). Она применяется для образования ниже кнопок Find и Close пустого пространства, обеспечивающего перемещение кнопок в верхнюю часть своего менеджера компоновки.
Одна из особенностей классов менеджеров компоновки заключается в том, что они не являются виджетами. Взамен этого они наследуют свойства класса QLayout, который, в свою очередь, является наследником класса QObject. На данном рисунке виджеты выделены сплошными линиями, а менеджеры компоновки очерчены пунктирными линиями, чтобы подчеркнуть их различие. При работе приложения менеджеры компоновки невидимы.
При добавлении внутренних менеджеров компоновки к родительскому менеджеру компоновки (строки 25, 33 и 34) для них автоматически устанавливается родительская связь. Затем, когда главный менеджер компоновки устанавливается для диалога (строка 35), он становится дочерним элементом диалога и все виджеты в менеджерах компоновки становятся дочерними элементами диалога. Иерархия полученных родословных связей представлена на рис. 2.3.