首页 > 解决方案 > QT - 无法使用 mouseMove 移动场景

问题描述

我有简单的 cpp 代码。我想用鼠标移动 QGraphicsScene:

// 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);
}

但是当我通过添加项目时: addItem(someQGraphicsItem);

显示项目。我可以拖动它……但不能用 RMB 和 mouseMove 移动场景。

标签: c++qtqgraphicsview

解决方案


我从SO: Zoom features using Qt中获取了我的旧示例,并添加了代码以通过鼠标拖动来平移内容:

#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();
}

输出:

演示视频(GIF 动画)

我挣扎了一段时间让它按预期运行,直到我意识到

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

不一样

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

此外,禁用自动适应视图很重要QGraphicsView

  canvas.setTransformationAnchor(QGraphicsView::NoAnchor);

在再次考虑 OP 问题时,我意识到另一个可能的问题:
如果场景小于视图,它会自动对齐。(涉及到QGraphicsView::alignment。)

在我的演示会话中,我从放大开始,以防止这个问题。

另一种选择是让场景即使比视图更小也可以拖动——可以通过显式设置QGraphicsView::sceneRect来调整场景大小。


在滚动文档时。QGraphicsView我注意到:

QGraphicsView::dragMode

拖动模式:拖动模式

此属性保存按下鼠标左键时在场景上拖动鼠标的行为。

该属性定义了当用户点击场景背景并拖动鼠标时应该发生什么(例如,使用指向手形光标滚动视口内容,或使用橡皮筋选择多个项目)。默认值 NoDrag 不执行任何操作。

此行为仅影响未由任何项目处理的鼠标单击。您可以通过创建 QGraphicsView 的子类并重新实现 mouseMoveEvent() 来定义自定义行为。

我觉得值得一提。


推荐阅读