how to pan images in QGraphicsView
Asked Answered
F

3

27

I am currently able to load my image into a grahpics scene, and then again into a QGraphicsViewer.

I am able to implement a zoom feature by dtecting a QEvent::Wheel and then calling the graphicsViews's scale() function.

However, I can't seem to figure out how to get the pan functionality working. I basically want to detect when a mouse has clicked down on the image, and then move the image left, right, up or down along with the mouse.

As of right now, I basically have a MouseFilter class that is detecting events, and doing different things depending on the event type. I attached that listener to the QGraphicsView object

Fatigue answered 20/1, 2011 at 23:4 Comment(0)
M
18

QGraphicsView has build-in mouse-panning support. Set correct DragMode and it will handle the rest. You do need the enable scroll bars for that to work.

Miserere answered 21/1, 2011 at 0:29 Comment(3)
Do you think I would be able to implement this in my own? I want to do this on the right mouse button press. How can I scroll the view from the code?Iconoclasm
@neuviemeporte: I don't know how to do it with the right mouse click. You may want to post it as a separate question so someone else can answer it.Miserere
You no longer have to enable the scrollbars in Qt 5.0Tropophilous
I
58

In case someone is wondering how to do it on their own, it's actually quite simple. Here's the code from my app:

class ImageView : public QGraphicsView
{
public:
    ImageView(QWidget *parent);
    ~ImageView();

private:
    virtual void mouseMoveEvent(QMouseEvent *event);
    virtual void mousePressEvent(QMouseEvent *event);
    virtual void mouseReleaseEvent(QMouseEvent *event);

    bool _pan;
    int _panStartX, _panStartY;
};

You need to store the start position of the drag, for example like this (I used the right button):

void ImageView::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::RightButton)
    {
        _pan = true;
        _panStartX = event->x();
        _panStartY = event->y();
        setCursor(Qt::ClosedHandCursor);
        event->accept();
        return;
    }
    event->ignore();
}

Also, you need to clear the flag and restore the cursor once the button is released:

void ImageView::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::RightButton)
    {
        _pan = false;
        setCursor(Qt::ArrowCursor);
        event->accept();
        return;
    }
    event->ignore();
}

To actually manage the drag, you need to override the mouse move event. QGraphicsView inherits a QAbstractScrollArea, and its scrollbars are easily accessible. You also need to update the pan position:

void ImageView::mouseMoveEvent(QMouseEvent *event)
{
    if (_pan)
    {
        horizontalScrollBar()->setValue(horizontalScrollBar()->value() - (event->x() - _panStartX));
        verticalScrollBar()->setValue(verticalScrollBar()->value() - (event->y() - _panStartY));
        _panStartX = event->x();
        _panStartY = event->y();
        event->accept();
        return;
    }
    event->ignore();

}
Iconoclasm answered 1/3, 2011 at 16:2 Comment(4)
yes, exactly what I was looking for..even years later, thanks!Cordes
Consider using the example above, but rather than manipulate the scrollbars, use QGraphicsView::scrollContentsBy. It takes an offset dx, dy that adjusts your contents (Scene).Olethea
Disregard - do not use ::scrollContentsBy - according to the docs you should only use scrollbars. "Calling this function in order to scroll programmatically is an error, use the scroll bars instead (e.g. by calling QScrollBar::setValue() directly)."Olethea
Can anyone tell me if this would interfere with right-clicking to pull up context menus?Basilicata
M
18

QGraphicsView has build-in mouse-panning support. Set correct DragMode and it will handle the rest. You do need the enable scroll bars for that to work.

Miserere answered 21/1, 2011 at 0:29 Comment(3)
Do you think I would be able to implement this in my own? I want to do this on the right mouse button press. How can I scroll the view from the code?Iconoclasm
@neuviemeporte: I don't know how to do it with the right mouse click. You may want to post it as a separate question so someone else can answer it.Miserere
You no longer have to enable the scrollbars in Qt 5.0Tropophilous
B
9

neuviemeporte solution requires to subclass a QGraphicsView.

Another working drag implementation can be obtained without subclassing the view using eventFilter. If you don't need to customize other behaviors of the QGraphicsView, this technique will save you some work.

Let's say your GUI logic is maintained by a QMainWindow subclass and the QGraphicsView & QGraphicsScene are declared as a private members of this subclass. You would have to implement the eventFilter function as follows:

bool MyMainWindow::eventFilter(QObject *obj, QEvent *event)
{
    if (obj == scene && event->type() == Event::GraphicsSceneMouseMove)
    {
        QGraphicsSceneMouseEvent *m = static_cast<QGraphicsSceneMouseEvent*>(event);
        if (m->buttons() & Qt::MiddleButton)
        {
            QPointF delta = m->lastScreenPos() - m->screenPos();
            int newX = view->horizontalScrollBar()->value() + delta.x();
            int newY = view->verticalScrollBar()->value() + delta.y();
            view->horizontalScrollBar()->setValue(newX);
            view->verticalScrollBar()->setValue(newY);
            return true;
        }
    }

    return QMainWindow::eventFilter(obj, event);
}

To filter events from the QGraphicsScene, you'll have to install MyMainWindow as an eventFilter of the scene. Perhaps you could do this in the same function where you setup your GUI.

void MyMainWindow::setupGUI()
{ 
    // along with other GUI stuff...

    scene->installEventFilter(this);
}

You can extend this idea to replace the cursor with the drag "hand" as previously shown.

Bracket answered 1/3, 2016 at 20:41 Comment(1)
Thank you, great workaround - for those who don't want to reimplement QGraphicsView :)Engen

© 2022 - 2024 — McMap. All rights reserved.