Allow QGraphicsView to move outside scene
-
I have a derived class of
QGraphicsViewwhere I set the drag mode toScrollHandDragand also implement the zoom functionality:Header
#ifndef CUSTOMGRAPHICSVIEW_H #define CUSTOMGRAPHICSVIEW_H #include <QGraphicsView> class CustomGraphicsView : public QGraphicsView { Q_OBJECT public: CustomGraphicsView(QWidget* parent = nullptr); protected: virtual void wheelEvent(QWheelEvent* event) override; }; #endif // CUSTOMGRAPHICSVIEW_HImplementation
#include "customview.h" #include <QWheelEvent> CustomGraphicsView::CustomGraphicsView(QWidget* parent) : QGraphicsView(parent) { setScene(new QGraphicsScene); setDragMode(ScrollHandDrag); } void CustomGraphicsView::wheelEvent(QWheelEvent* event) { // if ctrl pressed, use original functionality if (event->modifiers() & Qt::ControlModifier) QGraphicsView::wheelEvent(event); // otherwise, do yours else { setTransformationAnchor(QGraphicsView::AnchorUnderMouse); if (event->delta() > 0) { scale(1.1, 1.1); } else { scale(0.9, 0.9); } } }When I use this class in a program (see below), I can move around the scene and zoom in and out. However, when the image is bigger in one of the dimension than the viewport, but not in the other one (see attached image) I can only drag along the axis that coincides with the image being bigger than the. This, in the attached image, is vertical as it can be seen by the presence of the right-hand side scroll bar.
My question is: is there a way to not restrict the movement? Can I set the scroll mode that allows me to move freely regardless of the scene being contained in the view? Is my only option to reimplement
mouseMoveEvent?Application
#include <QApplication> #include <QGraphicsPixmapItem> #include "customview.h" int main(int argc, char** argv) { QApplication app(argc, argv); CustomGraphicsView cgv; QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(QImage("clouds-country-daylight-371633.jpg"))); cgv.scene()->addItem(item); cgv.show(); return app.exec(); }The image I used is this one.
-
I have a derived class of
QGraphicsViewwhere I set the drag mode toScrollHandDragand also implement the zoom functionality:Header
#ifndef CUSTOMGRAPHICSVIEW_H #define CUSTOMGRAPHICSVIEW_H #include <QGraphicsView> class CustomGraphicsView : public QGraphicsView { Q_OBJECT public: CustomGraphicsView(QWidget* parent = nullptr); protected: virtual void wheelEvent(QWheelEvent* event) override; }; #endif // CUSTOMGRAPHICSVIEW_HImplementation
#include "customview.h" #include <QWheelEvent> CustomGraphicsView::CustomGraphicsView(QWidget* parent) : QGraphicsView(parent) { setScene(new QGraphicsScene); setDragMode(ScrollHandDrag); } void CustomGraphicsView::wheelEvent(QWheelEvent* event) { // if ctrl pressed, use original functionality if (event->modifiers() & Qt::ControlModifier) QGraphicsView::wheelEvent(event); // otherwise, do yours else { setTransformationAnchor(QGraphicsView::AnchorUnderMouse); if (event->delta() > 0) { scale(1.1, 1.1); } else { scale(0.9, 0.9); } } }When I use this class in a program (see below), I can move around the scene and zoom in and out. However, when the image is bigger in one of the dimension than the viewport, but not in the other one (see attached image) I can only drag along the axis that coincides with the image being bigger than the. This, in the attached image, is vertical as it can be seen by the presence of the right-hand side scroll bar.
My question is: is there a way to not restrict the movement? Can I set the scroll mode that allows me to move freely regardless of the scene being contained in the view? Is my only option to reimplement
mouseMoveEvent?Application
#include <QApplication> #include <QGraphicsPixmapItem> #include "customview.h" int main(int argc, char** argv) { QApplication app(argc, argv); CustomGraphicsView cgv; QGraphicsPixmapItem* item = new QGraphicsPixmapItem(QPixmap::fromImage(QImage("clouds-country-daylight-371633.jpg"))); cgv.scene()->addItem(item); cgv.show(); return app.exec(); }The image I used is this one.
After a careful read of the documentation, my conclusion is that it is not possible to move outside the scene. However, one can manually set the limits of the scene to something bigger than the actual scene. The easiest solution is to set a big enough scene at the beginning as suggested here. However, this is not dynamic and has limitations. I solved this issue by auto-computing the scene limits whenever the scene is updated. For that, I connect the
QGraphicsScene::changedto a slot where the auto size of the scene is computed and I manually force the scene to be updated with the mouse move. The final class with the desired behavior is:Header
#ifndef CUSTOMGRAPHICSVIEW_H #define CUSTOMGRAPHICSVIEW_H #include <QGraphicsView> class CustomGraphicsView : public QGraphicsView { Q_OBJECT public: CustomGraphicsView(QWidget* parent = nullptr); protected: virtual void wheelEvent(QWheelEvent* event) override; virtual void mouseMoveEvent(QMouseEvent* event) override; virtual void mousePressEvent(QMouseEvent* event) override; virtual void mouseReleaseEvent(QMouseEvent* event) override; void autocomputeSceneSize(const QList<QRectF>& region); }; #endif // CUSTOMGRAPHICSVIEW_HCPP
#include "customview.h" #include <QWheelEvent> CustomGraphicsView::CustomGraphicsView(QWidget* parent) : QGraphicsView(parent) { // Set up new scene setScene(new QGraphicsScene); // Do not show scroll bars setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Connect scene update to autoresize connect(scene(), &QGraphicsScene::changed, this, &CustomGraphicsView::autocomputeSceneSize); } void CustomGraphicsView::wheelEvent(QWheelEvent* event) { // if ctrl pressed, use original functionality if (event->modifiers() & Qt::ControlModifier) QGraphicsView::wheelEvent(event); // Rotate scene else if (event->modifiers() & Qt::ShiftModifier) { if (event->delta() > 0) { rotate(1); } else { rotate(-1); } } // Zoom else { ViewportAnchor previous_anchor = transformationAnchor(); setTransformationAnchor(QGraphicsView::AnchorUnderMouse); if (event->delta() > 0) { scale(1.1, 1.1); } else { scale(0.9, 0.9); } setTransformationAnchor(previous_anchor); } } void CustomGraphicsView::mouseMoveEvent(QMouseEvent* event) { QGraphicsView::mouseMoveEvent(event); if (event->buttons() & Qt::LeftButton) // If we are moveing with the left button down, update the scene to trigger autocompute scene()->update(mapToScene(rect()).boundingRect()); } void CustomGraphicsView::mousePressEvent(QMouseEvent* event) { if (event->buttons() & Qt::LeftButton) // Set drag mode when left button is pressed setDragMode(QGraphicsView::ScrollHandDrag); QGraphicsView::mousePressEvent(event); } void CustomGraphicsView::mouseReleaseEvent(QMouseEvent* event) { if (dragMode() & QGraphicsView::ScrollHandDrag) // Unset drag mode when left button is released setDragMode(QGraphicsView::NoDrag); QGraphicsView::mouseReleaseEvent(event); } void CustomGraphicsView::autocomputeSceneSize(const QList<QRectF>& region) { Q_UNUSED(region); // Widget viewport recangle QRectF widget_rect_in_scene(mapToScene(-20, -20), mapToScene(rect().bottomRight() + QPoint(20, 20))); // Copy the new size from the old one QPointF new_top_left(sceneRect().topLeft()); QPointF new_bottom_right(sceneRect().bottomRight()); // Check that the scene has a bigger limit in the top side if (sceneRect().top() > widget_rect_in_scene.top()) new_top_left.setY(widget_rect_in_scene.top()); // Check that the scene has a bigger limit in the bottom side if (sceneRect().bottom() < widget_rect_in_scene.bottom()) new_bottom_right.setY(widget_rect_in_scene.bottom()); // Check that the scene has a bigger limit in the left side if (sceneRect().left() > widget_rect_in_scene.left()) new_top_left.setX(widget_rect_in_scene.left()); // Check that the scene has a bigger limit in the right side if (sceneRect().right() < widget_rect_in_scene.right()) new_bottom_right.setX(widget_rect_in_scene.right()); // Set new scene size setSceneRect(QRectF(new_top_left, new_bottom_right)); }
