Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Allow QGraphicsView to move outside scene

Allow QGraphicsView to move outside scene

Scheduled Pinned Locked Moved Solved General and Desktop
2 Posts 1 Posters 1.2k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • apalomerA Offline
    apalomerA Offline
    apalomer
    wrote on last edited by
    #1

    I have a derived class of QGraphicsView where I set the drag mode to ScrollHandDrag and 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_H
    

    Implementation

    #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?

    enter image description here

    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.

    apalomerA 1 Reply Last reply
    0
    • apalomerA apalomer

      I have a derived class of QGraphicsView where I set the drag mode to ScrollHandDrag and 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_H
      

      Implementation

      #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?

      enter image description here

      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.

      apalomerA Offline
      apalomerA Offline
      apalomer
      wrote on last edited by
      #2

      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::changed to 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_H
      

      CPP

      #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));
      }
      
      1 Reply Last reply
      2

      • Login

      • Login or register to search.
      • First post
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved