3 окт. 2008 г.

Qt4: dynamic loading ui from an other ui, part 1

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

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

Для пояснения приведу пример: адрес получателя представляет из себя следующую форму:
И данная форма может быть использована как в диалоге для отправления больших грузов, для заполнении информации о ком-то и т.п.

Главное, что это шаблонная форма адресата - и когда меняется этот шаблон везде в системе должен быть использован этот шаблон. При этом, возможность перекомпиляции не доступна - движок не знает и не должен знать какие внешние ресурсы что используют - все данные доступны только в run time.

Выражаясь в терминах spring framework - необходимо включать (include) одного файла в другой для повторного использования набора компонент.

В продолжении предыдущей статьи Quick start with loading extern ui рассмотрим включение одного ui-файла другим ui-файлом.

Стандартного способа я не нашёл, поэтому будем делать собственный компонент / custom widget, который будет загружать то, что ему скажут.

LoadableWidget.h:
#include <QWidget>
#include <QtDesigner/QDesignerExportWidget>

class QDESIGNER_WIDGET_EXPORT LoadableWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(QString uiFilename READ getUiFilename WRITE setUiFilename)

public:
LoadableWidget(QWidget *parent = 0):QWidget(parent) {}

QString getUiFilename() const;

void setUiFilename(const QString fileName);
private:
QString uiFileName;
};

LoadableWidget.cpp:
#include <QDebug>
#include <QFile>
#include <QUiLoader>
#include <QWidget>
#include <QVBoxLayout>

#include "LoadableWidget.h"

QString LoadableWidget::getUiFilename() const
{
return this->uiFileName;
}

void LoadableWidget::setUiFilename(const QString fileName)
{
this->uiFileName = fileName;
QFile *file = new QFile(fileName);

if (file->exists())
{
QUiLoader *loader = new QUiLoader();
file->open(QFile::ReadOnly);

QWidget *widget = loader->load(file);
file->close();
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(widget);
}
else
{
qDebug() << "there is no file:" << fileName;
}
}

Пример использования данного компонента в ui-файле:

<ui version="4.0" >
<class>Form</class>
<widget class="QWidget" name="Form" >
<property name="windowTitle" >
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>

<!-- использование компонента, загружающего другие ui -->
<widget class="LoadableWidget" name="loadableWidget" >
<property name="uiFilename">
<string>../simple/widget.ui</string>
</property>
</widget>

</item>
<item>
<widget class="QLabel" name="label" >
<property name="text" >
<string>simple.ui label</string>
</property>
</widget>
</item>
</layout>
</widget>

<!-- описание собственных компонент / custom widgets declaration -->
<customwidgets>
<customwidget>
<class>LoadableWidget</class>
<extends>QWidget</extends>
<header>loadablewidget.h</header>
</customwidget>
</customwidgets>

<resources/>
<connections/>
</ui>

main.cpp приводить не буду - он такой же как и в предыдущей статье, точно такими же остаются шаги по созданию файла проекта (pro), Makefile'а и компиляции.

simple.ui
в ../simple/widget.ui описаны две кнопки Ok и Cancel

Обновлено: всё же я слукавил, что это всё - необходимо custom widget собрать и положить в отдельную библиотеку.

Для полного счастья надо собрать custom widget в библиотеку, и использовать её - Qt4: dynamic loading ui from an other ui, part 2

Комментариев нет: