Оглавление
Использование UI-файла в вашем приложенииUI-файл Qt Designer представляет собой дерево виджетов формы в формате XML. Формы могут быть обработаны:
Обработка формы во время компиляцииВы создаёте компоненты интерфейса при помощи Qt Designer и используете встроенные инструменты сборки Qt, qmake и uic, чтобы сгенерировать для них код при сборке приложения. Сгенерированный код содержит объект формы пользовательского интерфейса. Это структура C++, содержащая:
Сгенерированный код может включаться в ваше приложение и использоваться непосредственно из него. Кроме того, вы можете использовать его для расширения подклассов стандартных виджетов. Обработка формы во время компиляции может быть использована в вашем приложении одним из следующих способов:
Для демонстрации мы создадим простое приложение "Calculator Form". Оно основано на исходном примере "Calculator Form". Приложение состоит из одного файла исходного текста, main.cpp, и UI-файла. Файл calculatorform.ui, созданный в Qt Designer, показан ниже: Мы будем использовать qmake для построения исполняемых файлов, так что нам потребуется написать .pro файл: TEMPLATE = app FORMS = calculatorform.ui SOURCES = main.cpp Особенностью этого файла является объявление FORMS, которое говорит qmake, какие файлы обрабатывать с помощь uic. В этом случае файл calculatorform.ui используется для создания файла ui_calculatorform.h, который может быть использован любым файлом, перечисленным в объявлении SOURCES. Замечание: Вы можете использовать Qt Creator для создания проекта Calculator Form. Он автоматически сгенерирует файлы main.cpp, UI и .pro, которые вы можете затем изменить. Прямой подходЧтобы использовать прямой подход, мы подключаем файл ui_calculatorform.h непосредственно в main.cpp: #include "ui_calculatorform.h"
Функция main создаёт виджет калькулятора путём создания стандартного QWidget, который мы используем для размещения пользовательского интерфейса, описанного в файле calculatorform.ui. int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget *widget = new QWidget; Ui::CalculatorForm ui; ui.setupUi(widget); widget->show(); return app.exec(); } В этом случае Ui::CalculatorForm является интерфейсом, описывающим объект из файла ui_calculatorform.h, который настраивает все диалоговые виджеты и соединения между сигналами и слотами. Прямой подход предоставляет быстрый и простой способ для применения простых, автономных компонентов в вашем приложении. Однако компоненты, созданные при помощи Qt Designer, часто требуют тесной интеграции с остальной частью кода приложения. Например, описанный выше код CalculatorForm будет компилироваться и запускаться, но объекты QSpinBox не будут взаимодействовать с QLabel, поскольку нам необходим пользовательский слот для выполнения операции сложения и отображения результата в QLabel. Чтобы добиться этого, нам необходимо использовать подход одиночного наследования. Подход одиночного наследованияДля использования подхода одиночного наследования мы создаём подкласс от стандартного виджета Qt и подключаем закрытый экземпляр объекта формы пользовательского интерфейса. Он может принять вид:
Использование переменной-членаВ этом подходе мы создаём подкласс виджета Qt и настраиваем пользовательский интерфейс внутри конструктора. Используемые в этом случае компоненты делают видимыми виджеты и компоновщики, используемые в форме, для подкласса виджета Qt и предоставляют стандартную систему для создания соединений сигналов и слотов между пользовательским интерфейсом и другими объектами в вашем приложении. Сгенерированная структура Ui::CalculatorForm является членом класса. Этот подход применяется в примере "Calculator Form". Для гарантии того, что мы можем использовать пользовательский интефейс, нам необходимо подключить заголовочный файл, который сгенерировал uic, перед тем, как мы будем ссылаться на Ui::CalculatorForm: #include "ui_calculatorform.h"
Это означает, что файл .pro должен быть обновлён для подключения calculatorform.h: HEADERS = calculatorform.h Подкласс определяется следующим образом: class CalculatorForm : public QWidget { Q_OBJECT public: CalculatorForm(QWidget *parent = 0); private slots: void on_inputSpinBox1_valueChanged(int value); void on_inputSpinBox2_valueChanged(int value); private: Ui::CalculatorForm ui; }; Важной особенностью класса является закрытый объект ui, который предоставляет код для настройки и управления пользовательским интерфейсом. Конструктор для подкласса создаёт и конфигурирует все виджеты и компоновщики для диалога только посредством вызова у объекта ui функции setupUi(). Как только это сделано, можно изменять пользовательский интерфейс как необходимо. CalculatorForm::CalculatorForm(QWidget *parent) : QWidget(parent) { ui.setupUi(this); } Мы можем соединять сигналы и слоты в пользовательском интерфейсе виджетов обычным способом, заботясь только о том, чтобы предварять каждый используемый виджет префиксом ui. Преимуществом такого подхода является простое использование наследования, предоставляющее QWidget-основанный интерфейс, и инкапсуляция переменных виджета пользовательского интерфейса в члене данных ui. Мы можем использовать этот метод, чтобы описать внутри того же самого виджета несколько пользовательских интерфейсов, каждый из которых будет внутри своего пространства имён, и расположить их друг на друге (или скомпоновать). Этот подход может быть использован, например, для создания индивидуальных вкладок на существующей форме. Использование указателя на переменную-членКак вариант, структура Ui::CalculatorForm может быть сделана указателем на член класса. В этом случае заголовочный файл выглядит следующим образом: namespace Ui { class CalculatorForm; } class CalculatorForm : public QWidget ... virtual ~CalculatorForm(); ... private: Ui::CalculatorForm *ui; ... Соответствующий исходный файл выглядит следующим образом: #include "ui_calculatorform.h" CalculatorForm::CalculatorForm(QWidget *parent) : QWidget(parent), ui(new Ui::CalculatorForm) { ui->setupUi(this); } CalculatorForm::~CalculatorForm() { delete ui; } Преимуществом этого подхода является то, что объект пользовательского интерфейса может быть объявлен предварительно, что означает, что нам не требуется подключать сгенерированный файл ui_calculatorform.h в заголовочный файл. Форма может быть изменена без перекомпиляции зависимых исходных файлов. Это особенно важно, если класс является предметом ограничений на бинарную совместимость. Обычно мы рекомендуем этот подход для библиотек и больших приложений. Для получения дополнительной информации смотрите Создание разделяемых библиотек. Подход множественного наследованияОт форм, созданных с помощью Qt Designer, вместе со стандартным, основанным на QWidget, классом может быть создан один подкласс. Такой подход делает все компоненты пользовательского интерфейса, описанные в форме, непосредственно доступными в рамках подкласса и позволяет создавать соединения между сигналами и слотами обычным способом при помощи функции connect(). Этот подход используется в примере "Multiple Inheritance". Нам необходимо подключить заголовочный файл, который сгенерировал uic из файла calculatorform.ui, следующим образом: #include "ui_calculatorform.h"
Класс определяется способом, похожим на используемый в подходе одиночного наследования, за исключением того что на этот раз мы наследовались от двух классов: QWidget и Ui::CalculatorForm, следующим образом: class CalculatorForm : public QWidget, private Ui::CalculatorForm { Q_OBJECT public: CalculatorForm(QWidget *parent = 0); private slots: void on_inputSpinBox1_valueChanged(int value); void on_inputSpinBox2_valueChanged(int value); }; Мы наследуем Ui::CalculatorForm как закрытый класс, чтобы гарантировать, что объекты пользовательского интерфейса будут закрытыми в нашем подклассе. Мы можем также унаследовать его с ключевыми словами public или protected в тех ситуациях, когда мы могли бы сделать ui открытым или защищённым в предыдущем случае. Конструктор подкласса выполняет большинство тех же задач, что и конструктор, используемый в примере одиночного наследования: CalculatorForm::CalculatorForm(QWidget *parent) : QWidget(parent) { setupUi(this); } В этом случае к виджетам, которые используются в пользовательском интерфейсе, можно получить доступ точно так же, как если бы они были созданы в коде вручную. Нам больше не требуется префикс ui для доступа к ним. Реакция на изменения языкаQt уведомляет приложения, если изменяется язык пользовательского приложения, отправляя событие типа QEvent::LanguageChange. Для вызова функции-члена retranslateUi() объекта пользовательского интерфейса мы переопределяем QWidget::changeEvent() в классе формы следующим образом: void CalculatorForm::changeEvent(QEvent *e) { QWidget::changeEvent(e); switch (e->type()) { case QEvent::LanguageChange: ui->retranslateUi(this); break; default: break; } } Обработка формы во время выполненияКак вариант, формы могут быть обработаны во время выполнения, производя динамически сгенерированные пользовательские интерфейсы. Это может быть сделано с помощью модуля QtUiTools, который предоставляет класс QUiLoader для обработки форм, созданных с помощью Qt Designer. Подход UiToolsНеобходим файл ресурсов, содержащий файл UI, для обработки форм во время выполнения. Кроме того, приложение должно быть сконфигурировано для использования модуля QtUiTools. Это выполняется включением следующего объявления в проект qmake, гарантирующего, что приложение будет скомпилировано и собрано соответствующим образом. CONFIG += uitools Класс QUiLoader предоставляет объект загрузчика формы для создания пользовательского интерфейса. Этот пользовательский интерфейс может быть извлечён с помощью любого QIODevice, например, объекта QFile, для получения формы, сохранённой в файле ресурсов проекта. Функция QUiLoader::load() создаёт виджет формы, используя описание пользовательского интерфейса, сохранённого в файле. Классы модуля QtUiTools могут быть подключены при помощи следующей директивы: #include <QtUiTools>
Функция QUiLoader::load() вызывается, как показано в этом коде из примера "Text Finder": QWidget* TextFinder::loadUiFile() { QUiLoader loader; QFile file(":/forms/textfinder.ui"); file.open(QFile::ReadOnly); QWidget *formWidget = loader.load(&file, this); file.close(); return formWidget; } В классе, который использует QtUiTools для построения пользовательского интерфейса во время выполнения, мы можем обнаружить объекты в форме, используя qFindChild(). Например, в следующем примере мы находим некоторые компоненты на основе их имён объектов и типов виджетов: ui_findButton = qFindChild<QPushButton*>(this, "findButton"); ui_textEdit = qFindChild<QTextEdit*>(this, "textEdit"); ui_lineEdit = qFindChild<QLineEdit*>(this, "lineEdit"); Обработка формы во время выполнения даёт разработчику свободу изменения пользовательского интерфейса программы простым изменение файла UI. Это полезно при настройке программ, чтобы удовлетворить различные потребности пользователя, такие как большие иконки или другую цветовую схему для поддержки специальных возможностей. Автоматические соединенияСоединения сигналов и слотов, определённые для форм во время компиляции или во время выполнения, могут быть установлены вручную или автоматически при помощи способности QMetaObject создавать соединения между сигналами и слотами с соответствующим названием. Обычно в QDialog, если мы хотим обработать информацию, введённую пользователем, прежде, чем принять её, мы должны соединить сигнал clicked() от кнопки OK с пользовательским слотом в нашем диалоге. Сначала мы покажем пример диалогового окна, в котором слот подсоединён вручную, а затем сравним его с диалоговым окном, использующим автоматическое соединение. Диалог без автосоединенияМы определяем диалог так же, как и раньше, но теперь включаем слот в дополнение к конструктору: class ImageDialog : public QDialog, private Ui::ImageDialog { Q_OBJECT public: ImageDialog(QWidget *parent = 0); private slots: void checkValues(); }; Слот checkValues() будет использоваться для проверки значений, указанных пользователем. В конструкторе диалогового окна мы настраиваем виджет, как и прежде, а у кнопки Cancel соединяем сигнал clicked() со слотом reject() диалогового окна. Также мы отключаем свойство autoDefault для обоих кнопок, чтобы быть уверенными, что диалог не вмешивается в способ, которым строка редактирования обрабатывает событие нажатия клавиши Return: ImageDialog::ImageDialog(QWidget *parent) : QDialog(parent) { setupUi(this); okButton->setAutoDefault(false); cancelButton->setAutoDefault(false); ... connect(okButton, SIGNAL(clicked()), this, SLOT(checkValues())); } Мы подсоенияем у кнопки OK сигнал clicked() к слоту checkValues() диалогового окна и реализуем его следующим образом: void ImageDialog::checkValues() { if (nameLineEdit->text().isEmpty()) (void) QMessageBox::information(this, tr("No Image Name"), tr("Please supply a name for the image."), QMessageBox::Cancel); else accept(); } Этот пользовательский слот делает минимум для того, чтобы удостовериться, что данные, введённые пользователем, верны - он принимает только тот ввод, в котором было указано название для изображения. Виджеты и диалоги с автосоединениемНесмотря на то что легко реализовать пользовательский слот в диалоговом окне и соединить его в конструкторе, мы можем вместо этого использовать средства автосоединения QMetaObject для подсоединения сигнала clicked() кнопки OK в нашем подклассе. uic автоматически создаёт код в функции setupUi() диалога, чтобы выполнить это, так что нам необходимо только объявить и реализовать слот с именем, которое соответствует стандартному соглашению: void on_<object name>_<signal name>(<signal parameters>); Используя это соглашение, мы можем определить и реализовать слот, который реагирует на нажатие мышью по кнопке OK: class ImageDialog : public QDialog, private Ui::ImageDialog { Q_OBJECT public: ImageDialog(QWidget *parent = 0); private slots: void on_okButton_clicked(); }; Другим примером автоматического соединения сигнала и слота может быть Text Finder со слотом on_findButton_clicked(). Мы используем систему QMetaObject для включения соединений сигнала и слота: QMetaObject::connectSlotsByName(this); Это позволяет нам реализовать слот, как показано ниже: void TextFinder::on_findButton_clicked() { QString searchString = ui_lineEdit->text(); QTextDocument *document = ui_textEdit->document(); bool found = false; if (isFirstTime == false) document->undo(); if (searchString.isEmpty()) { QMessageBox::information(this, tr("Empty Search Field"), "The search field is empty. Please enter a word and click Find."); } else { QTextCursor highlightCursor(document); QTextCursor cursor(document); cursor.beginEditBlock(); ... cursor.endEditBlock(); isFirstTime = false; if (found == false) { QMessageBox::information(this, tr("Word Not Found"), "Sorry, the word cannot be found."); } } } Автоматическое соединение сигналов и слотов предоставляет и стандартное соглашение об именовании, и явный интерфейс для разработчиков виджетов для работы с ними. Обеспечивая исходный код, который реализует указанный интерфейс, дизайнер пользовательского интерфейса может убедиться, что его дизайн работает в действительности без необходимости самостоятельно писать код. |
Попытка перевода Qt документации. Если есть желание присоединиться, или если есть замечания или пожелания, то заходите на форум: Перевод Qt документации на русский язык... Люди внесшие вклад в перевод: Команда переводчиков |