Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QVBoxLayout just stacking widgets on top of each other



  • I have a class named VerticalWidgetList, which is meant to use a QVBoxLayout to display widgets added to it in a list from top to bottom in the order the widgets were added. I'm having a problem with it, though, as when I add widgets using addWidget(), it instead seems to stack the widgets on top of each other. Here is the source code for the class. Am I setting this up right?

    verticalwidgetlist.h

    #ifndef VERTICALWIDGETLIST_H
    #define VERTICALWIDGETLIST_H
    
    #include <QScrollArea>
    #include <QVBoxLayout>
    #include <QWidgetList>
    
    class VerticalWidgetList : public QScrollArea {
        Q_OBJECT
    public:
        explicit VerticalWidgetList(QWidget *parent = nullptr);
    
        bool addWidget(QWidget *child);
        void clearWidgets();
        bool insertWidget(int index, QWidget *child);
        bool removeWidget(int index);
        bool removeWidgetAt(QWidget *child);
        QWidget *takeWidget(int index);
        int widgetAt(QWidget *child) const;
    
    private:
        QWidget *m_central;
        QVBoxLayout *m_layout;
        QWidgetList m_list;
    
    };
    
    #endif // VERTICALWIDGETLIST_H
    
    

    verticalwidgetlist.cpp

    #include "verticalwidgetlist.h"
    
    VerticalWidgetList::VerticalWidgetList(QWidget *parent)
        : QScrollArea(parent)
        , m_central(new QWidget)
        , m_layout(new QVBoxLayout(m_central))
        , m_list()
    {
        setWidget(m_central);
        setWidgetResizable(true);
    }
    
    bool VerticalWidgetList::addWidget(QWidget *child) {
        if(child == nullptr)
            return false;
        m_layout->addWidget(child);
        m_list.append(child);
        child->setParent(this);
        return true;
    }
    
    void VerticalWidgetList::clearWidgets() {
        while(m_list.count()) {
            QWidget *widget = m_list.takeAt(0);
            m_layout->removeWidget(widget);
        }
    }
    
    bool VerticalWidgetList::insertWidget(int index, QWidget *child) {
        if(index < 0 || index > m_list.count())
            return false;
        if(child == nullptr)
            return false;
        m_layout->insertWidget(index, child);
        m_list.insert(index, child);
        child->setParent(this);
        return true;
    }
    
    bool VerticalWidgetList::removeWidget(int index) {
        if(index < 0 || index >= m_list.count())
            return false;
        QWidget *widget = m_list.takeAt(index);
        m_layout->removeWidget(widget);
        delete widget;
        return true;
    }
    
    bool VerticalWidgetList::removeWidgetAt(QWidget *child) {
        if(child == nullptr)
            return false;
        m_layout->removeWidget(child);
        int index = widgetAt(child);
        m_list.removeAt(index);
        delete child;
        return true;
    }
    
    QWidget *VerticalWidgetList::takeWidget(int index) {
        if(index < 0 || index >= m_list.count())
            return nullptr;
        QWidget *widget = m_list.takeAt(index);
        m_layout->removeWidget(widget);
        widget->setParent(nullptr);
        return widget;
    }
    
    int VerticalWidgetList::widgetAt(QWidget *child) const {
        if(child == nullptr)
            return -1;
        return m_list.indexOf(child);
    }
    
    


  • @jsulm Fortunately, I just solved it. I should not have specified the VerticalWidgetList class as the parent of the widgets added. Apparently, when you add it to the layout, the widgets added get some sort of other parent class. I still nullptr out the widget in the takeWidget() function, however, as control of it should pass to the function receiving the return value. When I got rid of setting the parent in the addWidget() and insertWidget() functions, it now works properly.

    I do wonder if I should still delete the child widget in removeWidget() and removeWidgetAt() after removing it from the layout. Or do I need to set the parent to nullptr before deleting it? When my program used clearWidgets(), which calls removeWidget(), there was no problem.

    It works now!

    This is the source code now:

    verticalwidgetlist.h

    #ifndef VERTICALWIDGETLIST_H
    #define VERTICALWIDGETLIST_H
    
    #include <QScrollArea>
    #include <QVBoxLayout>
    #include <QWidgetList>
    
    class VerticalWidgetList : public QScrollArea {
        Q_OBJECT
    public:
        explicit VerticalWidgetList(QWidget *parent = nullptr);
    
        bool addWidget(QWidget *child);
        void clearWidgets();
        bool insertWidget(int index, QWidget *child);
        bool removeWidget(QWidget *child;
        bool removeWidgetAt(int index);
        QWidget *takeWidget(int index);
        int widgetAt(QWidget *child) const;
    
    private:
        QWidget *m_central;
        QVBoxLayout *m_layout;
        QWidgetList m_list;
    
    };
    
    #endif // VERTICALWIDGETLIST_H
    
    

    verticalwidgetlist.cpp

    #include "verticalwidgetlist.h"
    
    VerticalWidgetList::VerticalWidgetList(QWidget *parent)
        : QScrollArea(parent)
        , m_central(new QWidget)
        , m_layout(new QVBoxLayout(m_central))
        , m_list()
    {
        setWidget(m_central);
        setWidgetResizable(true);
    }
    
    bool VerticalWidgetList::addWidget(QWidget *child) {
        if(child == nullptr)
            return false;
        m_layout->addWidget(child);
        m_list.append(child);
        return true;
    }
    
    void VerticalWidgetList::clearWidgets() {
        while(m_list.count()) {
            QWidget *widget = m_list[0];
            removeWidget(widget);
        }
    }
    
    bool VerticalWidgetList::insertWidget(int index, QWidget *child) {
        if(index < 0 || index > m_list.count())
            return false;
        if(child == nullptr)
            return false;
        m_layout->insertWidget(index, child);
        m_list.insert(index, child);
        return true;
    }
    
    bool VerticalWidgetList::removeWidget(QWidget *child) {
        if(child == nullptr)
            return false;
        m_layout->removeWidget(child);
        int index = widgetAt(child);
        m_list.removeAt(index);
        delete child;
        return true;
    }
    
    bool VerticalWidgetList::removeWidgetAt(int index) {
        if(index < 0 || index >= m_list.count())
            return false;
        QWidget *widget = m_list.takeAt(index);
        m_layout->removeWidget(widget);
        delete widget;
        return true;
    }
    
    QWidget *VerticalWidgetList::takeWidget(int index) {
        if(index < 0 || index >= m_list.count())
            return nullptr;
        QWidget *widget = m_list.takeAt(index);
        m_layout->removeWidget(widget);
        widget->setParent(nullptr);
        return widget;
    }
    
    int VerticalWidgetList::widgetAt(QWidget *child) const {
        if(child == nullptr)
            return -1;
        return m_list.indexOf(child);
    }
    
    

    EDIT: If anyone tried the source code up until this point, in this post, try again. I was rearranging removeWidget() and removeWidgetAt(), which I realized were mismatched in terms of their parameters. It's good now!


  • Qt Champions 2019



  • @jsulm Fortunately, I just solved it. I should not have specified the VerticalWidgetList class as the parent of the widgets added. Apparently, when you add it to the layout, the widgets added get some sort of other parent class. I still nullptr out the widget in the takeWidget() function, however, as control of it should pass to the function receiving the return value. When I got rid of setting the parent in the addWidget() and insertWidget() functions, it now works properly.

    I do wonder if I should still delete the child widget in removeWidget() and removeWidgetAt() after removing it from the layout. Or do I need to set the parent to nullptr before deleting it? When my program used clearWidgets(), which calls removeWidget(), there was no problem.

    It works now!

    This is the source code now:

    verticalwidgetlist.h

    #ifndef VERTICALWIDGETLIST_H
    #define VERTICALWIDGETLIST_H
    
    #include <QScrollArea>
    #include <QVBoxLayout>
    #include <QWidgetList>
    
    class VerticalWidgetList : public QScrollArea {
        Q_OBJECT
    public:
        explicit VerticalWidgetList(QWidget *parent = nullptr);
    
        bool addWidget(QWidget *child);
        void clearWidgets();
        bool insertWidget(int index, QWidget *child);
        bool removeWidget(QWidget *child;
        bool removeWidgetAt(int index);
        QWidget *takeWidget(int index);
        int widgetAt(QWidget *child) const;
    
    private:
        QWidget *m_central;
        QVBoxLayout *m_layout;
        QWidgetList m_list;
    
    };
    
    #endif // VERTICALWIDGETLIST_H
    
    

    verticalwidgetlist.cpp

    #include "verticalwidgetlist.h"
    
    VerticalWidgetList::VerticalWidgetList(QWidget *parent)
        : QScrollArea(parent)
        , m_central(new QWidget)
        , m_layout(new QVBoxLayout(m_central))
        , m_list()
    {
        setWidget(m_central);
        setWidgetResizable(true);
    }
    
    bool VerticalWidgetList::addWidget(QWidget *child) {
        if(child == nullptr)
            return false;
        m_layout->addWidget(child);
        m_list.append(child);
        return true;
    }
    
    void VerticalWidgetList::clearWidgets() {
        while(m_list.count()) {
            QWidget *widget = m_list[0];
            removeWidget(widget);
        }
    }
    
    bool VerticalWidgetList::insertWidget(int index, QWidget *child) {
        if(index < 0 || index > m_list.count())
            return false;
        if(child == nullptr)
            return false;
        m_layout->insertWidget(index, child);
        m_list.insert(index, child);
        return true;
    }
    
    bool VerticalWidgetList::removeWidget(QWidget *child) {
        if(child == nullptr)
            return false;
        m_layout->removeWidget(child);
        int index = widgetAt(child);
        m_list.removeAt(index);
        delete child;
        return true;
    }
    
    bool VerticalWidgetList::removeWidgetAt(int index) {
        if(index < 0 || index >= m_list.count())
            return false;
        QWidget *widget = m_list.takeAt(index);
        m_layout->removeWidget(widget);
        delete widget;
        return true;
    }
    
    QWidget *VerticalWidgetList::takeWidget(int index) {
        if(index < 0 || index >= m_list.count())
            return nullptr;
        QWidget *widget = m_list.takeAt(index);
        m_layout->removeWidget(widget);
        widget->setParent(nullptr);
        return widget;
    }
    
    int VerticalWidgetList::widgetAt(QWidget *child) const {
        if(child == nullptr)
            return -1;
        return m_list.indexOf(child);
    }
    
    

    EDIT: If anyone tried the source code up until this point, in this post, try again. I was rearranging removeWidget() and removeWidgetAt(), which I realized were mismatched in terms of their parameters. It's good now!