在自定义 QGraphicsView 中移动 QGraphicsItem 问题

Posted

技术标签:

【中文标题】在自定义 QGraphicsView 中移动 QGraphicsItem 问题【英文标题】:Issue moving an QGraphicsItem in a custom QGraphicsView 【发布时间】:2020-04-16 23:25:47 【问题描述】:

我在自定义 QGraphicView 类中移动 QGraphicItem 时遇到问题。我希望能够做的是通过单击鼠标左键选择该项目,然后将其移动到我单击鼠标右键的位置。

我强烈怀疑我的问题是 QGraphicsItem::setPos() 要求坐标位于父坐标中,我不确定要使用哪个 for of QMouseEvent::*Pos() 以及如何将其转换为父坐标。

屏幕截图显示正在发生的事情,而不是我遵循代码的内容。

main.cpp:(这里是简单的 main,标准测试工具)

#include "QtTest.h"
#include <QApplication>

int main(int argc, char *argv[])

    QApplication a(argc, argv);
    QtTest w;
    w.show();
    return a.exec();

QtTest.h:(这定义了主应用程序窗口)

#pragma once

#include <QtWidgets/QMainWindow>

class QGraphicsView;
class QGraphicsScene;
class QGraphicsItem;
class QMouseEvent;

class QtTest : public QMainWindow

    Q_OBJECT

public:
    QtTest(QWidget *parent = Q_NULLPTR);

private:
    QGraphicsView*   m_gv;
    QGraphicsScene*  m_pScene;

    void setupUI();
;

QtTest.cpp:(应用程序主窗口的实现)

#include "QtTest.h"
#include "testGV.h"

#include <QVariant>
#include <QApplication>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QMainWindow>
#include <QMouseEvent>
#include <QWidget>

QtTest::QtTest(QWidget *parent): QMainWindow(parent)

    setupUI();


void QtTest::setupUI()

    QWidget *centralWidget;

   if (objectName().isEmpty())
       setObjectName("QtTestClass");
   resize(600, 400);

   centralWidget = new QWidget(this);
   centralWidget->setObjectName("centralWidget");

   m_gv = new testGV(centralWidget);
   m_gv->setObjectName("graphicsView");
   m_gv->setGeometry(QRect(100, 10, 441, 331));

   setCentralWidget(centralWidget);

testGV.h:(自定义小部件的定义)

#pragma once

#include <QGraphicsView>
#include <QGraphicsItem>

class testGV : public QGraphicsView

    Q_OBJECT

public:
    testGV(QWidget* parent);

protected:
    void mousePressEvent(QMouseEvent*);

private:
    QGraphicsScene*  m_pScene;
    QGraphicsItem*   m_pItem;

    void   createScene();
;

testGV.cpp:(自定义小部件的实现)

#include "testGV.h"
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QMouseEvent>

testGV::testGV(QWidget* parent) : QGraphicsView(parent)

    createScene();


void testGV::createScene()

    m_pScene = new QGraphicsScene();
    m_pScene->addRect(QRect(30, 30, 150, 150), QPen(Qt::black), QBrush(Qt::black, Qt::NoBrush));
    QGraphicsEllipseItem* pTemp = m_pScene->addEllipse(QRect(0, 0, 15, 15), QPen(Qt::black), QBrush(Qt::red, Qt::SolidPattern));
    pTemp->setFlag(QGraphicsItem::ItemIsMovable);
    pTemp->setFlag(QGraphicsItem::ItemSendsGeometryChanges);

    setScene(m_pScene);


void testGV::mousePressEvent(QMouseEvent* pEvent)

    if (pEvent->button() == 1)                        // left button click
    
        m_pItem = itemAt(pEvent->pos());
    
    else if (pEvent->button() == 2)                    // right button click
    
        m_pItem->setPos(pEvent->pos());
        m_pScene->update();
    

左边的图像是初始显示,当我右键单击红点,然后单击大约黑点所在的正方形时,我得到了右边的图像。我要的是红点移动到我点击的地方。

【问题讨论】:

【参考方案1】:

问题的原因是QMouseEvent在view的坐标中有位置信息,但是item使用的是场景的坐标。这种情况下的解决方案是将视图的坐标映射到场景的坐标:

void testGV::mousePressEvent(QMouseEvent* pEvent)

    if (pEvent->button() == Qt::LeftButton)
        m_pItem = itemAt(pEvent->pos());
    else if (pEvent->button() == Qt::RightButton)
        if(m_pItem)
            m_pItem->setPos(mapToScene(pEvent->pos()));

但即使这样也有问题,你的物品的位置是相对于左上角的,所以位移会有误差,如果你想避免这种偏差,那么你必须考虑左键的位置:

void testGV::mousePressEvent(QMouseEvent* pEvent)

    if (pEvent->button() == Qt::LeftButton)
        m_pItem = itemAt(pEvent->pos());
        m_pItem->setData(0, mapToScene(pEvent->pos()));
    
    else if (pEvent->button() == Qt::RightButton)
        if(m_pItem)
            QPointF p = m_pItem->data(0).toPointF();
            QPointF sp = mapToScene(pEvent->pos());
            m_pItem->setPos(m_pItem->pos() + sp - p);
            m_pItem->setData(0, sp);
        

注意:创建指针时,它指向任何内存位置,因此可能会导致问题,因此我建议将其初始化为 nullptr 并检查指针是否有效:

testGV::testGV(QWidget* parent) : QGraphicsView(parent), m_pItem(nullptr)

    createScene();

【讨论】:

以上是关于在自定义 QGraphicsView 中移动 QGraphicsItem 问题的主要内容,如果未能解决你的问题,请参考以下文章

在 QGraphicsView/QGraphicsScene 中移动 QGraphicsProxyWidget 中的嵌入式小部件

怎么在QGraphicsView上实现鼠标移动事件

如何识别 QGraphicsView 鼠标移动事件?

QGraphicsView 放大或移动时不绘制行项目?

移动到 QGraphicsView 时创建没有窗口打开和关闭的 pyqtgraph 图

在 QGraphicsView 中创建自动滚动功能