Программирование на C++ с использованием библиотеки Qt4

         

Edit| Edit Signals/Slots


или просто нажав клавишу F4. С помощью левой кнопки мыши "зацепим" поле sb -- источник сигнала и "бросим" его на элемент le -- приёмник сигнала (рис.).

В открывшемся диалоге (рис.) поставим галочку



Edit Tab Order


. На редактируемой форме появятся квадратики с числами (рис.). Будем щёлкать по ним левой кнопкой мыши в нужном порядке. После этого можно опять войти в режим предпросмотра



Form| Preview


).

Разумеется, для реализации более сложных действий (например, изменение доступности одних элементов диалога в зависимости от значений, вводимых пользователем в другие элементы) без настоящего программирования обойтись не получится.

Сохраним описание диалога под любым именем (например, dialog) и с расширением ui.

В листинге 11 приведён фрагмент получившегося в результате файла. Как видим, описание диалога хранится в формате XML.



Label


(рис.) и с помощью левой кнопки мыши перетащим его на форму. В правой части дизайнера, в окне



Lay Out Horizontally


(расположить по горизонтали). В результате автоматически будет создан менеджер размещения QHBoxLayout и оба выделенных виджета окажутся внутри него. Останется только задать единичное значение параметра horizontalStretch (растяжение по горизонтали) из раздела sizePolicy для однострочного поля ввода.


Рис. Внешний вид окна Qt Designer

Аналогично создадим два горизонтальных менеджера размещения для двух следующих строк нашего диалога. Поле для ввода даты/времени пропустим, т.к. оно единственное в своей строке. Далее выделим три зависимых переключателя и создадим для них всё тот же горизонтальный менеджер размещения. Для самой рамки Group Box, внутри которой расположены три зависимых переключателя, также создадим свой QHBoxLayout (хотя эта рамка и единственная в своей строке, но она является контейнером для других элементов и без менеджера размещения её высота не может быть вычислена правильно).

Кнопки диалога трогать не будем, потому что при создании формы мастер уже разместил их в элементе Button Box, который является аналогом менеджера размещения, только используется специально для кнопок. Можно изменить состав кнопок с помощью свойства standardButtons и их выравнивание по горизонтали (свойство centerButtons=true).

Наконец, нажмём правой кнопкой мышки по пустому фону диалога и в появившемся контекстном меню выберем команду





Lay Out Vertically


(расположить по вертикали). В результате будет создан вертикальный менеджер QVBoxLayout и внутри него будут размещены все имеющиеся на форме строки QHBoxLayout вместе с полем ввода даты/времени и вертикальным промежутком (для которых мы так и не создали горизонтального менеджера размещения).

Можно проверить, как будет выглядеть наш диалог при различном оформлении (команда меню



Файл *.ui


<ui version="4.0" >
<class>
Dialog</class>
<widget class="QDialog" name="Dialog" >
<property name="geometry" >
<rect>
<x>
0</x>
<y>
0</y>
<width>
274</width>
<height>
259</height>
</rect>
</property>
....... </ui>

В листинге 12 показано, как использовать в программе диалог, разработанный с помощью QtDesigner.



Загрузка ui-ресурса (examples-qt/05/05.cpp)


#include <QApplication>
#include <QDialog>

#include "ui_dialog.h"

int main(int argc, char *argv[]) { QApplication app(argc, argv);
Ui::Dialog ui; QDialog *dialog = new QDialog; ui.setupUi(dialog);
dialog->
show();
return app.exec();
}

Мы подключили заголовочный файл, имя которого начинается префиксом ui_, после которого записывается имя файла, созданного в дизайнере. Откуда берётся ui_dialog.h? Этот файл формируется с помощью утилиты uic, которая автоматически вызывается при компиляции программы.

Затем объявили экземпляр класса Ui::Dialog (этот класс описан в заголовочном файле) и экземпляр стандартного класса QDialog, после чего для первого вызвали метод setupUi, указав второй в качестве параметра.

Компиляция программы проводится обычным порядком: сначала команда qmake -project, затем просто qmake и, наконец, mingw32-make или nmake.

Можете запустить программу на выполнение: на экран выводится окно диалога, доступность поля ввода вещественного числа зависит от состояния переключателя, изменяемое значение sb тут же отображается в однострочном поле ввода le, а при нажатии любой из двух кнопок диалог закрывается.

Для выполнения более сложных действий придётся описать новый класс, например, MyDialog, указав в качестве базового стандартный класс QDialog. При этом относительно разработанного в дизайнере класса Ui::Dialog имеется две возможности: либо определить экземпляр этого класса внутри MyDialog, либо сделать класс Ui::Dialog вторым родителем класса MyDialog.

Первый вариант (с одним базовым классом) приведён в листинге 13.



Работа с ui-ресурсом


// В заголовочном файле *.h: #include <QDialog>
#include "ui_dialog.h"

class MyDialog : public QDialog { Q_OBJECT public: MyDialog(QWidget *parent = 0);
private: Ui::Dialog ui; };

// В файле *.cpp: MyDialog::MyDialog(QWidget *parent) : QDialog(parent) {

ui.setupUi(this);

ui.cb->
addItem(tr("Первый"));
ui.cb->
addItem(tr("Второй"));
ui.cb->
addItem(tr("Третий"));
ui.cb->
setCurrentIndex(2);

// ....... } Второй вариант (с двумя базовыми классами) показан в листинге 14.



Работа с ui-ресурсом (файлы examples-qt/06/06.h и 06.cpp)


// В заголовочном файле *.h: #include <QDialog>
#include "ui_dialog.h"

class MyDialog : public QDialog, private Ui::Dialog { Q_OBJECT public: MyDialog(QWidget *parent=0);
};

// В файле *.cpp: MyDialog::MyDialog(QWidget *parent) : QDialog(parent) {

setupUi(this);

cb->
addItem(tr("Первый"));
cb->
addItem(tr("Второй"));
cb->
addItem(tr("Третий"));
cb->
setCurrentIndex(2);

// ........ }

int main(int argc, char *argv[]) { QApplication app(argc, argv);
QTextCodec *codec = QTextCodec::codecForName("CP1251");
QTextCodec::setCodecForTr(codec);

MyDialog *dlg = new MyDialog();
dlg->
show();
return app.exec();
}

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

Соединения между сигналами и слотами для класса MyDialog можно определять как обычно, с помощью метода connect (например, в конструкторе класса MyDialog), при этом пользовательские слоты должны быть перечислены в объявлении класса или унаследованы от базовых классов. Но можно воспользоваться особым правилом именования слотов: private slots: on_ИмяВиджета_ИмяСигнала(ПараметрыСигнала);
В этом случае соединение между сигналом и слотом будет выполнено автоматически (в методе setupUi(), который генерируется утилитой uic). Например: // В заголовочном файле *.h: #include <QDialog>
#include "ui_dialog.h"

class MyDialog : public QDialog, public Ui::Dialog { Q_OBJECT public: MyDialog(QWidget *parent=0);
private slots: void on_rb3_toggled(bool s);
};

// В файле *.cpp: void MyDialog::on_rb3_toggled(bool s) { dte->
setHidden(s);
} В результате при выборе радиокнопки rb3 поле ввода даты/времени будет спрятано, а при выборе любой другой радиокнопки -- снова показано.

Оба описанных варианта компоновки внешнего ресурса с основной программой являются статическими: после компиляции программы ui-файл можно удалить. Существует и вариант динамической загрузки во время выполнения программы (листинг 15). Здесь используется класс QUiLoader. При этом требуется подключить заголовочный файл QtUiTools, а в файле проекта *.pro -- добавить строку CONFIG += uitools

После редактирования pro-файла не следует выполнять qmake с параметром -project, иначе все изменения в нём будут потеряны.



Динамическая загрузка ui-ресурса (examples-qt/07/07.cpp)


#include <QtGui>
#include <QtUiTools>

int main(int argc, char *argv[]) { QApplication app(argc, argv);
QTextCodec *codec = QTextCodec::codecForName("CP1251");
QTextCodec::setCodecForTr(codec);

QUiLoader uiLoader; QFile file("dialog.ui");
file.open(QFile::ReadOnly);
QWidget *dlg = uiLoader.load(&file);
file.close();
if (dlg) { QComboBox *cb = dlg->
findChild<QComboBox*>
("cb");
cb->
addItem(QObject::tr("Первый"));
cb->
addItem(QObject::tr("Второй"));
cb->
addItem(QObject::tr("Третий"));
cb->
setCurrentIndex(2);
cb->
setEditable(true);
cb->
setInsertPolicy(QComboBox::InsertAtBottom);

QSpinBox *sb = dlg->
findChild<QSpinBox*>
("sb");
sb->
setValue(5);

dlg->
findChild<QCheckBox*>
("chb")->
setCheckState(Qt::Checked);

// .......

dlg->
findChild<QRadioButton*>
("rb2")->
setChecked(true);

dlg->
show();
return app.exec();
} else return 1; }

Для обращения к элементам диалога при динамической загрузке используется метод parent->
findChild<Тип *>
("ИмяЭлемента") или функция qFindChild<Тип *>
(parent, "ИмяЭлемента")

Второй вариант предназначен для компиляторов, которые не поддерживают шаблоны методов (как, например, Microsoft Visual C++ 6.0).



NewForm


. Из представленных вариантов заготовок (диалоги с горизонтальным или вертикальным размещением кнопок, главное окно приложения или произвольный виджет) выберем первый и нажмём кнопку



Ok


. После этого можно снова проверить, как всё работает (



+


", после чего последовательно укажите все четыре параметра соединения (рис.). После этого в свойствах элемента chb надо задать начальное значение checked=false, а для элемента dsb -- значение enabled=false. Теперь можно в режиме предварительного просмотра проверить, как изменяется доступность поля ввода числа при изменении состояния переключателя.

Аналогично определим ещё два соединения, соответствующие нажатию кнопок "Сохранить" и "Отмена" (см. рис.). Стандартные для модального диалога слоты accept и reject закрывают диалог и запоминают результат Accepted (принят) или Rejected (отклонён), который затем может быть прочитан с помощью метода result().

Теперь предположим, что при изменении числа в поле sb необходимо тут же выводить это же значение в поле le. Воспользуемся вторым способом определения соединений. Войдём в режим визуального редактирования сигналов и слотов, выполнив пункт меню



Preview in


).


Рис. Порядок обхода элементов

Теперь надо задать порядок обхода элементов при нажатии клавиши Tab. Для этого выполним команду меню



Previewin


и с помощью клавиши Tab убедиться, что порядок обхода элементов задан правильно.

Допустим, нам требуется сделать недоступным поле ввода вещественного числа dsb при сбросе переключателя chb. Другими словами, надо связать сигнал toggled(bool) элемента chb и слот enabled(bool) элемента dsb. Если в нижнем правом углу окна Qt Designer вы не видите панель



Qt


. Покажем, как пользоваться дизайнером на примере нашего диалога (см.рис.).

Откроем Qt Designer и (если диалог создания новой формы не появится автоматически) выполним команду меню



Редактор свойств


) укажем идентификатор элемента (значение свойства



Show all signals and slots


(показывать все сигналы и слоты), в левом окне выберем сигнал valueChanged(QString), а в правом -- слот setText(QString). Нажмём



| Signal/Slot Editor


. Для добавления нового соединения нажмите кнопку "



Создание диалогов с помощью QtDesigner


Для разработки интерфейса программы в Qt имеется специальный инструмент -- Qt Designer. Он используется либо как самостоятельное приложение, либо как компонент, встроенный в интегрированную среду Microsoft Visual Studio. Во втором случае доступ к его функциям производится с помощью пункта меню



Text


).

Аналогично разместим на форме однострочное поле ввода Line Edit (идентификатор le), поле со списком Combo Box (идентификатор cb), поле для ввода целого числа с кнопками инкремента/декремента Spin Box (идентификатор sb), независимый переключатель Check Box (идентификатор chb), поле для ввода вещественного числа с кнопками инкремента/декремента Double Spin Box (идентификатор dsb), поле для выбора даты/времени Date/Time Edit (идентификатор dte), контейнер Group Box (идентификатор groupBox) с тремя зависимыми переключателями Radio Button (идентификаторы rb1, rb2 и rb3), как показано на рис.

Пока не следует сильно заботиться о красивом размещении элементов друг относительно друга и об их точных размерах. Для первой радиокнопки зададим значение свойства checked=true. Между рамкой с зависимыми переключателями и кнопками диалога вставим элемент Vertical Spacer (вертикальный промежуток, он находится в группе Spacers). Для элемента Button Box проверим значение свойства standardButtons: галочки должны быть установлены только для кнопок Cancel и Save.

Теперь выделим мышкой первые два элемента (Label и Line Edit), находящиеся в первой горизонтальной строке диалога. Для выделения нескольких элементов можно использовать клавишу Shift. Щёлкнем по выделенной группе правой кнопкой мыши и в появившемся контекстном меню выберем команду