6
votes

I've subclassed QGraphicsView for a custom canvas to be used in a CAD application. I've successfully reimplemented QGraphicsView::wheelEvent to check the keyboard modifiers for the control key and, if the control key is pressed, to zoom. I'm trying to implement a horizontal scroll when the user holds shift and uses the wheel.

The problem I'm having is that the horizontal scrolling also always scrolls up by 0.279. Not a huge problem, but hugely annoying and it points to something else being wrong.

So, here are the questions:

  1. Is this the right way to implement horizontal scrolling? If not, what is?
  2. How do I eliminate this delta of 0.279?

Thanks in advance. Code and sample output below

void myView::zoom(int delta)
{
    double factor = pow(1.2, delta/abs(delta));
    this->scale(factor, factor);
}

void myView::scrollHorizontal(int level)
{
    QPointF center = mapToScene(viewport()->rect().center());
    qDebug() << "center: " << center.x() << ", " << center.y();
    centerOn(QPointF(center.x() - level, center.y()));
}

void myView::wheelEvent(QWheelEvent *event)
{
    //qDebug() << "delta: " << event->delta();
    if (event->modifiers() & Qt::ControlModifier)
    {
        this->zoom(event->delta());
    }
    else if (event->modifiers() & Qt::ShiftModifier)
    {
        this->scrollHorizontal(event->delta());
    }
    else
        QGraphicsView::wheelEvent(event);
}

sample output from the qDebug() line in scrollHorizontal when at the left-edge of the scene:

center:  261.5 ,  615.654 
center:  261.5 ,  615.375 
center:  261.5 ,  615.096 
center:  261.5 ,  614.817 
center:  261.5 ,  614.538 
center:  261.5 ,  614.259 
center:  261.5 ,  613.98 
center:  261.5 ,  613.701 
center:  261.5 ,  613.421 
1
That is weird. I do find the QGraphicsView scrolling a little 'unnatural' when you reach the bounds of the scene rectangle, is the behaviour the same regardless of where the visible rect is in the scene rect?cmannett85
While it'd be interesting to know what is causing the issue, what you're doing is quite bad from the UX perspective. If someone wants a horizontal scroll, they can simply use an input device that has it implemented. Like all recent trackpads, mice with scroll paddles or multitouch, etc. Presumably, a CAD user can afford a decent input device and you don't need to offer such workarounds.Kuba hasn't forgotten Monica

1 Answers

5
votes

Your issue comes from the fact that mapToScene() processes an integer point, not a floating point point. The rounding error is always pointed the same way for a particular viewport size, thus you always add some delta.

You want to be directly modifying the scroll bar's value property or sending the event to the horizontal scroll bar. I've implemented the latter, it's simple enough.

On a Mac, you must absolutely disable the scrollbar inertia for your application. Otherwise as soon as you release the modifier and lift your finger off the trackpad/scroll wheel, vertical scrolling will continue due to inertia. This would make Mac users' experience suck and they would hate you for it :)

screenshot

gview-scroll.pro

QT       += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = gview-scroll
TEMPLATE = app
macx {
    LIBS += -framework Foundation
    OBJECTIVE_SOURCES += helper.m
}
SOURCES += main.cpp

helper.m

//helper.m
#include <Foundation/NSUserDefaults.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSString.h>

void disableMomentumScroll(void)
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"AppleMomentumScrollSupported"];
    [defaults registerDefaults:appDefaults];
}

main.cpp

//main.cpp
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QtCore/qmath.h>
#include <QScrollBar>
#include <QWheelEvent>
#include <QDebug>

class View : public QGraphicsView {
public:
    void zoom(int delta) {
        double factor = qPow(1.2, delta/qAbs(delta));
        scale(factor, factor);
    }
    void wheelEvent(QWheelEvent *event) {
        if (event->modifiers() & Qt::ControlModifier) {
            zoom(event->delta());
        }
        else if (event->modifiers() & Qt::ShiftModifier) {
            horizontalScrollBar()->event(event);
        }
        else {
            QGraphicsView::wheelEvent(event);
        }
    }
public:
    explicit View(QWidget *parent=0) : QGraphicsView(parent) {}
    explicit View(QGraphicsScene *scene, QWidget *parent=0) : QGraphicsView(scene, parent) {}
};

#ifndef Q_OS_MAC
void disableMomentumScroll() {}
#else
extern "C" { void disableMomentumScroll(); }
#endif

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    disableMomentumScroll();
    QGraphicsScene s;
    s.addEllipse(-50, -50, 100, 100, QPen(Qt::red), QBrush(Qt::gray));
    View w(&s);
    w.show();
    return a.exec();
}