[Solved]QScrollArea Squeezes Child Widgets instead of Expanding Scrollable Area



  • Hi all

    I have a program which uses QScrollArea. When i add widgets to QScrollArea i expect it to show scrollbar after widgets can't fit inside QScrollArea. But what happens is, QScrollArea squeezes the widgets.

    Here is my code:

    // Initialize variables
    m_vLayoutInner = new QVBoxLayout;
    m_spacer = new QSpacerItem(20, 20, QSizePolicy::Fixed, QSizePolicy::Expanding);
    QScrollArea * scrollArea = new QScrollArea;
    
    // Set properties
    scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    scrollArea->setWidgetResizable(true);
    
    // @todo There is a problem with scrollArea
    m_vLayoutInner->addSpacerItem(m_spacer);
    scrollArea->setLayout(m_vLayoutInner);
    
    // Set widths fixed
    scrollArea->setFixedWidth(275);
    
    // These lines are for testing purposes
    for(int i = 0; i < 36; i++)
    {
        m_vLayoutInner->insertLayout(0, new RInfoHolder(RInfoHolder::RInfoHolderType::RadioButton, "randomString"));
    }
    

    As you can understand, what i want is, being able to keep RInfoHolders at top and if they can't fit inside the area provided by QScrollArea then scrollbar showing up and scrolling down if user wants to find a certain one among those RInfoHolders

    I think i am passing over a little important detail :(



  • Does this help you out? link



  • @TheHawk said:

    Does this help you out? link

    I am trying to understand what is going on exactly. As far as i understand the mistake mentioned in the post is it lacks some of the necessery functionality in order for them to properly work with QScrollArea. Please correct me if i am wrong.

    However i am using RInfoHolder class which inherits from QHBoxLayout. Therefore it shouldn't be the cause of problem. Here is RInfoHolder:

    class RInfoHolder : public QHBoxLayout
    {
        Q_OBJECT
    
    public:
        enum RInfoHolderType : quint8
        {
            CheckBox,
            RadioButton
        };
    
        RInfoHolder(RInfoHolderType type, QString name, qint32 value = 0);
        ~RInfoHolder();
    
    signals:
        void valueChanged(QString name, qint32 value);
    
    public slots:
        void checkBoxValueChanged(bool isChecked);
        void selectedRadioButtonChanged(QAbstractButton * button);
    
    private:
        QString m_name;
    
    };
    

    and here is the source:

    RInfoHolder::RInfoHolder(RInfoHolderType type, QString name, qint32 value)
    {
        m_name = name;
        if(type == RInfoHolderType::CheckBox)
        {
            QLabel * label = new QLabel(name);
            QCheckBox * checkBox = new QCheckBox;
    
            checkBox->setCheckState(value > 0 ? Qt::Checked : Qt::Unchecked);
            label->setFixedWidth(150);
    
            this->addWidget(label);
            this->addWidget(checkBox);
    
            connect(checkBox, SIGNAL(toggled(bool))
                    , this, SLOT(checkBoxValueChanged(bool)));
        }
        else if(type == RInfoHolderType::RadioButton)
        {
            QLabel * label = new QLabel(name);
            QButtonGroup * buttonGroup = new QButtonGroup;
            QRadioButton * radioButton0 = new QRadioButton("0");
            QRadioButton * radioButton1 = new QRadioButton("1");
            QRadioButton * radioButton2 = new QRadioButton("2");
    
            label->setFixedWidth(175);
    
            buttonGroup->addButton(radioButton0);
            buttonGroup->addButton(radioButton1);
            buttonGroup->addButton(radioButton2);
            this->addWidget(label);
            this->addWidget(radioButton0);
            this->addWidget(radioButton1);
            this->addWidget(radioButton2);
            this->setAlignment(Qt::AlignLeft);
    
            for(QAbstractButton * button : buttonGroup->buttons())
            {
                if(button->text() == QString::number(value))
                    button->setChecked(true);
            }
    
            connect(buttonGroup, SIGNAL(buttonClicked(QAbstractButton*))
                    , this, SLOT(selectedRadioButtonChanged(QAbstractButton*)));
        }
    }
    
    RInfoHolder::~RInfoHolder()
    {  }
    
    void RInfoHolder::checkBoxValueChanged(bool isChecked)
    {
        emit valueChanged(m_name, static_cast<qint32>(isChecked));
    }
    
    void RInfoHolder::selectedRadioButtonChanged(QAbstractButton * button)
    {
        emit valueChanged(m_name, button->text().toInt());
    }
    

    The only thing this class provides is the ability to know the result of user' answer without knowing if there exists a QCheckBox or QRadioButton.

    Can you please eloborate on where should i focus?



  • Just so everyone can easily understand hiearchy of layouts without reading the code. First ScrollArea holds a VBoxLayout and this VBoxLayout holds many RInfoHolders which are derived from QHBoxLayout.



  • I dont see you add a top level layout for RInfoHolder. Could be that I missed it but I don't see it so quickly. Are you sure it has a top-level layout?



  • @TheHawk I think top level layout is only needed for the form itself. Correct me if i am wrong. Also RInfoHolder is meant to be inside other layouts which are child layouts of the top level layout.

    I found my mistake. Though not really sure why such a thing happens and i would love it if someone explained why.

    Anyways, here is the change i did and everything is fine now:
    Changed the initialization to this:

    QScrollArea * scrollArea = new QScrollArea;
    QWidget * scrollAreaWidgetContents = new QWidget;
    m_vLayoutInner = new QVBoxLayout(scrollAreaWidgetContents);
    

    Then

    scrollArea->setWidgetResizable(true);
    scrollArea->setWidget(scrollAreaWidgetContents);
    

    Calling these functions solved my problem. Especially setwidgetResizable is important.

    However i can not understand the reason why we need a widget in order for QScrollArea to work properly and just using a layout is not enough.

    I would appreciate it if someone were to enlighten me on that. Thanks in advance.


  • Lifetime Qt Champion

    Hi,

    It's all explained in the documentation:
    The QScrollArea class provides a scrolling view onto another widget.

    A scroll area is used to display the contents of a child widget within a frame. If the widget exceeds the size of the frame, the view can provide scroll bars so that the entire area of the child widget can be viewed. The child widget must be specified with setWidget().



  • @SGaist Thanks for the info.

    But then why there is setLayout() function. It is misleading, making people think(at least me) even without a widget, using a layout is enough.

    Anyways, thanks for the info.


  • Lifetime Qt Champion

    Because it still is a QWidget and setLayout is one of the base function


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.