是否可以在Qt设计器中使用依赖项注入(对于.ui文件)

Is it possible to use dependency injection with Qt Designer (for .ui files)?

本文关键字:注入 依赖 对于 文件 ui Qt 是否      更新时间:2023-10-16

我们正在C++(和Qt)中使用Visual Studio 2015和用于UI的Qt设计器(通过Form/.UI文件)进行开发。

我们现在需要在UI元素之间共享一些公共数据(例如,最近使用的路径等),我希望通过依赖注入(即,在构建过程中为UI提供对公共对象的引用)来实现这一点,而不是例如(ab)使用单例模式。

有人面临过(并解决过)类似的问题吗?有合理的方法吗?

更新(详细说明):
例如,我有一个自定义的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类中共享的文本大小和颜色),但我不能在构造函数中这样做(因为自动生成的Ui_FooDialog将FooWidget视为通用QWidget,它只需要/接受构造函数中的QWidget* parent-我不能提供指向共享TextColourAndSize对象的指针或引用。

我知道我可以在FooDialog中创建第二个ui->widget->setupTextColourAndSize(...)步骤(在最初的ui->setupUi(this)之后),它提供了公共数据对象,但拥有两个init()类型的函数似乎是一种相当糟糕的代码气味(一个已经足够糟糕了)。

FooWidget需要两个构造函数和一个依赖项的setter:

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*)

在任何情况下,都需要在Qt Designer中将fooWidget对象提升为FooWidget类。

好吧,根据我所看到的,您不需要"依赖注入"。问题陈述错误。

您可以直接从Qt设计器使用此自定义小部件。

  1. 创建FooDialog时,将常规小部件QWidget放置在需要FooWidget的位置
  2. 然后将这个小部件"升级"到FooWidget(可能需要添加一些关于该类型的简单信息)-(我很久以前就这样做了,不记得所有细节)

关于谷歌的详细说明:qt推广小工具qt设计器,你会发现很多例子如何做到这一点。

这些都是很好的解决方案,但在谈到依赖注入时,也可以选择用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());
}

多继承+从一些小模板代理类派生小部件既适用于自定义小部件,也适用于具有UI表单的小部件。在上面的草图中,我在FooDialog上放置了一个FooWidget(通过传播机制),并得到了两个消息框。

这个想法本身可以更好地实现,使用更智能的模板,只是试图减少样本代码,无论如何,这是一个不必要的复杂性。但从技术上讲,它可以在没有任何额外初始化的情况下工作=)