PDA

View Full Version : QScrollArea: How to avoid automatic scroll actions?



PhilippB
28th January 2009, 09:18
Hi!

I have a QScrollArea showing a QVBoxLayout with multiple widgets. At certain points of time, some (child-)widgets need to be disabled or enabled. Sometimes this is triggered by user actions on other widgets, sometimes this is triggered by internal events like timers, without any direct input from the user.

My problem is that QScrollArea automatically ensures visibility for widgets, as they become enabled. In my case, this leads to unwanted scroll actions. I.e., the user changes a control in the upper region of the scrolled area, and, as a result, QScrollArea automatically scrolls down. This can be quite annoying for the user, when the currently used control suddenly jumps out of sight.

The only solution I'm aware of, is disabling the whole scrolled widget first, then enabling the intended child widget, and finally enabling the whole scrolled widget again. This works pretty well, except for simultaneous user actions being aborted. As I said, sometimes this is triggered by something like timers, so the user might use, lets say, a QComboBox inside the scrolled area, while the whole scrolled widget is disabled for a short period of time. This leads to user interactions being randomly aborted without any obvious reason. (Which is also annoying...)

Any other ideas?

Thanks!
Philipp

wysota
28th January 2009, 09:48
I think you should reimplement eventFilter() for the scroll area and make sure the scroll area doesn't react on the widget's focusin event. Currently the filter looks like this:

bool QScrollArea::eventFilter(QObject *o, QEvent *e)
{
Q_D(QScrollArea);
#ifdef QT_KEYPAD_NAVIGATION
if (d->widget && o != d->widget && e->type() == QEvent::FocusIn
&& QApplication::keypadNavigationEnabled()) {
if (o->isWidgetType())
ensureWidgetVisible(static_cast<QWidget *>(o));
}
#endif
if (o == d->widget && e->type() == QEvent::Resize)
d->updateScrollBars();

return false;
}

PhilippB
28th January 2009, 16:56
Thanks for your advice. Nevertheless, I wasn't able to solve my problem yet.

I tried to subclass QScrollArea with the standard implementation you posted. (Just to have a starting point for modifications) But I wasnt able to compile because of missing "QScrollAreaPrivate" definitions.

Then I tried this:

bool NonJumpingScrollArea::eventFilter(QObject *o, QEvent *e)
{
if ( e->type() == QEvent::FocusIn )
{
return true;
}
return QScrollArea::eventFilter(o,e);
}
, but the ScrollArea is still reacting.
Also, changing the filtered signal type to QEvent::EnabledChange didn't help.

PhilippB
2nd February 2009, 16:31
Ok, I finally found the real reason for automatic scroll actions.

I use a PushButton, which gets temporarily disabled after it has been clicked. By clicking on it, the pushbutton receives the keyboard focus. Then, disabling this button leads to a call to focusNextChild, which ensures that nextChild is visible.

In short: To avoid self-scrolling inside QScrollarea, never disable focus-owning-widgets. Always call clearFocus first.

liminf
31st October 2012, 19:04
I have also encountered this "gotcha". In my case, I had a list of widgets managed by a vertical layout within the QScrollArea. Each of the child widgets have a QCheckBox to enable group actions. Once the group action took place, I hid those QCheckBox widgets whose check state was "Qt.Checked"; however, this produced an unexpected result as the QScrollView subsequently scrolled to a seemingly random location. I tried several things to try to prevent this autoscroll behavior (reimplement the QScrollArea::scrollContentsBy(), installed an event filter on both the QScrollArea and QScrollArea::widget() resizeEvents, etc.) but the only thing that worked was to stop calling QCheckBox::hide().

Thanks to PhilippB for identifying the solution. After simply setting the QCheckBox::setFocusPolicy(Qt::NoFocus), I was able to hide the check box without the scrolling behavior.