c++qtqgraphicsviewqgraphicssceneqmouseevent

Implementing freely dragging the scene in graphics-view


I would like to implement a free dragging functionality based on C++ Qt's graphics-view.

However, I need to achieve this without changing the sceneRect and without using view->setDragMode(QGraphicsView::ScrollHandDrag) , as some of my business logic relies on the sceneRect for calculations.

Below is the code that can reproduce the issue I am currently facing while still running successfully.

view.h:

#include <QGraphicsView>
#include <QMouseEvent>
#include <QWheelEvent>
#include <qscrollbar.h>
class CView : public QGraphicsView
{
    Q_OBJECT
public:
    CView(QWidget* parent = nullptr) : QGraphicsView(parent) {
        setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
        setResizeAnchor(QGraphicsView::AnchorUnderMouse);
    }

protected:
    void mousePressEvent(QMouseEvent* event) override {
        if (event->button() == Qt::LeftButton) {
            m_lastPos = event->pos();
        }

        QGraphicsView::mousePressEvent(event);
    }

    void mouseMoveEvent(QMouseEvent* event) override {
        if (event->buttons() & Qt::LeftButton) {
            QPoint delta = event->pos() - m_lastPos;

            QRectF sceneRect = this->sceneRect();
            sceneRect.translate(-delta);
            this->setSceneRect(sceneRect);

            ensureVisible(sceneRect);

            m_lastPos = event->pos();
        }

        QGraphicsView::mouseMoveEvent(event);
    }


    void wheelEvent(QWheelEvent* event) override {
        QPoint scrollAmount = event->angleDelta();

        double scaleFactor = 1.15;
        if (scrollAmount.y() > 0) {
            scale(scaleFactor, scaleFactor);
        }
        else if (scrollAmount.y() < 0) {
            scale(1.0 / scaleFactor, 1.0 / scaleFactor);
        }

        // QRectF sceneRect = this->sceneRect();
        // QPointF viewCenter = mapToScene(viewport()->rect().center());
        // sceneRect.moveCenter(viewCenter);
        // this->setSceneRect(sceneRect);

        event->accept();
    }

private:
    QPoint m_lastPos;
};

scene.h:

#include <QObject>
#include <QGraphicsScene>

class CScene : public QGraphicsScene
{
    Q_OBJECT
public:
    CScene() {}

protected:
    void drawBackground(QPainter *painter, const QRectF &rect) override {
        QGraphicsScene::drawBackground(painter, rect);
        painter->fillRect(sceneRect(), Qt::black);
    }
};

main.cpp:

#include "View.h"
#include "Scene.h"
#include <QApplication>

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    CView* view = new CView;
    view->resize(600, 500);
    CScene* scene = new CScene;
    scene->setSceneRect(0, 0, 300, 200);
    view->setScene(scene);
    view->show();
    return a.exec();
}

I am encountering two issues:


Solution

  • I have identified the source of the problem.

    The first issue indeed occurred due to the ensureVisible(sceneRect) function, which aims to ensure that the given rectangle is visible within the view.

    Regarding the second issue, by calling qDebug() << viewportTransform(), I discovered that m31 and m32 (the translation components) have changed, resulting in the offset problem. To resolve this issue, all I need to do is set m31 and m32 to 0. This can be achieved by using QGraphicsView::setSceneRect(0, 0, width, height).