QScrollArea does not scroll to maximum after widgets have been added
Asked Answered
F

6

6

My setup looks like this:

Main Window
  |- QHBoxLayout
       |- Some Widgets
       |- QScrollArea
       |    |- QHBoxLayout
       |         |- QGridLayout
       |              |- Widgets dynamically loaded in at runtime
       |         |- QVBoxLayout
       |- Some Widgets

After I added in Widgets I want the Scroll Area to completely scroll down. I did this by:

this->ui->gridLayout_table->addWidget(/*Widget*/); // this works perfectly nice
this->ui->gridLayout_table->addWidget(/*Widget*/); // this also works perfectly nice
this->ui->scrollArea->verticalScrollBar()->setValue(this->ui->scrollArea->verticalScrollBar()->maximum());

At the last command it scrolls to maximum minus the height of the newly added widgets. Is there any way to flush the changes before scrolling?

Thanks in advance Lukas

Feat answered 7/11, 2013 at 12:47 Comment(1)
I filed a bug report: bugreports.qt-project.org/browse/QTBUG-35250Lennon
A
10

Python version for the rangeChanged method:

scroll_area = QtGui.QScrollArea()
scroll_bar = scroll_area.verticalScrollBar()
scroll_bar.rangeChanged.connect(lambda: scroll_bar.setValue(scroll_bar.maximum()))

By connecting the rangeChanged method to the scrolldown, it ensures that each time there is a change in range the scrollbar will scroll down to the max.

Source

Annunciate answered 15/2, 2017 at 17:23 Comment(0)
S
4

I've had the same problem. I found a work-around, which might be useful for you as well.

Using scrollBar->setValue(scrollBar->maximum()); does not work when being used right after having added widgets to the scrolled area. The maximum value of the scroll bar simply is not updated yet.

However it does work if you have a button that the user has to click just to adjust the scroll bar's position to the maximum. It seems that the scroll area notifies its scroll bar asynchronously (through signal) only. So I simply added a hidden button pb_AdjustScrollBar to my widget and simulated a click, using the animateClick(int) method after having added widgets to the scrolled area. Once the signal clicked(bool) is received for the AdjustScrollBar button, I then trigger the scrollBar to use the maximum position with:

scrollBarPtr->triggerAction(QAbstractSlider::SliderToMaximum);

Notes: the animateClick timeout must be set to something like 100ms (default). Putting the timeout much shorter has caused the scrollBar not to update to its real maximum. However I guess 100ms is fast enough for user interactions anyhow.

Still I would be curious to know, how this issue can be solved in a smarter way.

Schorl answered 1/6, 2014 at 10:16 Comment(2)
Almost 2 years later, I'm also facing this problem. The work around proposed here by famoses may not be the cleanest way for having this feature working properly, but it does the job... Thanks!Treat
This worked for me, thanks!! None of the answers all over the internet mention this solution, surprising.Semela
B
2

I found out the way. After add a widget put: scroll->widget()->resize(scroll->widget()->sizeHint()); qApp->processEvents();

Bybidder answered 5/8, 2015 at 11:26 Comment(1)
thx, works for me. But I replace resize call with scroll->widget()->adjustSize()Tanyatanzania
C
2

One workaround is to use the rangeChanged(int,int) signal of the vertical scroll bar.

#include <QWidget>
#include <QBoxLayout>
#include <QScrollArea>
#include <QScrollBar>

class myClass
{
    Q_OBJECT
public:
    myClass();
private:
    QScrollArea *scrollArea;
    int scrollBarMax = 0;
private slots:
    void scrollToBottom(int min, int max);
};

myClass::myClass()
{
    QVBoxLayout *myLayout = new QVBoxLayout;
    QWidget *myWidget = new QWidget;
    myWidget->setLayout(myLayout);
    scrollArea = new QScrollArea;
    scrollArea->setWidget(myWidget);
    scrollArea->setWidgetResizeable(true);

    QObject::connect(scrollArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(scrollToBottom(int,int)));
}

void myClass::scrollToBottom(int min, int max)
{
    (void) min;
    if (max > scrollBarMax) scrollArea->verticalScrollBar()->setValue(max);
    scrollBarMax = max;
}
Colored answered 3/2, 2017 at 20:23 Comment(0)
L
0

Hm, I don't have an answer and the same problem. I however can supply a small reproducing example:

#include <QtGui>

class TestWidget : public QWidget
{
    Q_OBJECT

public:
    TestWidget(QWidget * parent = 0);

private slots:
    void addButton();

private:
    QScrollArea * scrollArea;
    QWidget * contents;
    QVBoxLayout * contentsLayout;
};

TestWidget::TestWidget(QWidget * parent)
    : QWidget(parent)
{
    QVBoxLayout * layout = new QVBoxLayout(this);
    scrollArea = new QScrollArea(this);
    layout->addWidget(scrollArea);

    contents = new QWidget(this);
    scrollArea->setWidget(contents);
    contents->resize(300,400);
    scrollArea->setWidgetResizable(true);

    contentsLayout = new QVBoxLayout(contents);
    for (int i = 0; i < 10; ++i)
    {
        QPushButton * button = new QPushButton(QString("button %1").arg(i), this);
        connect(button, SIGNAL(clicked()), this, SLOT(addButton()));
        contentsLayout->addWidget(button);
    }

}

void TestWidget::addButton()
{
    QPushButton * button = new QPushButton("button", this);
    connect(button, SIGNAL(clicked()), this, SLOT(addButton()));
    contentsLayout->addWidget(button);
    QScrollBar * scrollBar = scrollArea->verticalScrollBar();
    scrollBar->setValue(scrollBar->maximum());
}

int main(int argc, char * argv[])
{
    QApplication app(argc, argv);

    TestWidget widget;
    widget.show();

    app.setQuitOnLastWindowClosed(true);

    return app.exec();
}

#include "main.moc"

The problem occurs both with Qt 4.8 and 5.2 (needs minor modifications of the example).

Lennon answered 29/11, 2013 at 22:29 Comment(0)
H
0

Had the same problem, here is what does the manual say:

"In such cases, setting the layout's size constraint property to one which provides 
constraints on the minimum and/or maximum size of the layout (e.g., 
QLayout::SetMinAndMaxSize) will cause the size of the scroll area to be updated 
whenever the contents of the layout changes."

In my case QScrollArea's contents size was changed by setMinimumSize method of a QScrollArea's widget, so i simply added ui->scroll_area->widget()->layout()->setSizeConstraint(QLayout::SetMinAndMaxSize); and it started to work fine.

Important: after resizing the QScrollArea's widget, you should change QScrollBar's value inside of a custom event handler. Simple example:

bool MyCustomClassWithScrollArea::event(QEvent * event)
{
    switch (event->type())
    {
        case MyCustomScrollToEndEvent::ScrollToEndEventType:
        {
            ui->scroll_area->verticalScrollBar()->setValue( ui->scroll_area->verticalScrollBar()->maximum() ); //scrolls to the end
            return true;
        }
    }

    return QWidget::event(event);
}
Halimeda answered 1/2, 2019 at 7:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.