Можно ли использовать внедрение зависимостей в Qt Designer (для файлов.ui)?
Мы разрабатываем на C++ (и Qt), используя Visual Studio 2015 и Qt Designer для нашего пользовательского интерфейса (через файлы Form / .ui).
Теперь нам нужно совместно использовать некоторые общие данные между нашими элементами пользовательского интерфейса (например, последние использованные пути и т. Д.), И я хотел бы сделать это посредством внедрения зависимостей (т. Е. Предоставления пользовательскому интерфейсу ссылки на общий объект во время построения) вместо, например, (а.б) использование шаблона синглтона.
Кто-нибудь сталкивался (и решал) подобные проблемы, и есть ли разумный способ сделать это?
Обновление (для уточнения):
Например у меня есть обычай FooWidget
который я хочу использовать в моем FooDialog.ui
файл формы.
// My custom Widget
class FooWidget : public QWidget {
Q_OBJECT
public:
FooWidget(QWidget* parent);
//...
}
// The FooDialog class (scaffolding created by Qt Designer)
class FooDialog : public QDialog {
Q_OBJECT
public:
FooDialog(QWidget* parent) : QDialog(parent), ui (new Ui::FooDialog()) {
ui->setupUp(this);
//...
}
private:
Ui::FooDialog* ui;
}
// The Ui::FooDialog class autogenerated(!) by Qt Designer
// I cannot (must not) change this code, as it will be regenerated every time
class Ui_FooDialog {
public:
FooWidget* widget;
void setupUi(QWidget *fooDialog) {
//...
widget = new FooWidget(fooDialog);
//...
}
}
namespace Ui { class ScannerStatus: public Ui_ScannerStatus {}; }
Я хотел бы предоставить FooWidget общий объект данных (например, размер текста и цвета, общие для всех моих классов пользовательского интерфейса), но я не могу сделать это в конструкторе (так как автоматически сгенерированный Ui_FooDialog обрабатывает FooWidget
как общий QWidget
, который только нуждается / принимает QWidget* parent
в конструкторе - я не могу предоставить указатель или ссылку на мой общий доступ TextColourAndSize
объект.
Я знаю, что мог бы создать вторую ui->widget->setupTextColourAndSize(...)
вмешаться FooDialog
(после начального ui->setupUi(this)
), который обеспечивает этот общий объект данных, но имеет два init()
функции типа кажутся довольно неприятным запахом кода (достаточно плохой).
3 ответа
FooWidget
нужны два конструктора и установщик для зависимости:
explicit FooWidget(QObject *parent = nullptr) : QWidget(parent), … {
…
}
FooWidget(Dependency *dep, QObject *parent = nullptr) : FooWidget(parent) {
setDependency(dep);
}
void setDependency(Dependency *dep) {
…
}
Затем вы устанавливаете зависимость после того, как виджет создан:
FooDialog(Dependency *dep, …) … {
setupUi(this);
ui->fooWidget->setDependency(dep);
}
Это может быть автоматизировано: родительский виджет может иметь свойство, которое содержит указатель на зависимость, а дочерние виджеты могут найти его автоматически:
FooDialog(Dependency *, …) : … {
setProperty("dependency", dep);
setupUi(this);
}
FooWidget(QWidget *parent) : … {
auto *dep = parent() ? parent()->property("dependency").value<Dependency*>() : nullptr;
if (dep) setDependency(dep);
}
Это будет работать без лишних усилий, если Dependency
происходит от QObject
, В противном случае вам понадобится следующее в подходящем заголовочном файле:
class Dependency { … };
Q_DECLARE_METATYPE(Dependency*)
При любых обстоятельствах вам необходимо продвигать fooWidget
Возражать FooWidget
класс в Qt Designer.
Хорошо, из того, что я вижу, вам не нужно "внедрение зависимости". Вопрос был неверно сформулирован.
Вы можете использовать этот пользовательский виджет прямо из дизайнера Qt.
- Когда вы создаете свой
FooDialog
разместить обычный виджетQWidget
в месте, где вам нужно иметьFooWidget
, - Затем "продвиньте" этот виджет регулярно
FooWidget
(возможно, вам придется добавить некоторую простую информацию об этом типе) - (Я сделал это давным-давно и не помню все детали).
Для подробной инструкции просто гуглите: qt продвигайте виджет qt designer, вы найдете множество примеров, как это сделать.
Это были хорошие решения, но, говоря об инъекциях зависимостей, есть также возможность повеселиться с C++. Это совсем не мудрое решение, конечно, я знаю это, но тем не менее...
foowidget.h
#ifndef FOOWIDGET_H
#define FOOWIDGET_H
#include <QWidget>
class Something
{
public:
QString getHello() const
{ return "Hello world!"; }
};
/***************************************************/
template<typename T>
class Injector
{
public:
QString getHello() const
{ return m_dataContainer.getHello(); }
private:
T m_dataContainer;
};
/***************************************************/
class FooWidget : public QWidget, public Injector<Something>
{
Q_OBJECT
public:
explicit FooWidget(QWidget* parent = nullptr);
protected:
virtual void mousePressEvent(QMouseEvent*) override;
};
#endif // FOOWIDGET_H
foowidget.cpp
#include "foowidget.h"
#include <QMessageBox>
FooWidget::FooWidget(QWidget *parent)
: QWidget(parent)
{ }
void FooWidget::mousePressEvent(QMouseEvent*)
{
QMessageBox::information(nullptr, "Test", getHello());
}
foodialog.h
#ifndef FOODIALOG_H
#define FOODIALOG_H
#include <QDialog>
class SomethingElse
{
public:
QString getHello() const
{ return "OMG! OMG"; }
};
#include "foowidget.h"
namespace Ui {
class FooDialog;
}
class FooDialog : public QDialog, public Injector<SomethingElse>
{
Q_OBJECT
public:
explicit FooDialog(QWidget *parent = nullptr);
~FooDialog();
protected:
void showEvent(QShowEvent *) override;
private:
QScopedPointer<Ui::FooDialog> ui;
};
#endif // FOODIALOG_H
foodialog.cpp
#include "foodialog.h"
#include "ui_foodialog.h"
#include <QMessageBox>
FooDialog::FooDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::FooDialog)
{
ui->setupUi(this);
}
FooDialog::~FooDialog()
{ }
void FooDialog::showEvent(QShowEvent *)
{
QMessageBox::information(nullptr, "Test", getHello());
}
Множественное наследование + получение виджетов из небольшого прокси-класса шаблонов работает как для пользовательских виджетов, так и для тех, которые имеют формы пользовательского интерфейса. Я поместил FooWidget в FooDialog (через механизм распространения) в скриншоте выше и получил два окна сообщений.
Сама идея может быть реализована лучше, с более разумным использованием шаблонов, просто попытались уменьшить пример кода, в любом случае, это является ненужным осложнением. Но технически это работает без каких-либо дополнительных инициализаций =)