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

Controlling the size of a QScrollArea



  • Hey.
    Consider the following:
    0_1551297190986_6fd3253b-1cb0-4a1b-9659-0e7704be9b7f-image.png
    Where Widget C is a parent of Widget A & Widget B and installed with QVBoxLayout, and all are subclasses of QWidget.

    Now, I want Widget B to take more space in height, but not on the expense of Widget A.
    Something along the line of:
    0_1551297235108_ffe937db-65d2-4125-8819-d471f1957fc8-image.png

    I tried re-implementing Widget B's QSize sizeHint()const:

    QSize WidgetB::sizeHint()const {
        QSize parentSize(QWidget::sizeHint());
        return QSize(parentSize.width(), parentSize.height() + 500);
    }
    

    Unfortunately, this had no effect.

    On the other hand, re-implementing QSize minimumSizeHint()const did the trick:

    QSize WidgetB::minimumSizeHint()const {
        QSize parentSize(QWidget::minimumSizeHint());
        return QSize(parentSize.width(), parentSize.height() + 500);
    }
    

    But this is not a good solution for me, since I cannot resize Widget C (top level widget) because of Widget B minimum requirement.

    I want Widget B's size to start off with extended height, and let the user choose if they want to reduce Widget B height by resizing Widget C (which is the top level window).

    So; why is QSize sizeHint()const being ignored by Widget C's QVBoxLayout?
    How can I achieve the desired effect?

    Thanks.


  • Lifetime Qt Champion

    Hi,

    Why not use the stretch parameter when calling addWidget ?



  • @SGaist ok, I tried using stretch but this had no effect...

        QVBoxLayout *mainLayout = new QVBoxLayout;
        mainLayout->addWidget(_mainPanel);
        mainLayout->addWidget(_bottomPanel, 2);
        setLayout(mainLayout);
    

    could it be related to some of the properties of _mainPanel (aka "Widget A") and _bottomPanel (aka "Widget B")?
    what am I missing here?

    Thanks.


  • Lifetime Qt Champion

    Did you keep your sizeHint overloads ?



  • @SGaist OK! Progress! :)
    Indeed, I had to keep the overload for the sizeHint() and so I did, and now

    QVBoxLayout *mainLayout = new QVBoxLayout;
        mainLayout->addWidget(_mainPanel);
        mainLayout->addWidget(_bottomPanel, 2);
        setLayout(mainLayout);
    

    Together with this in _bottomPanel:

    QSize BottomPanel::sizeHint()const {
        QSize parentSize(QWidget::sizeHint());
        return QSize(parentSize.width(), parentSize.height() + 500);
    }
    

    Caused BottomPanel to be higher, but not by as much as I'd like.
    So I tried increasing:

    mainLayout->addWidget(_bottomPanel, 4); // now it's 4
    

    and:

    QSize BottomPanel::sizeHint()const {
        QSize parentSize(QWidget::sizeHint());
        return QSize(parentSize.width(), parentSize.height() + 800); // now it's 800
    }
    

    and it didn't grew further.
    Can you please explain how exactly does the factor works?
    The doc doesn't say much:

    The stretch factor applies only in the direction of the QBoxLayout, and is relative to the other boxes and widgets in this QBoxLayout. Widgets and boxes with higher stretch factors grow more.

    If the stretch factor is 0 and nothing else in the QBoxLayout has a stretch factor greater than zero, the space is distributed according to the QWidget:sizePolicy() of each widget that's involved.

    Nothing about the need to also re-implement sizeHint()...

    Thanks!


  • Lifetime Qt Champion

    The thing is, you are putting your widgets in a QScrollArea. So depending on how you did configure things, the growth of your widgets might be impacted.

    You should first get your widgets working as you want without the scroll area and then add it back to the mix.



  • @SGaist sorry, my bad.
    I should have mentioned (I was sure I did but apparently didn't), that in the pictures above, WidgetB is a subcalss of QScrollArea, not WidgetC.
    Why does the layout care which QWidget is it?


  • Lifetime Qt Champion

    QScrollArea is not used like that. You should your widgets in a "container" QWidget that you give to QScrollArea using setWidget.



  • @SGaist thanks for the consultant. Appreciate it.

    Let me please start over, I will be more specific.

    So what I currently have is this:

    0_1551429827541_fd897439-cf06-47c2-91cb-5c48247b090b-image.png

    QWidgetBlack installed QVBoxLayout and added two widgets: QWidgetRedTop and QWidgetRedBottom.
    QWidgetRedBottom installed QVBoxLayout and added QScrollAreaBlue
    QScrollAreaBlue added QWidgetGreen using QScrollAreaBlue::setWidget(QWidgetGreen) (this here)

    Now, what I want is the app to start like this:

    0_1551430438217_5255f0eb-6b38-4a5f-aefc-4c053c36d890-image.png

    So, following your first advice, I did:
    On QWidgetBlack:

    QVboxLayout* vbox = new QVBoxLayout;
    vbox->addWidget(QWidgetRedTop); 
    vbox->addWidget(QWidgetRedBottom, 1); // with stretch
    

    On QWidgetRedBottom:

    QSize QWidgetRedBottom::sizeHint()const { // override
        QSize s = QWidget::sizeHint();
        return QSize(s.width(), s.height() + 600);
    }
    

    And indeed it stretched out the way I wanted, but not enough.
    increasing both:

    vbox->addWidget(QWidgetRedBottom, 2); // with bigger stretch
    

    and

    QSize QWidgetRedBottom::sizeHint()const { // override
        QSize s = QWidget::sizeHint();
        return QSize(s.width(), s.height() + 800); // now 800 instead of 600
    }
    

    did not make it grow any further.

    Why is that? What am I doing wrong?

    Thanks.



  • I think I found a similar question here, that also solves my predicament.

    Quoting from there, the relevant part is:

    So the most you can do is to define sizeHint and minimumSizeHint, set a stretch factor greater than all the others, and set a size-policy of Expanding. But even with all these things in place, it seems the layout manager is still not guaranteed to honour the size-hint when the minimum-size-hint is less.
    The problem is that the definition of Expanding is not quite strong enough:

    The sizeHint() is a sensible size, but the widget can be shrunk and still be useful. The widget can make use of extra space, so it should get as much space as possible (e.g. the horizontal direction of a horizontal slider).

    For whatever reason, the layout manager seems to prioritise shrinking over expanding if another widget has a stronger size-policy (e.g. Fixed). This looks like the wrong choice from the point of view of your example, but I suppose there may be other situations where it makes more sense. The behaviour seems to be exactly the same in both Qt4 and Qt5, so I don't know whether it should be considered a bug, or just a quirk of the implementation.

    So I used the same workaround:
    In QWidgetBlack:

    QVboxLayout* vbox = new QVBoxLayout;
    vbox->addWidget(QWidgetRedTop); 
    vbox->addWidget(QWidgetRedBottom); // no need for stretch...
    setLayout(vbox);
    
    /* WA */
    QWidgetRedBottom->setMinimumSize(QWidgetRedBottom->sizeHint());
    adjustSize();
    QWidgetRedBottom->setMinimumSize(QWidgetRedBottom->minimumSizeHint());
    /******/
    

    and it totally worked!

    If anyone has comments/suggestions for a different solution I'd be happy to learn.


Log in to reply