Spec-Zone.ru › Qt 5.6

Использование файла UI-дизайнера в вашем приложении

Файлы UI Qt Designer представляют собой дерево виджетов формы в формате XML. Формы можно обрабатывать:

  • На этапе компиляции, что означает, что формы преобразуются в код C++, который может быть скомпилирован.
  • Во время выполнения, что означает, что формы обрабатываются классом QUiLoader, который динамически строит дерево виджетов при парсинге XML-файла.

Обработка форм на этапе компиляции

Вы создаете компоненты пользовательского интерфейса с помощью Qt Designer и используете интегрированные инструменты сборки Qt, qmake и uic, для генерации кода для них при построении приложения. Сгенерированный код содержит объект пользовательского интерфейса формы. Это структура C++, которая содержит:

  • Указатели на виджеты, макеты, элементы макета, группы кнопок и действия формы.
  • Функцию-член, названную setupUi(), для построения дерева виджетов на родительском виджете.
  • Функцию-член, названную retranslateUi(), которая обрабатывает перевод строковых свойств формы. Дополнительную информацию см. в разделе Реагирование на изменения языка.

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

Обработанную на этапе компиляции форму можно использовать в вашем приложении с помощью одного из следующих подходов:

  • Прямой подход: вы создаете виджет для использования в качестве места для компонента и настраиваете пользовательский интерфейс внутри него.
  • Подход с одиночным наследованием: вы создаете подкласс базового класса формы (QWidget или QDialog, например) и включаете в него частную экземпляцию объекта пользовательского интерфейса формы.
  • Подход с множественным наследованием: вы создаете подкласс как базового класса формы, так и объекта пользовательского интерфейса формы. Это позволяет использовать виджеты, определенные в форме, непосредственно в пределах области подкласса.

Для демонстрации мы создаем приложение с простой формой калькулятора. Оно основано на оригинальном примере Формы калькулятора.

Приложение состоит из одного исходного файла, main.cpp и файла UI.

Файл calculatorform.ui, разработанный с помощью Qt Designer, показан ниже:

Мы будем использовать qmake для построения исполняемого файла, поэтому нам необходимо написать файл .pro:

HEADERS     = calculatorform.h

Особой особенностью этого файла является объявление 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 является членом класса.

Этот подход используется в примере Формы калькулятора.

Чтобы гарантировать возможность использования пользовательского интерфейса, нам необходимо включить заголовочный файл, который 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);
}

Мы можем подключать сигналы и слоты к виджетам пользовательского интерфейса обычным способом, добавив префикс on_<имя объекта>. Дополнительную информацию см. в разделе widgets-and-dialogs-with-auto-connect.

Преимущества этого подхода заключаются в простом использовании наследования для обеспечения интерфейса на основе 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().

Этот подход используется в примере Множественного наследования.

Нам необходимо включить заголовочный файл, который 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 , гарантируя, что приложение скомпилировано и связанно должным образом.

QT += uitools

Класс QUiLoader предоставляет объект загрузчика форм для построения пользовательского интерфейса. Этот пользовательский интерфейс можно получить из любого QIODevice, например, объекта QFile, чтобы получить форму, хранящуюся в файле ресурсов проекта. Функция QUiLoader::load() строит виджет формы, используя описание пользовательского интерфейса, содержащееся в файле.

Классы модуля QtUiTools можно включить с помощью следующей директивы:

#include <QtUiTools>

Функция QUiLoader::load() вызывается, как показано в этом коде из примера Поиск текста:

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 = findChild<QPushButton*>("findButton");
    ui_textEdit = findChild<QTextEdit*>("textEdit");
    ui_lineEdit = findChild<QLineEdit*>("lineEdit");

Обработка форм во время выполнения предоставляет разработчику свободу изменения пользовательского интерфейса программы просто путем изменения файла пользовательского интерфейса. Это полезно при настройке программ для удовлетворения различных потребностей пользователей, таких как дополнительные большие значки или другая цветовая схема для поддержки доступности.

Автоматические подключения

Подключения сигналов и слотов, определенные для форм во время компиляции или выполнения, могут быть настроены вручную или автоматически, используя возможности QMetaObject для создания подключений между сигналами и слотами с соответствующими именами.

В общем случае, в QDialog, если мы хотим обработать информацию, введенную пользователем, перед ее принятием, нам нужно подключить сигнал clicked() от кнопки OK к пользовательскому слоту в нашем диалоге. Сначала мы покажем пример диалога, в котором слот подключен вручную, а затем сравним его с диалогом, который использует автоматическое подключение.

Диалог без автоподключения

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

class ImageDialog : public QDialog, private Ui::ImageDialog
{
    Q_OBJECT

public:
    ImageDialog(QWidget *parent = 0);

private slots:
    void checkValues();
};

Слот checkValues() будет использоваться для проверки введенных пользователем значений.

В конструкторе диалога мы настраиваем виджеты, как и раньше, и подключаем сигнал clicked() кнопки Отмена к слоту reject() диалога. Мы также отключаем свойство autoDefault для обеих кнопок, чтобы убедиться, что диалог не вмешивается в способ, которым редактор строки обрабатывает события возврата:

ImageDialog::ImageDialog(QWidget *parent)
    : QDialog(parent)
{
    setupUi(this);
    okButton->setAutoDefault(false);
    cancelButton->setAutoDefault(false);
    ...
    connect(okButton, SIGNAL(clicked()), this, SLOT(checkValues()));
}

Мы подключаем сигнал clicked() кнопки OK к слоту 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();
};

Еще одним примером автоматического подключения сигналов и слотов будет Поиск текста со своим слотом 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.");
        }
    }
}

Автоматическое подключение сигналов и слотов предоставляет как стандартную систему именования, так и явный интерфейс для работы разработчиков пользовательских интерфейсов. Предоставляя исходный код, реализующий заданный интерфейс, дизайнеры пользовательских интерфейсов могут проверить, что их проекты на самом деле работают, не прибегая к написанию кода самим.

© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/archives/qt-5.6/designer-using-a-ui-file.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API