Mouse wheel events, event filters, and QScrollArea

— richardwb on Sunday, September 12, 2010 @ 18:21

Let’s say you have a QScrollArea which contains some custom widget. Now, although this widget itself has no use for mouse wheel events, when it’s in your QScrollArea you’d really like for it to grow and shrink in response to the wheel. Or maybe you’d just like to map vertical wheel events to horizontal scrolling, since you know the widget will never need vertical scrollbars.

The biggest problem here is that QScrollArea accepts wheel events (this isn’t strictly true, as you’ll soon see). Events propagate from the child up to the parent until someone accepts the event, and this means that QScrollArea will prevent wheel events from getting to your own code (again, this isn’t strictly true).

Reading the documentation it’s clear that you’d either want to subclass QScrollArea or use event filters. Since you don’t need to subclass QScrollArea for any other reason, using event filters sounds like a reasonable approach. You’ll set up an event filter that forces wheel events to be ignored and install it on the QScrollArea. That way, it’ll propagate up to your containing widget.

So you’d adjust your containing widget like so:

class MyWidget : public QObject
{
    public:
        // ...

        bool eventFilter(QObject* /*obj*/, QEvent* evt)
        {
            if (evt->type() == QEvent::Wheel)
            {
                // ignore the event (this effectively 
                // makes it "skip" one object)
                evt->ignore();
            }
            // return false to continue event propagation
            // for all events
            return false;
        }

    protected:
        void wheelEvent(QWheelEvent* event)
        {
            // your own custom stuff
            // ...
            // if you handle the event and don't want it to
            // propagate any further, accept it:
            event->accept();
        }
};

And then you’d use it like so:

QScrollArea* scrollArea = new QScrollArea(parent);
scrollArea->setWidget(someWidget);
// assuming that 'this' is MyWidget
scrollArea->installEventFilter(this);

This doesn’t work. Instead, what you need to do is installEventFilter() on QScrollArea’s viewport:

scrollArea->viewport()->installEventFilter(this);

In fact, QScrollArea is not the one that is accepting wheel events. It’s the viewport that is doing all of that. By installing an event filter on the viewport, the wheel events will continue to propagate up the tree.

comments powered by Disqus