如何在QGraphicsItem的paint()方法中通过QWidget参数获取鼠标点击与释放位置?
解决QGraphicsItem paint方法中获取橡皮筋矩形坐标的问题
首先得理清你遇到的核心问题:paint方法里的QWidget* widget其实是QGraphicsView的viewport部件,而非QGraphicsView本身。你之前直接用parent()没成功,是因为需要先拿到viewport的父对象(也就是QGraphicsView),再通过它获取存储橡皮筋坐标的guiSchematicViewUtil实例或对应坐标值。
下面给你几种可行的解决方案,你可以根据项目结构选择:
方案一:通过QGraphicsView关联事件过滤器实例
如果你的guiSchematicViewUtil可以作为QGraphicsView的成员变量存在,这是最直接的方式:
- 先在你的QGraphicsView子类(比如命名为
MyGraphicsView)中添加guiSchematicViewUtil的成员:
class MyGraphicsView : public QGraphicsView { Q_OBJECT public: guiSchematicViewUtil* viewUtil; // ... 其他成员与方法 };
确保初始化view时给viewUtil赋值为你的事件过滤器实例。
- 在
guiSchematicRect::paint方法中获取view并访问util的变量:
void guiSchematicRect::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (!widget) return; // 从viewport拿到对应的QGraphicsView MyGraphicsView* myView = qobject_cast<MyGraphicsView*>(widget->parent()); if (!myView || !myView->viewUtil) return; // 获取橡皮筋坐标 QPoint rubberBandOrigin = myView->viewUtil->rubberBandOrigin; QPoint rubberBandEnd = myView->viewUtil->rubberBandEnd; bool isRubberBandActive = myView->viewUtil->rubberBandActive; // 关键:把viewport坐标转换为item局部坐标系 QPointF sceneOrigin = myView->mapToScene(rubberBandOrigin); QPointF sceneEnd = myView->mapToScene(rubberBandEnd); QPointF localOrigin = mapFromScene(sceneOrigin); QPointF localEnd = mapFromScene(sceneEnd); // 接下来就可以用localOrigin和localEnd进行绘制操作了 }
注意:需要把guiSchematicViewUtil中的rubberBandOrigin、rubberBandEnd、rubberBandActive设为public,或者提供getter方法(更推荐后者,保证封装性)。
方案二:利用QObject的Property存储坐标
如果不想修改QGraphicsView的结构,可以用Qt的setProperty和property方法,把橡皮筋坐标存在QGraphicsView上:
- 修改
eventFilter方法,更新坐标时同步到view的property:
bool guiSchematicViewUtil::eventFilter(QObject *watched, QEvent *event) { bool filterEvent = false; switch(event->type()) { case QEvent::MouseButtonPress: { QMouseEvent * mouseEvent = static_cast<QMouseEvent *>(event); rubberBandOrigin = mouseEvent->pos(); rubberBandActive = true; // 将坐标存入QGraphicsView的property QGraphicsView * view = static_cast<QGraphicsView *>(watched->parent()); view->setProperty("rubberBandOrigin", QVariant(rubberBandOrigin)); view->setProperty("rubberBandActive", QVariant(rubberBandActive)); break; } case QEvent::MouseButtonRelease: { if (rubberBandActive) { QMouseEvent * mouseEvent = static_cast<QMouseEvent *>(event); rubberBandEnd = mouseEvent->pos(); QGraphicsView * view = static_cast<QGraphicsView *>(watched->parent()); view->setProperty("rubberBandEnd", QVariant(rubberBandEnd)); view->setProperty("rubberBandActive", QVariant(false)); QRectF zoomRectInScene = QRectF(view->mapToScene(rubberBandOrigin), view->mapToScene(rubberBandEnd)); view->fitInView(zoomRectInScene, Qt::KeepAspectRatio); rubberBandActive = false; } break; } default: break; } return filterEvent; }
- 在
paint方法中读取这些property:
void guiSchematicRect::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { if (!widget) return; QGraphicsView* view = qobject_cast<QGraphicsView*>(widget->parent()); if (!view) return; // 读取property并转换类型 QPoint rubberBandOrigin = view->property("rubberBandOrigin").toPoint(); QPoint rubberBandEnd = view->property("rubberBandEnd").toPoint(); bool isRubberBandActive = view->property("rubberBandActive").toBool(); // 同样需要坐标转换 QPointF sceneOrigin = view->mapToScene(rubberBandOrigin); QPointF sceneEnd = view->mapToScene(rubberBandEnd); QPointF localOrigin = mapFromScene(sceneOrigin); QPointF localEnd = mapFromScene(sceneEnd); // 使用转换后的坐标进行绘制 }
这种方式无需修改现有类结构,灵活性高,但要注意类型转换的安全性(比如判断property是否存在)。
方案三:单例模式(仅适用于单视图场景)
如果程序中只有一个QGraphicsView使用橡皮筋功能,可以把guiSchematicViewUtil做成单例:
- 修改
guiSchematicViewUtil的定义:
class guiSchematicViewUtil : public QObject { Q_OBJECT public: // 获取单例实例 static guiSchematicViewUtil* instance() { static guiSchematicViewUtil instance; return &instance; } // 提供getter方法获取坐标 QPoint getRubberBandOrigin() const { return rubberBandOrigin; } QPoint getRubberBandEnd() const { return rubberBandEnd; } bool isRubberBandActive() const { return rubberBandActive; } private: // 私有构造函数,禁止外部实例化 guiSchematicViewUtil() {} QPoint rubberBandOrigin; QPoint rubberBandEnd; bool rubberBandActive = false; // ... 其他事件过滤代码 };
- 在
paint方法中直接调用单例:
void guiSchematicRect::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { guiSchematicViewUtil* util = guiSchematicViewUtil::instance(); QPoint rubberBandOrigin = util->getRubberBandOrigin(); QPoint rubberBandEnd = util->getRubberBandEnd(); bool isRubberBandActive = util->isRubberBandActive(); // 坐标转换逻辑和之前一致 if (widget) { QGraphicsView* view = qobject_cast<QGraphicsView*>(widget->parent()); if (view) { QPointF sceneOrigin = view->mapToScene(rubberBandOrigin); QPointF sceneEnd = view->mapToScene(rubberBandEnd); QPointF localOrigin = mapFromScene(sceneOrigin); QPointF localEnd = mapFromScene(sceneEnd); // 进行绘制操作 } } }
这种方式最简单,但如果有多个视图,会导致坐标混乱,仅适合单视图场景。
关键注意点
- 坐标转换:橡皮筋坐标是相对于viewport的像素坐标,而
paint方法使用的是item局部坐标系,一定要通过mapToScene和mapFromScene转换后再使用,否则绘制位置会出错。 - 空指针检查:使用
qobject_cast转换类型后,务必检查指针是否为空,避免程序崩溃。
内容的提问来源于stack exchange,提问作者tushar
相关产品推荐
相关产品推荐

