You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在QGraphicsItem的paint()方法中通过QWidget参数获取鼠标点击与释放位置?

解决QGraphicsItem paint方法中获取橡皮筋矩形坐标的问题

首先得理清你遇到的核心问题:paint方法里的QWidget* widget其实是QGraphicsView的viewport部件,而非QGraphicsView本身。你之前直接用parent()没成功,是因为需要先拿到viewport的父对象(也就是QGraphicsView),再通过它获取存储橡皮筋坐标的guiSchematicViewUtil实例或对应坐标值。

下面给你几种可行的解决方案,你可以根据项目结构选择:

方案一:通过QGraphicsView关联事件过滤器实例

如果你的guiSchematicViewUtil可以作为QGraphicsView的成员变量存在,这是最直接的方式:

  1. 先在你的QGraphicsView子类(比如命名为MyGraphicsView)中添加guiSchematicViewUtil的成员:
class MyGraphicsView : public QGraphicsView {
    Q_OBJECT
public:
    guiSchematicViewUtil* viewUtil;
    // ... 其他成员与方法
};

确保初始化view时给viewUtil赋值为你的事件过滤器实例。

  1. 在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上:

  1. 修改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;
}
  1. 在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做成单例:

  1. 修改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;

    // ... 其他事件过滤代码
};
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 15:07:43