这是我的第一篇掘金博客,开启掘金写作之路。
1.引言
- 之前使用C++/Qt做的项目中有一个功能是显示结点的拓扑图,既要有结点图标又要有文字信息;而在网上搜了很多,一些是使用Qt编写可拖拽图片的代码,一些是可拖拽的文字等,但是既有图片同时下方显示文字信息的代码没有搜到。
- 啊哈,如果都没有,那我就会是第一个啦~(狗头)
2.效果展示图:
3.踩过的坑
1、一开始呢,肯定是想着先把简单的实现了,那行,就一个一个来,图片和文字,试试能不能单独显示在场景里。这都还算简单,图片使用QPixmap,设置路径load(":/images/img1.png"),设置可选中、可拖拽属性:setPixmap(pixHost.scaled(46, 42, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)),再设置图片的长宽、坐标,ok此时就能将它添加到场景里了pScene->addItem(this)。
显示文字到场景里,使用QGraphicsTextItem:QGraphicsTextItem *text = new QGraphicsTextItem;,设置文字内容、颜色、字体、坐标等,再addItem,文字也能添加到场景里了。
2、这个时候,问题来了,这俩怎么绑定呢,现在倒是都能拖拽,但只是无组织无纪律地游离在场景中。于是在代码里,将对象的函数点出来看(‘.’ or ‘->’),发现一个设置父元素的函数(但是我这个时候没有正确地使用它,导致后来又绕了一大圈),于是我将结点图片的父元素设置为文字。
然后就出现了如下的情况:拖动文字,图片跟着走了,线条还在原地;我再拖动图片,好家伙,这个时候线条隔空跟着走,文字还在原地。崩溃的边缘......
3、不抛弃不放弃,我问了一个曾经可能使用过QGraphicsScene的人,问他怎么绑定文字和图片,让两个一起移动(从这里就开始跑偏了),他说,你可以使用QGraphicsItemGroup。
ok,我使用元素组QGraphicsItemGroup,这次的结果是,拖动节点,文字和图片一起移动了,线条还停在原地的局面,绝了真的,总是会出现意想不到的错。
4、继续问别人,回答说是组接管了内部元素的事件,内部的元素的一些行为就不能被捕捉到,线条就不能跟着移动,所以可能需要做一个元素组与内部元素之间的处理。
ok,我百度后使用void QGraphicsItem::setHandlesChildEvents(bool enabled),如果enabled为true,则QGraphicsItemGroup将处理其所有子item的事件,为false则QGraphicsItemGroup只处理自己的事件。好家伙,设置完了还是那样,点线分离,我枯了。
又各种搜索,看到在此基础上,需要重新写bool QGraphicsItem::sceneEvent(QEvent *event)虚函数,好的嘛,重写,头文件里声明 + cpp中实现,一顿操作,很好,还是不成功,那个线就是不能响应拖拽事件跟着走。
5、最后的最后,看qt文档也无果(因为英语不好,绝了),只得搜索QGraphicsItem看看它有哪些属性,搜到一篇博客上看到有人分析QGraphicsItem,说它可以设置父元素,于是终于灵光一现,使用文本的QGraphicsTextItem设置其父元素为图片,并且屏蔽文本的可选中和可拖拽性质(无语,之前在第2步的时候,是给图片设置父元素为文字,与这个正确答案擦肩而过,刚好相反)。代码段如下:
//显示结点图片
QPixmap pixHost;
pixHost.load(":/images/router.png");
setPixmap(pixHost.scaled(46, 42, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
width = 29;
height = 42;
//结点位置
qreal x = 150 * (key % 3 + 1), y = 100 * (key / 3 + 1);
setPos(x, y);
//结点下方的文本
QGraphicsTextItem *text = new QGraphicsTextItem;
text->setPlainText(nodeName); //文字内容
text->setDefaultTextColor(QColor(Qt::black));
text->setFont(QFont("Microsoft YaHei", 12));
text->setPos(0, 35); //文本位置,是其父控件(图片)的相对位置
text->setParentItem(this); //设置文本的父控件为图片
pScene->addItem(this);
这样设置后就可以实现想要的功能了。
源代码地址:链接:pan.baidu.com/s/1_SkD58rU… 提取码:cydq
4.代码结构讲解:
从图上可以看见,这个文件包括三个类:NodeFrame、NodeClass、LinkClass。从main函数进入的是NodeFrame。
-
NodeFrame中将结点信息维护一个链表结构,每个链表中的结点信息取值,赋值给NodeClass类,作为一个结点对象。再使用LinkClass绘制两个结点之间的连线。
-
NodeClass实现的是结点的绘制与显示,也就是我上面说的那一大堆,结点包括文字和图片,并且只有图片可选中。可拖拽,文字不可选中一直跟着图片位置进行移动。在此文件中重写了itemChange、mousePressEvent、mouseMoveEvent、mouseReleaseEvent函数,在拖拽结点的同时触发改变结点连线的位置信息,这样,连线也会跟着结点移动了。
-
LinkClass实现结点与结点之间连线的绘制,连线的出发点与到达点都使用NodeClass结点,获取结点的位置信息,重写paint函数绘制连线。
总结
首先就是遇到事情一定要坚持,大不了像我这种,各种百度,各种问人,虽然问题解决得很慢,不过绕了弯路最后也能绕回来,那也可以呀,至少问题解决了嘛。
然后呢,就是要多思考,如果在设置父元素的时候我知道换一种方式,或者说我尝试各种修改代码,一不小心解决了的话,那可能会节省很多时间。
再然后,在scene中设置两个元素绑定,使用setParentItem即可。
qt官方文档,能看就尽量看这个吧,很全。
——THE END——