Manually Implementing Custom Widget in MainWindow



  • I think I've created a circular dependency in my code, but I'm not sure if it really is circular dependency, and if it's even the correct way to do things.

    I know that it is possible to design a UI that can be instantiated in another UI. For example, a widget with a button that dynamically adds new buttons to the widget when pressed.

    0_1550090382257_Capture.PNG

    I've struggled to replicate this in source/ manually until today. But the way I've designed it confuses me somewhat because it seems like two widgets depend upon each other circularly.

    The following are the steps that lead to the circular dependency I'm referring to:

    1. In MainWindow::setup(), I create the containerWidget followed by the inCl class object which takes containerWidget as a param.
    2. In customComboClass::setup I basically set the layout to the containerWidget.
    3. Back in MainWindow::setup() I add containerWidget to mainLayout.
    4. Then I add inCl to containerWidget's layout. This is where I see the circle complete.

    mainwindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QVBoxLayout>
    #include <QDebug>
    
    #include "customcomboclass.h"
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    private:
        void setup();
    
        customComboClass *inCl;
    
        QPushButton *call_button;
        QVBoxLayout *mainLayout;
        QWidget *containerWidget;
        QWidget *mainWidget;
    };
    
    #endif // MAINWINDOW_H
    
    

    mainwindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent)
    {
        setup();
    }
    
    MainWindow::~MainWindow()
    {
    }
    
    void MainWindow::setup(){
        containerWidget = new QWidget(this);// Container widget for customComboClass widget object. Holds elements of inheriting class.
        inCl = new customComboClass(containerWidget);// Inheriting class object. Send containing widget as param.
    
        call_button = new QPushButton(this);
        call_button->setText("Make a Call");
    
        mainLayout = new QVBoxLayout();
        mainLayout->addWidget(call_button);
        mainLayout->addWidget(containerWidget);// Add containing widget to layout.
        containerWidget->layout()->addWidget(inCl);// Add inheritng class object to containing widget. Fill containing widget with elements from inheriting class object.
    
        mainWidget = new QWidget();
        mainWidget->setLayout(mainLayout);
        mainWidget->setMinimumSize(120,100);
        setCentralWidget(mainWidget);
    }
    
    

    customcomboclass.h

    #ifndef CUSTOMCOMBOCLASS_H
    #define CUSTOMCOMBOCLASS_H
    
    #include <QWidget>
    #include <QDebug>
    #include <QListWidget>
    #include <QListWidgetItem>
    #include <QHBoxLayout>
    #include <QPushButton>
    #include <QMessageBox>
    
    class customComboClass : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit customComboClass(QWidget *w, QWidget *parent = nullptr);
        ~customComboClass();
    
    public slots:
        void setup(QWidget *w);
    
    private:
        QStringList itemList = {"", "OFF", "Circuit", "Radio"};
        QPushButton *button;
        QListWidget *listWidget;
        QHBoxLayout *hLayout;
    };
    
    #endif // CUSTOMCOMBOCLASS_H
    
    

    customcomboclass.cpp

    #include "customcomboclass.h"
    #include "ui_customcomboclass.h"
    
    customComboClass::customComboClass(QWidget *tWidget, QWidget *parent) :
        QWidget(parent)
    {
        setup(tWidget);
    }
    
    customComboClass::~customComboClass()
    {
    }
    
    void customComboClass::setup(QWidget *w){
        button = new QPushButton();
        button->setText("Press Me");
        button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
    
        listWidget = new QListWidget();
        listWidget->addItems(itemList);
        listWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
        listWidget->setMaximumHeight(25);
    
        hLayout = new QHBoxLayout();
        hLayout->addWidget(listWidget);
        hLayout->addWidget(button);
    
        w->setLayout(hLayout);
    }
    
    
    
    

    Main.cpp

    #include "mainwindow.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
    
        return a.exec();
    }
    

  • Lifetime Qt Champion

    Hi,

    Usually, each widget is responsible for its own setup. They don't depend on another widget like you did. The container widget will create a new contained widget and add it to one of its layout for example. But the contained widget will not care about what the container widget is.



  • @SGaist

    I'm not exactly sure how to pass my custom widget to MainWindow.

    How would you recommend accomplishing this?


  • Lifetime Qt Champion

    You can use your custom widgets like any other widget. What exactly is troubling you ?



  • @SGaist

    My hangup is that I don't know the proper way to design a custom widget with it's elements (buttons, sliders, labels...) and layouts, and add it to another widget, such as the MainWindow.

    I've made custom widget classes that basically look like this:

    button;
    vertical_layout;
    vertical_layout->addWidget(button);
    custom_widget;
    custom_widget->setLayout(vertical_layout);
    

    Then create an object of that class in MainWindow, and add it to MainWindow's layout. But sometimes I've gotten strange errors, and I've always thought that this way isn't the right way.

    I'm just trying to learn what is the best way to make a custom widget and add it to MainWindow.


  • Lifetime Qt Champion

    You can use designer or do it by code only. That's up to you.

    What strange errors do you get ?



  • I don't always get errors. Typically the widget simply doesn't display.

    Maybe it would help if I include a simpler project as an example.

    MainWindow.h

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QDialog>
    #include <QPushButton>
    #include <QLineEdit>
    #include <QMessageBox>
    #include <QVBoxLayout>
    #include <QHBoxLayout>
    #include <QGridLayout>
    #include <QPixmap>
    #include <QIcon>
    #include <QDebug>
    
    #include "customwidgetclass.h"
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    private slots:
        void buttonClick();
    
    private:
        void setup();
    
        QDialog *dialog = new QDialog(this);
    
        customWidgetClass *CWC;
    
    
        QPushButton *button;
    
        QVBoxLayout *vLayout;
        QWidget *mainWidget;
    };
    
    #endif // MAINWINDOW_H
    
    

    MainWindow.cpp

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent)
    {
        setup();
        CWC = new customWidgetClass();
        connect(button, &QPushButton::released, this, &MainWindow::buttonClick);
    }
    
    MainWindow::~MainWindow()
    {
    }
    
    void MainWindow::setup(){
        button = new QPushButton();
        button->setText("Press me");
    
        vLayout = new QVBoxLayout();
        vLayout->addWidget(button);
        vLayout->addWidget(CWC);
    
        mainWidget = new QWidget();
        setCentralWidget(mainWidget);
        mainWidget->setLayout(vLayout);
    }
    
    void MainWindow::buttonClick(){
    }
    
    

    CustomWidgeClass.h

    #ifndef CUSTOMWIDGETCLASS_H
    #define CUSTOMWIDGETCLASS_H
    
    #include <QWidget>
    #include <QDialog>
    #include <QPushButton>
    #include <QHBoxLayout>
    #include <QLabel>
    #include <QDebug>
    
    class customWidgetClass
    {
    public:
        customWidgetClass();
    
    private:
        void setup();
    
        QLabel *label;
        QLabel *label2;
        QPushButton *button;
        QHBoxLayout *hLayout;
        QDialog *dialog;
    };
    
    #endif // CUSTOMWIDGETCLASS_H
    
    

    CustomWidgetClass.cpp

    #include "customwidgetclass.h"
    
    customWidgetClass::customWidgetClass()
    {
        setup();
    }
    
    void customWidgetClass::setup(){
        label = new QLabel();
        label->setText("Label1");
    
        label2 = new QLabel();
        label2->setText("Label2");
    
        button = new QPushButton();
        button->setText("custom button");
    
        hLayout = new QHBoxLayout();
        hLayout->addWidget(label);
        hLayout->addWidget(button);
        hLayout->addWidget(label2);
    
        dialog = new QDialog();
        dialog->setLayout(hLayout);
    }
    
    

    In this project, I do get an error; in MainWindow.cpp, *vLayout->addWidget(CWC); CANNOT INITIALIZE A PARAMETER OF TYPE 'QWidget *' WITH AN LVALUE OF TYPE 'customWidgetClass '


  • Lifetime Qt Champion

    @Burke212 said in Manually Implementing Custom Widget in MainWindow:

    class customWidgetClass

    Well its not a QWidget as you don't inherited from it. :)
    like
    class customWidgetClass : public QWidget

    so layout wont accept it.
    It only likes QWidget subclasses.



  • @mrjj

    You are correct. I can't believe I missed that!

    However, after making the changes, the app still doesn't display any of the elements from customWidetClass. Only the button from MainWindow displays.

    I've added the code to inherit from the QWidget class in customWidgetClass, and added *QWidget parent = nullptr to the constructor.



  • dialog = new QDialog();
    dialog->setLayout(hLayout);
    

    What is that ?



  • @mpergand

    As I understand it, custom widgets use a widget, dialog, or other element to sort of group all of the elements together. So I'm using dialog to group the hLayout, button, label, & label2 in the custom widget class.

    Is that not the way to do things?



  • dialog->show();
    

    Add this and you will see your buttons :)

    I guess that's not what you want, right ?
    Why do you create a Dialog ?
    if you want the buttons to be in your custom widget, simply put the layout in it, that's all !

    Anyway, your code is wrong:

    setup();
    CWC = new customWidgetClass();
    connect(button, &QPushButton::released, this, &MainWindow::buttonClick);
    

    in setup your add CWC in the layout, but you instantiate it later !
    Should crash for sure ;)


  • Moderators

    @Burke212
    what @mpergand meant and rightfully pointed out, you create a new QDialog and sign the layout to that.
    But that is not what you want to do!

    this should do what you want it to:

    void customWidgetClass::setup(){
        label = new QLabel();
        label->setText("Label1");
    
        label2 = new QLabel();
        label2->setText("Label2");
    
        button = new QPushButton();
        button->setText("custom button");
    
        hLayout = new QHBoxLayout();
        hLayout->addWidget(label);
        hLayout->addWidget(button);
        hLayout->addWidget(label2);
    
        setLayout(hLayout); // assigns the layout to your newly created object derived of QWidget I presume.
    }
    


  • @mpergand @J-Hilk

    That's interesting. I had always thought that custom widgets needed an element to group the class together.

    Now, when I make your suggested changes, the output says "QLayout: Cannot add a null widget to QVBoxLayout".

    What would cause CWC to return as a null widget?


  • Moderators

    @Burke212

    change

    setup();
    CWC = new customWidgetClass();
    

    to

    CWC = new customWidgetClass();
    setup();
    

    hopefully you did initialize CWC as nullptr, otherwise that would have crashed in release mode 10/10 times.