Qt自定义GraphicScene中GraphicArcItem无法点击选中的问题求助
这问题我之前也碰到过,QTreeView能选中说明item本身已经正确加入场景并被模型追踪了,直接点选不中大概率是碰撞检测的区域不对,也就是QGraphicsItem的boundingRect或者shape函数出了问题,给你几个具体的排查和修复方向:
1. 先确认boundingRect的区域是否真的覆盖了绘制的圆弧
你当前的boundingRect是基于mapFromItem计算的,但这里可能存在坐标映射的坑:
- 首先,
mapFromItem(sview, sview->getCenterEllipse())得到的是当前GraphicArcItem坐标系下对应sview中心的点,但如果GraphicArcItem没有设置父节点(默认是场景直接子节点),而sview也是场景子节点,那这个转换其实等同于sview->mapToScene(sview->getCenterEllipse()),也就是场景坐标。但boundingRect要求返回的是自身坐标系的矩形,如果你没给GraphicArcItem设置pos,那自身坐标系原点就是场景原点,这时候计算出来的x/y是场景中的绝对位置,看起来没问题,但实际可能因为坐标精度(比如用了double但转换时的误差)导致boundingRect和绘制区域错位。 - 快速验证方法:在
paint函数里加一行代码,用红色虚线画出boundingRect的轮廓:
运行后看看红色框是不是正好框住你绘制的圆弧和矩形,如果错位了,那就是坐标计算的问题。painter->setPen(QPen(Qt::red, 1, Qt::DashLine)); painter->drawRect(boundingRect());
2. 重写shape函数实现精确的碰撞检测
默认情况下,QGraphicsItem的碰撞检测是用boundingRect的矩形区域,如果你点击的是圆弧的细线条,可能没落在矩形的有效点击区域里(尤其是线条很细的时候)。这时候需要重写shape函数,返回实际绘制的圆弧路径:
QPainterPath GraphicArcItem::shape() const { QPainterPath path; StateView* sview = m_link->getStart()->getStateView(); QPointF top_left = mapFromItem(sview, sview->getCenterEllipse()); double x = top_left.rx() - 60.0 - 15.0; double y = top_left.ry() - 60.0 - 15.0; QRectF rectangle = QRectF(x,y,50.0,50.0); // 添加圆弧路径,注意Qt的角度是1/16度 path.arcTo(rectangle, startAngle()/16.0, spanAngle()/16.0); // 如果需要包含外围的矩形点击区域,也可以加上: // path.addRect(QRectF(x-5,y-5,60.0,60.0)); return path; }
这样点击圆弧线条的时候,就能准确命中了。
3. 给GraphicArcItem设置父节点简化坐标计算
你现在的GraphicArcItem是场景的直接子节点,坐标计算依赖跨item的映射,容易出错。可以把它的父节点设为对应的StateView,这样所有坐标都是相对于StateView的,计算更简单:
在创建GraphicArcItem的时候:
GraphicArcItem *lview = new GraphicArcItem(link); StateView* sview = link->getStart()->getStateView(); lview->setParentItem(sview); addItem(lview);
然后修改boundingRect和paint里的坐标计算,不需要再用mapFromItem,直接用相对于父节点的坐标:
// paint函数示例 QPointF parentCenter = parentItem()->boundingRect().center(); double x = parentCenter.rx() - 60.0 - 15.0; double y = parentCenter.ry() - 60.0 - 15.0; QRectF rectangle = QRectF(x,y,50.0,50.0); painter->drawArc(rectangle, startAngle(), spanAngle());
这样能避免很多坐标映射的错误。
4. 检查getCenterEllipse的正确性
StateView里的getCenterEllipse用rect().x() + rect().width()/2计算中心,其实可以直接用boundingRect().center()替代,更可靠,因为rect()是构造时的矩形,而boundingRect是你重写后的带extra的矩形,换用更标准的方法能减少潜在问题。
最后,记得修改完后重新编译测试,先从验证boundingRect的位置开始,这是最容易排查的点。
内容的提问来源于stack exchange,提问作者Juliette Marquis

