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:
When I start dragging the mouse, the view jumps (I noticed that if the scrollbars are not hidden, their values become the maximum. I'm not sure if this is due to a coordinate problem or because I added ensureVisible(sceneRect)
in the mouseMoveEvent
).
If I embed the view into a widget and place a QPushButton
inside the widget, when I click the button and call view->fitInView(sceneRect, Qt::KeepAspectRatio)
, the scene's position is not centered within the view.
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)
.