0

I am interested in better understanding Qt and for that I would like to see how a relatively complex example of nested layouts and multiples widgets GUI is done solely programmatically; i.e. without the use of Qt Creator's Design.

I have seen several example code demonsrating only very simple layouts and -almost- always without utilising the header files; in fact most of the examples I have seen, dump the code in the main. It is my understanding (please correct me if I am wrong) that good programming practices dictate that you separate the design in a separate class (e.g. mainwindow) and in the main.cpp you merely invoke and show that main window. Perhaps, most crucially, all the definitions of widgets, layouts etc. should be defined in the header file and then invoked and customised in the mainwindow.cpp.

A fine exammple of good programming practices programmatically but for a relatively simple GUI desing can be seen here.

If anyone can provide online resource(s) where non-simple code examples of good programmatical GUI designs that show how to properly define and customise nested layouts and widgets that would be very much appreciated.

Alternatively, here is a straightforward programmatical GUI design example for which it would be great to derive code (without the Designer) for me to study and gain understanding from:

note: my focus is only on the nested layouts, multiple widgets and the where and how these should be defined and invoked on the different Qt source files.

enter image description here

where the nested layout and widget specifics can be seen here:

enter image description here

with the actual GUI result as seen here:

enter image description here

UPDATE

As Mike suggested in the comments section, from the uic the code for what the Designer does can be seen, which in my case for the example mentioned above is the following

>uic mainwindow.ui outputs:

#ifndef UI_MAINWINDOW_H
#define UI_MAINWINDOW_H

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QFrame>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QProgressBar>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QSpacerItem>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_MainWindow
{
public:
    QWidget *centralWidget;
    QVBoxLayout *verticalLayout_3;
    QFrame *top_frame;
    QGridLayout *gridLayout;
    QSpacerItem *horizontalSpacer;
    QProgressBar *progressBar_2;
    QProgressBar *progressBar_4;
    QSpacerItem *verticalSpacer;
    QSpacerItem *horizontalSpacer_2;
    QPushButton *pushButton_1;
    QProgressBar *progressBar_1;
    QPushButton *pushButton_2;
    QPushButton *pushButton_3;
    QProgressBar *progressBar_3;
    QPushButton *pushButton_4;
    QFrame *bottom_frame;
    QHBoxLayout *horizontalLayout;
    QFrame *frame;
    QFormLayout *formLayout;
    QLabel *label_1;
    QLabel *label_2;
    QSpacerItem *verticalSpacer_2;
    QLineEdit *lineEdit;
    QLineEdit *lineEdit_2;
    QFrame *frame_2;
    QHBoxLayout *horizontalLayout_2;
    QLabel *label_3;
    QSpacerItem *horizontalSpacer_3;
    QLabel *label_4;

    void setupUi(QMainWindow *MainWindow)
    {
        if (MainWindow->objectName().isEmpty())
            MainWindow->setObjectName(QStringLiteral("MainWindow"));
        MainWindow->resize(878, 632);
        centralWidget = new QWidget(MainWindow);
        centralWidget->setObjectName(QStringLiteral("centralWidget"));
        verticalLayout_3 = new QVBoxLayout(centralWidget);
        verticalLayout_3->setSpacing(6);
        verticalLayout_3->setContentsMargins(11, 11, 11, 11);
        verticalLayout_3->setObjectName(QStringLiteral("verticalLayout_3"));
        top_frame = new QFrame(centralWidget);
        top_frame->setObjectName(QStringLiteral("top_frame"));
        QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
        sizePolicy.setHorizontalStretch(0);
        sizePolicy.setVerticalStretch(0);
        sizePolicy.setHeightForWidth(top_frame->sizePolicy().hasHeightForWidth());
        top_frame->setSizePolicy(sizePolicy);
        top_frame->setFrameShape(QFrame::StyledPanel);
        top_frame->setFrameShadow(QFrame::Raised);
        gridLayout = new QGridLayout(top_frame);
        gridLayout->setSpacing(6);
        gridLayout->setContentsMargins(11, 11, 11, 11);
        gridLayout->setObjectName(QStringLiteral("gridLayout"));
        horizontalSpacer = new QSpacerItem(30, 20, QSizePolicy::Fixed, QSizePolicy::Minimum);

        gridLayout->addItem(horizontalSpacer, 0, 2, 1, 1);

        progressBar_2 = new QProgressBar(top_frame);
        progressBar_2->setObjectName(QStringLiteral("progressBar_2"));
        progressBar_2->setValue(24);

        gridLayout->addWidget(progressBar_2, 2, 3, 1, 1);

        progressBar_4 = new QProgressBar(top_frame);
        progressBar_4->setObjectName(QStringLiteral("progressBar_4"));
        progressBar_4->setValue(24);

        gridLayout->addWidget(progressBar_4, 2, 5, 1, 1);

        verticalSpacer = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Fixed);

        gridLayout->addItem(verticalSpacer, 1, 1, 1, 1);

        horizontalSpacer_2 = new QSpacerItem(10, 20, QSizePolicy::Fixed, QSizePolicy::Minimum);

        gridLayout->addItem(horizontalSpacer_2, 0, 4, 1, 1);

        pushButton_1 = new QPushButton(top_frame);
        pushButton_1->setObjectName(QStringLiteral("pushButton_1"));

        gridLayout->addWidget(pushButton_1, 0, 0, 1, 1);

        progressBar_1 = new QProgressBar(top_frame);
        progressBar_1->setObjectName(QStringLiteral("progressBar_1"));
        progressBar_1->setValue(24);

        gridLayout->addWidget(progressBar_1, 0, 3, 1, 1);

        pushButton_2 = new QPushButton(top_frame);
        pushButton_2->setObjectName(QStringLiteral("pushButton_2"));

        gridLayout->addWidget(pushButton_2, 0, 1, 1, 1);

        pushButton_3 = new QPushButton(top_frame);
        pushButton_3->setObjectName(QStringLiteral("pushButton_3"));

        gridLayout->addWidget(pushButton_3, 2, 0, 1, 1);

        progressBar_3 = new QProgressBar(top_frame);
        progressBar_3->setObjectName(QStringLiteral("progressBar_3"));
        progressBar_3->setValue(24);

        gridLayout->addWidget(progressBar_3, 0, 5, 1, 1);

        pushButton_4 = new QPushButton(top_frame);
        pushButton_4->setObjectName(QStringLiteral("pushButton_4"));

        gridLayout->addWidget(pushButton_4, 2, 1, 1, 1);


        verticalLayout_3->addWidget(top_frame);

        bottom_frame = new QFrame(centralWidget);
        bottom_frame->setObjectName(QStringLiteral("bottom_frame"));
        bottom_frame->setFrameShape(QFrame::StyledPanel);
        bottom_frame->setFrameShadow(QFrame::Raised);
        horizontalLayout = new QHBoxLayout(bottom_frame);
        horizontalLayout->setSpacing(6);
        horizontalLayout->setContentsMargins(11, 11, 11, 11);
        horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
        frame = new QFrame(bottom_frame);
        frame->setObjectName(QStringLiteral("frame"));
        frame->setFrameShape(QFrame::StyledPanel);
        frame->setFrameShadow(QFrame::Raised);
        formLayout = new QFormLayout(frame);
        formLayout->setSpacing(6);
        formLayout->setContentsMargins(11, 11, 11, 11);
        formLayout->setObjectName(QStringLiteral("formLayout"));
        label_1 = new QLabel(frame);
        label_1->setObjectName(QStringLiteral("label_1"));

        formLayout->setWidget(0, QFormLayout::LabelRole, label_1);

        label_2 = new QLabel(frame);
        label_2->setObjectName(QStringLiteral("label_2"));

        formLayout->setWidget(1, QFormLayout::LabelRole, label_2);

        verticalSpacer_2 = new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);

        formLayout->setItem(2, QFormLayout::LabelRole, verticalSpacer_2);

        lineEdit = new QLineEdit(frame);
        lineEdit->setObjectName(QStringLiteral("lineEdit"));
        QSizePolicy sizePolicy1(QSizePolicy::Expanding, QSizePolicy::Expanding);
        sizePolicy1.setHorizontalStretch(0);
        sizePolicy1.setVerticalStretch(0);
        sizePolicy1.setHeightForWidth(lineEdit->sizePolicy().hasHeightForWidth());
        lineEdit->setSizePolicy(sizePolicy1);

        formLayout->setWidget(3, QFormLayout::SpanningRole, lineEdit);

        lineEdit_2 = new QLineEdit(frame);
        lineEdit_2->setObjectName(QStringLiteral("lineEdit_2"));
        sizePolicy1.setHeightForWidth(lineEdit_2->sizePolicy().hasHeightForWidth());
        lineEdit_2->setSizePolicy(sizePolicy1);

        formLayout->setWidget(4, QFormLayout::LabelRole, lineEdit_2);


        horizontalLayout->addWidget(frame);

        frame_2 = new QFrame(bottom_frame);
        frame_2->setObjectName(QStringLiteral("frame_2"));
        frame_2->setFrameShape(QFrame::StyledPanel);
        frame_2->setFrameShadow(QFrame::Raised);
        horizontalLayout_2 = new QHBoxLayout(frame_2);
        horizontalLayout_2->setSpacing(6);
        horizontalLayout_2->setContentsMargins(11, 11, 11, 11);
        horizontalLayout_2->setObjectName(QStringLiteral("horizontalLayout_2"));
        label_3 = new QLabel(frame_2);
        label_3->setObjectName(QStringLiteral("label_3"));

        horizontalLayout_2->addWidget(label_3);

        horizontalSpacer_3 = new QSpacerItem(40, 20, QSizePolicy::Fixed, QSizePolicy::Minimum);

        horizontalLayout_2->addItem(horizontalSpacer_3);

        label_4 = new QLabel(frame_2);
        label_4->setObjectName(QStringLiteral("label_4"));

        horizontalLayout_2->addWidget(label_4);

        frame->raise();
        frame->raise();
        label_3->raise();
        label_4->raise();

        horizontalLayout->addWidget(frame_2);


        verticalLayout_3->addWidget(bottom_frame);

        MainWindow->setCentralWidget(centralWidget);

        retranslateUi(MainWindow);

        QMetaObject::connectSlotsByName(MainWindow);
    } // setupUi

    void retranslateUi(QMainWindow *MainWindow)
    {
        MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", 0));
        pushButton_1->setText(QApplication::translate("MainWindow", "PushButton_1", 0));
        pushButton_2->setText(QApplication::translate("MainWindow", "PushButton_2", 0));
        pushButton_3->setText(QApplication::translate("MainWindow", "PushButton_3", 0));
        pushButton_4->setText(QApplication::translate("MainWindow", "PushButton_4", 0));
        label_1->setText(QApplication::translate("MainWindow", "TextLabel_1", 0));
        label_2->setText(QApplication::translate("MainWindow", "TextLabel_2", 0));
        label_3->setText(QApplication::translate("MainWindow", "TextLabel_3", 0));
        label_4->setText(QApplication::translate("MainWindow", "TextLabel_4", 0));
    } // retranslateUi

};

namespace Ui {
    class MainWindow: public Ui_MainWindow {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_MAINWINDOW_H
Community
  • 1
  • 1
  • 1
    The recommended practice is avoid huge monolithic GUIs, organize elements into smaller widgets and combine them, especially if they can be repeated such as your two columns each of 2 buttons and 2 progress bars. Also the question is too broad and primarily opinion based. – dtech Sep 23 '16 at 21:52
  • @ddriver I put effort not to make the question broad and certainly not opinion-based although I see what you mean and you do have a point. Nevertheless, I am looking for a small chunk of code demonstrating a few definitions and invocations specifically to demonstrate Qt nested layouts with multiple widgets for me to learn; no proper examples online as far as I can tell. –  Sep 23 '16 at 21:57
  • You nest any widget just like you nest a plain `QWidget`. Make a `TwoButtons : public QWidget` class and you have a widget that's two buttons, put 2 of whose in another widget, and you have a widget with 4 buttons. Just follow the general Qt programming guidelines - properties, signals and slots interfaces for your widgets to make them truly modular. – dtech Sep 23 '16 at 22:01
  • @If you could provide a full (no matter how small) code to support your description it would be great for me to study. –  Sep 23 '16 at 22:04
  • @nk-fford , organizing elements into smaller widgets and combining them into building blocks are mentioned by ddriver is recommended even when using the designer. You can have a form for a separate complicated widget and re-use that form in the designer in your main window or in a dialog as long as you wish. – Mike Sep 23 '16 at 22:12
  • So, are you looking for C++ code for your GUI? or are you asking for what components you should separate in the GUI you show? – Mike Sep 23 '16 at 22:16
  • @Mike I look for a good full example of code that imitates the nested layout and multiple widgets design in the GUI description. My aim is to see how we can programmatically design nested layouts and widgets using the header and the source files in C++ Qt. –  Sep 23 '16 at 22:21
  • the [Qt User Interface](https://doc.qt.io/qt-5/uic.html) automatically generates C++ code for the XML generated by the Qt designer. you can have a look at that automatically generated code for the GUI you show in your screenshot, have a look [here](https://stackoverflow.com/a/36512581/2666212). You can add as many nested layouts as you want in the designer and see how does that translate in C++. – Mike Sep 23 '16 at 22:31
  • @Mike Wow, I had no idea that such a tool existed. Will try it now. If you care to provide an answer with such a code I will be happy to accept it. –  Sep 23 '16 at 22:36
  • @nk-fford , this *tool* is how Qt works, this is how the GUI you design in the designer is compiled into the result executable. It is just translated into C++. that's why you call `ui->setupUi(this)` in your window's constructor. – Mike Sep 23 '16 at 22:42
  • @Mike I studied the output of the `Qt User Interface`; very useful. However, the part of my question for which I seeking advice/code example to demonstrate how such code should be separated on both the `.h` and `.cpp` source files still stands; I pressume it is not good practice (objectively not based on opinions) to have all the GUI on the `.cpp` and not put the definitions on the `.h`. Could you care to provide an example snippet of such a separation? –  Sep 23 '16 at 23:12
  • @nk-fford. If the output of `uic` somehow doesn't satisfy your requirements, there are dozens of [example programs in the qt docs](http://doc.qt.io/qt-5/qtexamplesandtutorials.html) that may do so. – ekhumoro Sep 24 '16 at 12:52

1 Answers1

1

Below is a transformation that you might wish to perform. I've included a shortened version of your code to demonstrate.

  1. Start with uic output.

    class Ui_MainWindow
    {
    public:
        QWidget *centralWidget;
        QVBoxLayout *verticalLayout_3;
        QFrame *top_frame;
    };
    
  2. Put all members of Ui_Foo into Foo itself, retaining their order.

    class MainWindow : public QMainWindow
    {
        QWidget *centralWidget;
        QVBoxLayout *verticalLayout_3;
        QFrame *top_frame;
    };
    
  3. Change member types from pointers to values.

    class MainWindow : public QMainWindow
    {
        QWidget centralWidget;
        QVBoxLayout verticalLayout_3;
        QFrame top_frame;
        ...
    };
    
  4. Initialize layouts in the class declaration by giving them the widgets they act on. This makes the structure of the user interface all obvious from the header itself:

    class MainWindow : public QMainWindow
    {
        QWidget centralWidget;
        QVBoxLayout verticalLayout_3{&centralWidget};
        QFrame top_frame;
        ...
    };
    
  5. Add code from setupUi to the constructor's body, changing it appropriately to act on values, not pointers:

    MainWindow::MainWindow(QWidget * parent) : QWidget{parent}
    {
        setObjectName(QStringLiteral("MainWindow"));
        resize(878, 632);
        centralWidget.setObjectName(QStringLiteral("centralWidget"));
        verticalLayout_3.setSpacing(6);
        verticalLayout_3.setContentsMargins(11, 11, 11, 11);
        verticalLayout_3.setObjectName(QStringLiteral("verticalLayout_3"));
        top_frame.setObjectName(QStringLiteral("top_frame"));
        QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
        sizePolicy.setHorizontalStretch(0);
        sizePolicy.setVerticalStretch(0);
        sizePolicy.setHeightForWidth(top_frame->sizePolicy().hasHeightForWidth());
        top_frame.setSizePolicy(sizePolicy);
        top_frame.setFrameShape(QFrame::StyledPanel);
        top_frame.setFrameShadow(QFrame::Raised);
        ...
    }
    
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Do you know why if there is a `QSpacerItem` in the ui (e.g. `QSpacerItem *verticalSpacer;` then if I follow your guide and put it in `class MainWindow : public QWidget` from pointers to values I get the error `C2512: 'QSpacerItem': no appropriate default constructor available`. Any suggestions why and how to avoid? –  Sep 27 '16 at 21:04
  • If you use a compiler that supports uniform member initialization, the you should initialize the spacer (and all other members!) in its declaration: `QSpacerItem foo{....}`, where `....` are the arguments that its constructor demands. If you use an older compiler, you need to initialize it in the [member initializer list](http://stackoverflow.com/a/8523361/1329652). – Kuba hasn't forgotten Monica Sep 27 '16 at 22:49