yarpenzirgin
yarpenzirgin

Reputation: 31

QT - cant make scene move with mouseMove

I've got simple cpp code. I would like to move QGraphicsScene with mouse move:

// MainWindow.cpp code ------------------------------

void MainWindow::initializeMainViewWidget() {
/* some code */
    itemsFrame->setLayout(layout);

    mView = std::make_unique<ui::MainView>();
    layout->addWidget(mView.get());

    mScene = std::make_unique<ui::MainScene>();
    mView->setScene(mScene.get());

    QObject::connect(mScene.get(), &ui::MainScene::openItem, this, &MainWindow::openUpdateItemDialog);

    mView->show();
}

// MainView.cpp code -------------------------------

void MainView::mousePressEvent(QMouseEvent *event) {
    if (event->button() == Qt::RightButton) {
        // Store original position.
        mPos0 = event->position(); //mPos0 is QPointF
        event->accept();
        setCursor(Qt::ClosedHandCursor);
        return;
    } else if (event->button() == Qt::LeftButton) {
        event->ignore();
    }
    QGraphicsView::mousePressEvent(event);
}


void MainView::mouseMoveEvent(QMouseEvent *event)
{
    if (event->buttons() & Qt::LeftButton) {
        event->ignore();
    } else if (event->buttons() & Qt::RightButton) {
        QPointF trans = event->position() - mPos0;
        mPos0 = event->position();
        event->accept();
        translate(trans.x(), trans.y());
        return;
    }
    QGraphicsView::mouseMoveEvent(event);
}

But when I add item by: addItem(someQGraphicsItem);

Item is shown. I can drag it... but can't move scene with RMB and mouseMove.

Upvotes: 1

Views: 541

Answers (1)

Scheff&#39;s Cat
Scheff&#39;s Cat

Reputation: 20161

I took my old sample from SO: Zoom functionality using Qt and added the code to pan the contents by mouse dragging:

#include <QtWidgets>

// class for widget to demonstrate zooming
class Canvas: public QGraphicsView {

  // variables:
  private:
    // start position for pan
    QPoint _posRMB;

  // methods:
  public: 
    // constructor.
    Canvas() = default;
    // destructor.
    virtual ~Canvas() = default;
    // disabled:
    Canvas(const Canvas&) = delete;
    Canvas& operator=(const Canvas&) = delete;

  protected:

    virtual void mousePressEvent(QMouseEvent *pQEvent) override
    {
      if (pQEvent->button() == Qt::RightButton) {
        _posRMB = pQEvent->pos();
        pQEvent->accept();
        setCursor(Qt::ClosedHandCursor);
      }
    }

    virtual void mouseMoveEvent(QMouseEvent *pQEvent) override
    {
      if (pQEvent->buttons() & Qt::RightButton) {
        // pos() -> virtual canvas, _posRMB -> virtual canvas
        QPointF delta = mapToScene(pQEvent->pos()) - mapToScene(_posRMB);
        // modify transform matrix
        translate(delta.x(), delta.y());
        _posRMB = pQEvent->pos();
        // force update
        update();
        pQEvent->accept();
      }
    }

    virtual void mouseReleaseEvent(QMouseEvent *pQEvent)
    {
      if (pQEvent->button() == Qt::RightButton) {
        unsetCursor();
      }
    }

    virtual void wheelEvent(QWheelEvent *pQEvent) override
    {
      //qDebug() << "Wheel Event:";
      // pos() -> virtual canvas
      QPointF pos = mapToScene(pQEvent->pos());
      // scale from wheel angle
      float delta = 1.0f + pQEvent->angleDelta().y() / 1200.0f;
      // modify transform matrix
      QTransform xform = transform();
      xform.translate(pos.x(), pos.y()); // origin to spot
      xform.scale(delta, delta); // scale
      xform.translate(-pos.x(), -pos.y()); // spot to origin
      setTransform(xform);
      // force update
      update();
      pQEvent->accept();
    }
};

QRectF toScr(QWidget *pQWidget, float x, float y, float w, float h)
{
  const int wView = pQWidget->width(), hView = pQWidget->height();
  const int s = wView < hView ? wView : hView;
  return QRectF(
    (0.5f * x + 0.5f) * s, (0.5f * y + 0.5f) * s,
    0.5f * w * s, 0.5f * h * s);
}

int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // setup GUI
  Canvas canvas;
  canvas.setTransformationAnchor(QGraphicsView::NoAnchor);
  canvas.setDragMode(QGraphicsView::NoDrag);
  canvas.resize(256, 256);
  canvas.show();
  // prepare scene
  QGraphicsScene qGScene;
  qGScene.addRect(toScr(canvas.viewport(), -1.0f, -1.0f, 2.0f, 2.0f), QColor(0x000000u));
  qGScene.addRect(toScr(canvas.viewport(), -0.2f, -0.2f, 0.4f, 0.4f), QColor(0x00ff00u));
  qGScene.addRect(toScr(canvas.viewport(), -0.8f, -0.8f, 0.4f, 0.4f), QColor(0xff0000u));
  qGScene.addRect(toScr(canvas.viewport(), -0.8f, 0.4f, 0.4f, 0.4f), QColor(0x0000ffu));
  qGScene.addRect(toScr(canvas.viewport(), 0.4f, 0.4f, 0.4f, 0.4f), QColor(0xff00ffu));
  qGScene.addRect(toScr(canvas.viewport(), 0.4f, -0.8f, 0.4f, 0.4f), QColor(0xffff00u));
  canvas.setScene(&qGScene);
  // runtime loop
  return app.exec();
}

Output:

Demo Video (GIF animation)

I struggled a while to get it running as expected until I realized that

QPointF delta = mapToScene(pQEvent->pos() - _posRMB); // Not working!

isn't the same as

QPointF delta = mapToScene(pQEvent->pos()) - mapToScene(_posRMB);

Additionally, it's important to disable the auto-fit-to-view of QGraphicsView:

  canvas.setTransformationAnchor(QGraphicsView::NoAnchor);

While thinking again about OPs question, I became aware of another possible issue:
If the scene is smaller than the view, it's auto-aligned. (The QGraphicsView::alignment is involved.)

In my demo-session, I started with zoom-in, to prevent this issue.

Another option, to make the scene draggable even it's smaller than the view – the scene size can be tweaked by setting QGraphicsView::sceneRect explicitly.


While scrolling through the doc. of QGrapicsView I noticed:

QGraphicsView::dragMode

dragMode : DragMode

This property holds the behavior for dragging the mouse over the scene while the left mouse button is pressed.

This property defines what should happen when the user clicks on the scene background and drags the mouse (e.g., scrolling the viewport contents using a pointing hand cursor, or selecting multiple items with a rubber band). The default value, NoDrag, does nothing.

This behavior only affects mouse clicks that are not handled by any item. You can define a custom behavior by creating a subclass of QGraphicsView and reimplementing mouseMoveEvent().

which I found worth to mention.

Upvotes: 1

Related Questions