1
votes

I am developing a custom widget consisting of other widgets (buttons, labels, viewers etc) which is going to be a module in other projects. It is important to have multiple layouts for this custom widget.

The layout is going to change depending on some dynamically determined factors, like the application which is part (e.g. for the X application we want things to be arranged vertically, or we want Z widget next to M widget instead of one under another) and also depending on the features detected (if hardware X is not detected then hide A, B, C widgets).

Until now, I had a single layout in Designer Form which I would modify a little bit programmatically on the fly for small rearrangements.This not viable anymore since in some applications we need a completely different layout.

So, to sum thing up, we have fixed number of widgets but we want different rearrangement and visibility depending on some dynamically determined factors.

What's the proper way to handle this situation?

Solutions I have thought so far:

  • Create multiple .ui files and load the one I need dynamically using QUiLoader

  • Create the layouts purely programmatically

  • Create single .ui file with a stacked widget and each page would be a different layout. When switching a layout, the widgets of the "active" layout will be assigned by pointers.

1
Single big GUI definition file is often problematic, unexpexted issues arise when making changes (not limited to Qt Designer's .ui files). - hyde
I think this depends on the complexity if the 3rd option is to be used. - drescherjm
You are missing option, create independent widgets which do one thing. They can be pure C++ or use .ui files, doesn't matter as they are independent, self-contained GUI components. Then use Promote functionality of Designer to actually use them in Designer. Also, top-level is probably simplest to do in pure C++ code without Designer. - hyde
@drescherjm I am not really in favor of 3rd option for the reason you mentioned, the complexity and also does not seem "right" to me. But if it's less cumbersome than the other 2 then I might go with that. - Manos
@hyde In my custom widget I already have other custom widgets using promotion. Additionally, the aforementioned custom widget is going to be promoted in other, larger applications. - Manos

1 Answers

1
votes

I suggest option #2, and a clean way to implement it.

If your custom widget is just a QWidget subclass with some children widgets, you could add a protected virtual method which only takes care of laying the children out, and a static factory method to instantiate the right subclass, depending on runtime configuration options.

Just make children instances as protected members, so that subclasses can access them, add the protectd customlayout pure virtual method, the public initialize method, and the static create method. In constructor, just setup the children widgets and connect signals and slots (don't mind about children position and visibility, here). Once called, the initialize method will set the layout returned by the virtual function as the custom widget layout.

class CustomWidget : public QWidget
{
    Q_OBJECT

protected:

    virtual QLayout * customlayout() = 0;

    QLabel a;
    QLineEdit b;
    QPushButton c;

public:
    CustomWidget(QWidget * p) : QWidget(p)
    {
        a.setText("Text:");
        c.setText("Ok");

        //setup signals/slot etc.

    }
    void initialize()
    {
        setLayout(customlayout());
    }

    static CustomWidget * create();
};

A couple of possible implementations:

#include <QHBoxLayout>

class XCustomWidget : public CustomWidget
{
public:
    XCustomWidget() : CustomWidget(nullptr){}
protected:
    QLayout * customlayout() override
    {
        QHBoxLayout * l = new QHBoxLayout();
        l->addWidget(&a);
        l->addWidget(&b);
        l->addWidget(&c);
        return l;
    }
};

#include <QVBoxLayout>

class YCustomWidget : public CustomWidget
{
public:
    YCustomWidget() : CustomWidget(nullptr){}
protected:
    QLayout * customlayout() override
    {
        QVBoxLayout * l = new QVBoxLayout();
        l->addWidget(&a);
        l->addWidget(&b);
        c.setVisible(false);
        l->addStretch(1);
        return l;
    }
};

A possible factory implementation:

CustomWidget *CustomWidget::create()
{
    //read configuration ...

    if(isApplicationX)
    {
        return new XCustomWidget();
    }

    if(isApplicationY)
    {
        return new YCustomWidget();
    }
}

And the instantiation (e.g. in a form constructor):

CustomWidget * w = CustomWidget::create();
w->initialize();
layout()->addWidget(w);