Можно ли использовать внедрение зависимостей в 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.

  1. Когда вы создаете свой FooDialog разместить обычный виджет QWidget в месте, где вам нужно иметь FooWidget,
  2. Затем "продвиньте" этот виджет регулярно 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 (через механизм распространения) в скриншоте выше и получил два окна сообщений.

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

Другие вопросы по тегам