diagramscene工程难点分析

目录

1.工程效果

 2.工程存放路径

3.工程难点分析

3 本工程存在的bug


1.工程效果

本工程非常类似miscrosoft visio 软件的流程图组件:

 2.工程存放路径

Examples\Qt-XX.XX.XX\widgets\graphicsview\diagramscene 目录下,XX.XX.XX为Qt的版本号,如:5.14.1。

3.工程难点分析

本工程有两个比较难懂的地方,其中一个代码如下:


void DiagramScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    if (line != nullptr && myMode == InsertLine) {
        QList<QGraphicsItem *> startItems = items(line->line().p1());
        if (startItems.count() && startItems.first() == line)
            startItems.removeFirst();
        QList<QGraphicsItem *> endItems = items(line->line().p2());
        if (endItems.count() && endItems.first() == line)
            endItems.removeFirst();

        removeItem(line);
        delete line;
//! [11] //! [12]

        if (startItems.count() > 0 && endItems.count() > 0 &&
            startItems.first()->type() == DiagramItem::Type &&
            endItems.first()->type() == DiagramItem::Type &&
            startItems.first() != endItems.first()) {
            DiagramItem *startItem = qgraphicsitem_cast<DiagramItem *>(startItems.first());
            DiagramItem *endItem = qgraphicsitem_cast<DiagramItem *>(endItems.first());
            Arrow *arrow = new Arrow(startItem, endItem);
            arrow->setColor(myLineColor);
            startItem->addArrow(arrow);
            endItem->addArrow(arrow);
            arrow->setZValue(-1000.0);
            addItem(arrow);
            arrow->updatePosition();
        }
    }
//! [12] //! [13]
    line = nullptr;
    QGraphicsScene::mouseReleaseEvent(mouseEvent);
}

第4~13行:检测拖动鼠标划线、当释放鼠标时,直线起始点和终止点下面的项对应的第一个项是不是直线对象自己,如果是则将直线对象从startItems 、endItems删除,因为拖动鼠标划线本意是想通过划线将两个非直线的item连接起来。直线自己连接自己无意义。

第16~28行:如果删除startItems 、endItems后,startItems 、endItems容器还不为空,证明startItems 、endItems中存放了非直线对象(item),则分别取出startItems 、endItems中的第一项,即取出两个非直线item,创建一个箭头对象(Arrow ),设置Arrow对象颜色、z次序,并将其设置到刚取出的非直线item对象中去。将箭头对象(Arrow )添加到场景并调用 Arrow::updatePosition()函数更新箭头对象(Arrow )位置。当调用 Arrow::updatePosition()时,会重新在这两个item之间创建一条直线对象。

另一个难点如下:

void Arrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
				  QWidget *)
{
	if (myStartItem->collidesWithItem(myEndItem))
		return;

	QPen myPen = pen();
	myPen.setColor(myColor);
	qreal arrowSize = 20;
	painter->setPen(myPen);
	painter->setBrush(myColor);
//! [4] //! [5]

	QLineF centerLine(myStartItem->pos(), myEndItem->pos());
	QPolygonF endPolygon = myEndItem->polygon();
	QPointF p1 = endPolygon.first() + myEndItem->pos();
	QPointF intersectPoint;
	for (int i = 1; i < endPolygon.count(); ++i) {
		QPointF p2 = endPolygon.at(i) + myEndItem->pos();
		QLineF polyLine = QLineF(p1, p2);
		QLineF::IntersectionType intersectionType =
			polyLine.intersects(centerLine, &intersectPoint);
		if (intersectionType == QLineF::BoundedIntersection)
			break;
		p1 = p2;
	}

	setLine(QLineF(intersectPoint, myStartItem->pos()));
//! [5] //! [6]

	double angle = std::atan2(-line().dy(), line().dx());

	QPointF arrowP1 = line().p1() + QPointF(sin(angle + M_PI / 3) * arrowSize,
									cos(angle + M_PI / 3) * arrowSize);
	QPointF arrowP2 = line().p1() + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize,
									cos(angle + M_PI - M_PI / 3) * arrowSize);

	arrowHead.clear();
	arrowHead << line().p1() << arrowP1 << arrowP2;
//! [6] //! [7]
	painter->drawLine(line());
	painter->drawPolygon(arrowHead);
	if (isSelected()) {
		painter->setPen(QPen(myColor, 1, Qt::DashLine));
		QLineF myLine = line();
		myLine.translate(0, 4.0);
		painter->drawLine(myLine);
		myLine.translate(0,-8.0);
		painter->drawLine(myLine);
	}
}

第14~26行:将myStartItem项位置点和myEndItem项位置点创建一个直线对象centerLine。将myEndItem项所在多边形的每个顶点取出并转为场景坐标,然后判断这个多边形每条边是否和centerLine相交,如果相交,则取出交点,且利用setLine函数,以交点为起始点,myStartItem项位置点为终点,更改Arrow类对象的直线。

第31~42:画出直线的箭头。

第43~49:当直线箭头被选中时,在上一步画出的直线的上边、下边画出1个像素画笔宽的虚线,这样给人的感觉是直线箭头被选中了。

经过这样操作后,就将myStartItem、myEndItem用直线箭头连接起来了,并能实现选中的感觉。

3 本工程存在的bug

本工程DiagramScene类的setLineColor、setTextColor、setItemColor函数存在bug,现以setLineColor为例子说明,其它类同。

void DiagramScene::setLineColor(const QColor &color)
{
    myLineColor = color;
    if (isItemChange(Arrow::Type)) {
        Arrow *item = qgraphicsitem_cast<Arrow *>(selectedItems().first());
        item->setColor(myLineColor);
        update();
    }
}

isItemChange函数如下:

bool DiagramScene::isItemChange(int type) const
{
    const QList<QGraphicsItem *> items = selectedItems();
    const auto cb = [type](const QGraphicsItem *item) { return item->type() == type; };
    return std::find_if(items.begin(), items.end(), cb) != items.end();
}

setLineColor函数的意思就是看看选中的项中是否存在箭头类型的项,如果存在,则取出选中项列表中的第一个项,并将其设置为setLineColor函数参数的颜色,问题是:选中项列表中的第一个项可能不是箭头类型项,即如下代码:

 Arrow *item = qgraphicsitem_cast<Arrow *>(selectedItems().first());

返回的item可能为nullptr,即 qgraphicsitem_cast<Arrow *>转换失败,导致item为nullptr,从而导致后面的代码崩溃。可以试着左边一个方框,右边一个方框,然后这两个方框用直线箭头连接,然后先选中左边的方框,再选中箭头,此时就崩溃了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值