Qt中QGraphicsItem子类Edge实现自环弧绘制及重写方法问题
解决QGraphicsItem自环弧绘制及边界/形状问题
我看了你的代码,自环绘制和边界计算的问题主要出在三个地方:弧的参数设置、包围盒的计算逻辑,还有自环箭头的缺失。下面一步步给你修正:
1. 修复自环的绘制逻辑(paint方法)
原来的drawArc参数有误,而且没有绘制自环的箭头。我们可以把自环做成从节点右侧出发,绕到上方再回到节点的圆弧,同时添加指向节点的箭头:
void Edge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) { if (!source || !dest) return; QPen pen((option->state & QStyle::State_Selected ? Qt::cyan : Qt::black), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); painter->setPen(pen); if (source != dest) { // 保留原有普通箭头绘制逻辑 QLineF line(sourcePoint, destPoint); if (qFuzzyCompare(line.length(), qreal(0.))) return; painter->drawLine(line); double angle = std::atan2(-line.dy(), line.dx()); QPointF destArrowP1 = destPoint + QPointF(sin(angle - M_PI / 1.8) * qMin(arrowSize, line.length()), cos(angle - M_PI / 1.8) * qMin(arrowSize, line.length())); QPointF destArrowP2 = destPoint + QPointF(sin(angle - M_PI + M_PI / 1.8) * qMin(arrowSize, line.length()), cos(angle - M_PI + M_PI / 1.8) * qMin(arrowSize, line.length())); painter->setBrush((option->state & QStyle::State_Selected ? Qt::cyan : Qt::black)); painter->drawPolygon(QPolygonF() << line.p2() << destArrowP1 << destArrowP2); } else { // 自环绘制修正 const qreal ringRadius = Node::Radius + 10; // 自环半径,比节点半径大一点,可按需调整 const QPointF center = source->pos(); // 节点中心坐标 // 绘制圆弧:从右侧(0度)到上方(270度),跨度270度(Qt角度单位是1/16度,所以要乘16) QRectF arcRect(center.x() - ringRadius, center.y() - ringRadius, 2*ringRadius, 2*ringRadius); painter->drawArc(arcRect, 0 * 16, 270 * 16); // 添加自环箭头 painter->setBrush((option->state & QStyle::State_Selected ? Qt::cyan : Qt::black)); QPointF arrowTip = center + QPointF(0, -ringRadius); // 箭头指向节点上方的圆弧端点 double arrowAngle = M_PI / 2; // 对应圆弧端点的切线方向 QPointF arrowP1 = arrowTip + QPointF(sin(arrowAngle - M_PI / 6) * arrowSize, cos(arrowAngle - M_PI / 6) * arrowSize); QPointF arrowP2 = arrowTip + QPointF(sin(arrowAngle + M_PI / 6) * arrowSize, cos(arrowAngle + M_PI / 6) * arrowSize); painter->drawPolygon(QPolygonF() << arrowTip << arrowP1 << arrowP2); } }
2. 修复boundingRect和shape的计算
原来的nPolygonMath对自环用了矩形框,导致边界不准确,我们需要为自环构建精准的路径和包围盒:
QRectF Edge::boundingRect() const { if (!source || !dest) return QRectF(); if (source != dest) { return nPolygonMath().boundingRect(); } else { const qreal ringRadius = Node::Radius + 10; const QPointF center = source->pos(); // 包围盒要包含圆弧和箭头,额外预留笔宽和箭头大小的空间 qreal extra = pen().width() + arrowSize; return QRectF(center.x() - ringRadius - extra, center.y() - ringRadius - extra, 2*(ringRadius + extra), 2*(ringRadius + extra)); } } QPainterPath Edge::shape() const { QPainterPath ret; if (source != dest) { ret.addPolygon(nPolygonMath()); } else { const qreal ringRadius = Node::Radius + 10; const QPointF center = source->pos(); QRectF arcRect(center.x() - ringRadius, center.y() - ringRadius, 2*ringRadius, 2*ringRadius); // 添加圆弧路径 ret.arcTo(arcRect, 0, 270); // 添加箭头路径,确保选中时能覆盖箭头区域 QPointF arrowTip = center + QPointF(0, -ringRadius); double arrowAngle = M_PI / 2; QPointF arrowP1 = arrowTip + QPointF(sin(arrowAngle - M_PI / 6) * arrowSize, cos(arrowAngle - M_PI / 6) * arrowSize); QPointF arrowP2 = arrowTip + QPointF(sin(arrowAngle + M_PI / 6) * arrowSize, cos(arrowAngle + M_PI / 6) * arrowSize); ret.addPolygon(QPolygonF() << arrowTip << arrowP1 << arrowP2); // 给路径添加宽度,方便选中(细线直接点击很难选中) QPainterPathStroker stroker; stroker.setWidth(pen().width() + 4); ret = stroker.createStroke(ret); } return ret; } // 保留普通边的nPolygonMath逻辑,自环部分无需处理 QPolygonF Edge::nPolygonMath() const { QPolygonF nPolygon; if (source != dest) { QLineF line = QLineF(sourcePoint.x(), sourcePoint.y(), destPoint.x(), destPoint.y()); qreal radAngle = line.angle() * M_PI / 180; qreal selectionOffset = 3; qreal dx = selectionOffset * sin(radAngle); qreal dy = selectionOffset * cos(radAngle); QPointF offset1 = QPointF(dx, dy); QPointF offset2 = QPointF(-dx, -dy); nPolygon << line.p1() + offset1 << line.p1() + offset2 << line.p2() + offset2 << line.p2() + offset1; } return nPolygon; }
3. 额外注意点
ringRadius可以根据你的UI需求调整,让自环和节点保持合适的距离- 自环的圆弧跨度(比如改成180度半圆)、箭头角度都可以按需修改
- 确保
adjust()方法中,当source == dest时无需更新sourcePoint和destPoint(自环绘制用不到这两个点)
这样修改后,自环的绘制会更准确,boundingRect和shape也能正确覆盖自环区域,选中和碰撞检测功能也能正常工作。
内容的提问来源于stack exchange,提问作者Kto To
相关产品推荐
相关产品推荐

