QScrollArea scrollbars not updating
-
Hi all,
Like many others, I'm tearing my hair out with QScrollArea. I'm building an app where widgets - let's call them "blocks" - can be arbitrarily added|moved|deleted within a scroll area.
I keep a QVector of pointers to the blocks. Each block is a child of a single, common widget - let's call this the background - and this single background widget is the child of the scroll area.
I programatically adjust the size of the background widget based on the relative locations of the blocks. (For example, if I drag the blocks further apart with the mouse, the background size is increased and the scrollarea scrollbars update correctly.)
To update the size of the background widget, I've found I need to call background.resize() and background.setMinimumSize(). If the size of the background widget is changing, then the scroll area updates its scrollbars correctly. However, if the scroll area itself changes size due to resizing the application (and without the background widget changing size), the scroll bars do not update.
This is unfortunately a problem and not expected behaviour. If the scroll area is reduced, the scrollbars need to shrink to enable scrolling to the extremeties of the background widget.
The only way to get the scrollbars to refresh is to change the size of the background widget but I don't want to keep toggling or jittering the background widget size every time I resize the app & scroll area.
Any help would be most appreciated. I'm working with Qt 6.4.2 on macOS.
Michael
-
Hi all,
Like many others, I'm tearing my hair out with QScrollArea. I'm building an app where widgets - let's call them "blocks" - can be arbitrarily added|moved|deleted within a scroll area.
I keep a QVector of pointers to the blocks. Each block is a child of a single, common widget - let's call this the background - and this single background widget is the child of the scroll area.
I programatically adjust the size of the background widget based on the relative locations of the blocks. (For example, if I drag the blocks further apart with the mouse, the background size is increased and the scrollarea scrollbars update correctly.)
To update the size of the background widget, I've found I need to call background.resize() and background.setMinimumSize(). If the size of the background widget is changing, then the scroll area updates its scrollbars correctly. However, if the scroll area itself changes size due to resizing the application (and without the background widget changing size), the scroll bars do not update.
This is unfortunately a problem and not expected behaviour. If the scroll area is reduced, the scrollbars need to shrink to enable scrolling to the extremeties of the background widget.
The only way to get the scrollbars to refresh is to change the size of the background widget but I don't want to keep toggling or jittering the background widget size every time I resize the app & scroll area.
Any help would be most appreciated. I'm working with Qt 6.4.2 on macOS.
Michael
Like many others, I'm tearing my hair out with QScrollArea
Ouch! Sorry to hear that. Sounds painful.
To update the size of the background widget, I've found I need to call background.resize() and background.setMinimumSize()
Manipulating size constraints like this is usually a bad idea leading to problems and is not needed here.
Here's an idea that seems to be working like what you want.
An item that can be moved with a drag:class Item : public QWidget { QPointF prev; public: using QWidget::QWidget; void mousePressEvent(QMouseEvent* evt) override { prev = evt->globalPosition(); } void mouseMoveEvent(QMouseEvent* evt) override { QPointF delta = evt->globalPosition() - prev; prev = evt->globalPosition(); move(pos() + delta.toPoint()); } // and just so we see it QSize sizeHint() const override { return {40,40}; } void paintEvent(QPaintEvent*) override { QPainter(this).fillRect(rect(), Qt::red); } };
A canvas that resizes itself when child is moved:
class Canvas : public QWidget { public: using QWidget::QWidget; QSize sizeHint() const override { return childrenRect().united({0,0,1,1}).size(); } bool event(QEvent* evt) override { if(evt->type() == QEvent::ChildAdded) static_cast<QChildEvent*>(evt)->child()->installEventFilter(this); return QWidget::event(evt); } bool eventFilter(QObject* obj, QEvent* evt) override { if (evt->type() == QEvent::Move) adjustSize(); return QWidget::eventFilter(obj, evt); } };
And then you simply use that in a scroll area:
Canvas* canvas = new Canvas(); (new Item(canvas))->move(10,10); (new Item(canvas))->move(60,60); QScrollArea* sa = new QSCrollArea(); sa->setWidget(canvas);
I don't have a mac to test that on, but it shouldn't matter what OS it runs on.
-
Thanks very much Chris. I'll try your method today.
-
Hi Chris,
I have your code working - thanks very much. The way it handles the canvas size is cleaner than my approach.
Unfortunately the scrollbars are still miss-behaving in the same way as with my code. When the items are being dragged around (and therefore the child rect and canvas size is updating), the scrollbars are correctly updating. However when the app is resized (and the scroll area resizes with it), the scrollbars freeze and don't update. When I move an item, the scrollbars suddenly refresh/update.
So I also tried this on Windows. Qt 6.4.1 on Windows is working fine - only Qt on macOS has the problem, so I'm filing a bug report. Grrrrrrr.
Thanks again.
-
Yup, I tried on Windows. Sounds like a platform specific bug indeed. Maybe you can cheat force it somehow, e.g. since the scrollbars adjust on moving the items try calling adjustSize() in the resize event handler.
Btw. what's the sizeAdjustPolicy on macOS? Maybe it's just there's some difference in the defaults between platforms?
-
I ran your code again on two more macOS machines this morning and it's running fine. I don't know why. Maybe I previously ran the wrong program. (I've
Anyway, thanks again. Your help is the best I've seen on Qt Forum yet.
Cheers,
Michael