2.3. Быстрая разработка диалогов.

Qt разрабатывалась так, чтобы можно было писать код программы вручную без особого напряжения. Тем не менее, Qt Designer еще больше расширяет возможности программиста, предоставляя ему возможность визуального дизайна.

В этом разделе мы, с помощью Qt Designer, напишем диалог Go-to-Cell ("Перейти к ячейке"), показанный на рисунке 2.4. Совершенно неважно, как разрабатывается диалог -- вручную ли, или с помощью Qt Designer, всегда выполняется одна и та же последовательность действий:



Рисунок 2.4. Диалог перехода к ячейке.


Чтобы запустить Qt Designer, выберите пункт Qt 3.2.x|Qt Designer в меню "Пуск" -- в ОС Windows или дайте команду designer -- в Unix. После того как программа запустится, она предложит на выбор список шаблонов. Щелкните по шаблону "Dialog" и нажмите кнопку OK. После этого перед вами должна появиться заготовка будущего окна диалога с именем "Form1".

Рисунок 2.5. Qt Designer с заготовкой окна диалога.


Для начала разместим виджеты на форме. На инструментальной панели, слева, щелкните по компоненту TextLabel, затем щелкните по форме -- в результате на форме появится компонент "метка". Аналогичным образом разместите на форме одно поле ввода (LineEdit), одну горизонтальную распорку (Spacer) и две кнопки (PushButton). Разместите их так, чтобы у вас получилось нечто похожее на рисунок 2.6. Не тратьте слишком много времени на позиционирование виджетов. Мы все равно будем использовать менеджеры размещения, которые выполнят эту работу за нас.

Распорка (spacer) отображается на заготовке в виде синей пружинки. Во время работы уже готовой программы распорки не будут отображаться.

Рисунок 2.6. Внешний вид заготовки формы с виджетами.


Установите свойства для каждого из виджетов, используя Редактор свойств, расположенный в правой части главного окна Qt Designer.

  1. Щелкните по компоненту TextLabel и запишите в его свойство name строку "label", а в свойство text -- "&Cell Location:".

  2. Щелкните по компоненту LineEdit и запишите в свойство name строку "lineEdit".

  3. Для распорки запишите в свойство orientation "Horizontal".

  4. Для первой кнопки запишите в свойство name строку "okButton", в свойство enabled -- "False", в свойство default -- "True" и в свойство text -- "OK".

  5. Для второй кнопки. Запишите в свойство name строку "cancelButton", а в свойство text -- в "OK".

  6. Щелкните в любом свободном месте формы и запишите в свойство name строку "GoToCellDialog", а в свойство caption -- "Go to Cell".

Но это еще не все, нам нужно назначить дружественный компонент для метки, который будет реагировать на акселератор Alt+C. На данный момент метка отображается как "&Cell Location:". Выберите пункт меню Tools|Set Buddy (курсор мыши приобретет вид крестика). Затем поместите указатель мыши на метку, нажмите левую кнопку и, удерживая ее в нажатом положении, переместите указатель мыши на компонент LineEdit. Затем отпустите кнопку мыши. Изображение метки изменится -- символ амперсанда исчезнет, а первый символ метки приобретет знак подчеркивания. В принципе, то же самое можно сделать внутри редактора свойств, установкой свойства buddy метки.

Рисунок 2.7. Внешний вид заготовки формы после установки свойств виджетов.


Следующий шаг -- размещение виджетов на форме с помощью менеджеров компоновки:

  1. Щелкните мышью по метке. Нажмите клавишу Shift и удерживая ее -- щелкните по полю ввода. Оба компонента окажутся выделенными. Теперь выберите пункт меню Layout|Lay Out Horizontally.

  2. Щелкните мышью по распорке. Нажмите клавишу Shift и удерживая ее -- щелкните сначала по кнопке "OK", а затем по кнопке "Cancel". Теперь выберите пункт меню Layout|Lay Out Horizontally.

  3. Щелкните по свободному пространству на форме и выберите пункт меню Layout|Lay Out Vertically.

  4. Выберите пункт меню Layout|Adjust Size.

Красные контуры областей выравнивания, которые сейчас видны на заготовке, во время работы программы отображаться не будут.

Рисунок 2.8. Внешний вид заготовки формы после настройки размещения компонентов.


Теперь выберите пункт меню Tools|Tab Order. На каждом из виджетов, которые могут принимать фокус, появятся цифры в синих кружочках. Щелчками мыши по компонентам установите желаемый порядок навигации клавишей Tab и нажмите Esc.

Рисунок 2.9. Установка порядка навигации клавишей Tab.


На этом дизайн внешнего вида формы можно считать завершенным. Теперь перейдем к настройке функциональной части -- свяжем сигналы и слоты и создадим свой слот. Выберите пункт меню Edit|Connections, перед вами откроется окно редактора связей:

Рисунок 2.10. Окно редактора связей после установки всех соединений.


Рисунок 2.11. Окно редактора слотов.


Для начала создадим новый слот: щелкните по кнопке Edit Slots.... Перед вами откроется окно редактора слотов (см. рис. 2.11). Создайте приватный слот с именем enableOkButton().

Затем необходимо настроить три соединения. Чтобы создать соединение -- щелкните по кнопке "New" и установите поля Sender, Signal, Receiver и Slot, выбирая требуемые значения из выпадающих списков в каждом из них. У вас должно получиться следующее:

okButton clicked() GoToCellDialog accept() cancelButton clicked() GoToCellDialog reject() lineEdit textChanged(const QString &) GoToCellDialog enableOkButton()

Чтобы посмотреть, как будет выглядеть окно диалога во время работы программы -- выберите пункт меню Preview|Preview Form. Проверьте порядок навигации клавишей Tab. Проверьте работу акселератора Alt+C (поле ввода должно получить фокус ввода). Нажмите кнопку Cancel, чтобы закрыть окно.

Сохраните результаты работы в файле gotocelldialog.ui в каталоге gotocell и создайте файл main.cpp, в том же каталоге, с помощью обычного текстового редактора:

#include <qapplication.h> #include "gotocelldialog.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); GoToCellDialog *dialog = new GoToCellDialog; app.setMainWidget(dialog); dialog->show(); return app.exec(); } Создайте файл проекта и Makefile утилитой qmake (qmake -project; qmake gotocell.pro). Утилита qmake сама обнаружит файл gotocelldialog.ui и добавит в Makefile все необходимые правила по созданию gotocelldialog.h и gotocelldialog.cpp. Все .ui файлы преобразуются в код C++ с помощью утилиты uic (User Interface Compiler -- Компилятор Пользовательских Интерфейсов).

Вся прелесть Qt Designer-а состоит в том, что вы можете свободно изменять дизайн формы без необходимости вторгаться в исходный код на C++. Когда разработка дизайна ведется в тексте программы (вручную) то это может отнять довольно значительное время. Qt Designer сохранит ваши силы и время.

Если теперь запустить программу, то вы заметите:

Мы должны решить эти проблемы.

Щелкните дважды по свободному пространству на форме, чтобы вызвать редактор исходного кода. В окне редактора добавьте следующие строки:

#include <qvalidator.h> void GoToCellDialog::init() { QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}"); lineEdit->setValidator(new QRegExpValidator(regExp, this)); } void GoToCellDialog::enableOkButton() { okButton->setEnabled(lineEdit->hasAcceptableInput()); } Функция init() автоматически вызывается конструктором формы (конструктор генерируется утилитой uic). Она настраивает проверку корректности ввода для LineEdit. Qt предоставляет три класса, выполняющих проверку на корректность: QIntValidator, QDoubleValidator и QRegExpValidator. Для своих нужд мы будем использовать последний, который будет выполнять проверку на основе регулярного выражения: "[A-Za-z][1-9][0-9]{0,2}". Это выражение означает: "Позволить ввод одного алфавитного символа в верхнем или нижнем регистре, за которым должна следовать одна цифра, в диапазоне от 1 до 9, за которой может следовать до двух цифр, в диапазоне от 0 до 9".

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

Слот enableOkButton() разрешает или запрещает кнопку "OK", в зависимости от того, насколько правильный номер ячейки содержится в поле ввода. Для проверки правильности используется функция QLineEdit::hasAcceptableInput(), которая обращается к объекту класса QRegExpValidator, созданному в функции init().

Рисунок 2.12. Окно редактора исходного кода.


После этого опять сохраните диалог. Qt Designer сохранит оба файла -- и gotocelldialog.ui, и gotocelldialog.ui.h. Пересоберите приложение и запустите его. Введите в поле ввода строку "A12" -- кнопка "OK" перейдет в разрешенное состояние. Попробуйте набрать произвольный текст и понаблюдайте за тем, как работает проверка корректности ввода. Нажмите кнопку "Cancel", чтобы завершить работу программы.

В этом примере мы создали диалог с помощью Qt Designer и добавили некоторый код, с помощью редактора исходного кода Qt Designer-а. Интерфейсная часть диалога была сохранена в файле gotocelldialog.ui (по сути файл формата XML), а исходный текст -- в файле gotocelldialog.ui.h. Это очень удобно, поскольку gotocelldialog.ui.h можно править в любом текстовом редакторе.

Альтернативный подход заключается в разработке формы с помощью Qt Designer (как обычно), а затем создается дополнительный класс, порожденный от класса формы, в котором реализуется вся необходимая функциональность. Например, для нашего диалога Go-to-Cell можно было бы создать класс GoToCellDialogImpl, как наследник класса GoToCellDialog и реализовать в нем все необходимые функции. В результате такого подхода, новый заголовочный файл должен получиться таким:

#ifndef GOTOCELLDIALOGIMPL_H #define GOTOCELLDIALOGIMPL_H #include "gotocelldialog.h" class GoToCellDialogImpl : public GoToCellDialog { Q_OBJECT public: GoToCellDialogImpl(QWidget *parent = 0, const char *name = 0); private slots: void enableOkButton(); }; #endif А файл с реализацией: #include <qlineedit.h> #include <qpushbutton.h> #include <qvalidator.h> #include "gotocelldialogimpl.h" GoToCellDialogImpl::GoToCellDialogImpl(QWidget *parent, const char *name) : GoToCellDialog(parent, name) { QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}"); lineEdit->setValidator(new QRegExpValidator(regExp, this)); } void GoToCellDialogImpl::enableOkButton() { okButton->setEnabled(lineEdit->hasAcceptableInput()); } Некоторые разработчики, исповедующие такой подход, наверняка назвали бы базовый класс как: GoToCellDialogBase, а класс реализации: GoToCellDialog.

Создание классов-наследников может быть упрощено с помощью утилиты uic и набора дополнительных аргументов командной строки. Так, например, утилита uic, с ключом -subdecl, создаст скелетон заголовочного файла, а с ключом -subimpl -- соответствующий файл реализации.

В данной книге мы будем работать только с файлами .ui.h, поскольку это наиболее общеупотребимая практика, а создание дочерних классов, с помощью uic, довольно простая задача. Чтобы поглубже разобраться в различиях этих двух подходов, рекомендуем прочитать главу "Designer Approach" в справочном руководстве, поставляемом вместе с Qt Designer. Кроме того, прочитайте главу "Creating Dialogs", где показывается, как можно использовать вкладку "Members" для создания полей (переменных-членов) в классе формы.