Qt 图形视图 /基于Qt示例DiagramScene解读图形视图框架

文章目录

  • 概述
  • 从帮助文档看示例程序
    • 了解程序背景/功能
    • 理清程序概要设计
  • 分析图形视图的协同运作机制
    • 如何嵌入到普通Widget程序中?
    • 形状Item和文本Item的插入和删除?
    • 连接线Item与形状Item的如何关联?
    • 如何绘制ShapeItem间的箭头线?
  • 下水实践
  • 小结Qt示例阅读方法

概述

本文基于对 Qt 图形视图框架 DiagramScene 示例程序的研习,快速上手图像视图框架的使用和窥探图形视图框架的运作原理。文中通过帮助文档了解了示例程序的功能背景,分析了其设计结构,然后通过对示例程序的实际操作,感受其提供的功能,并以一个软件设计开发人员的角度来审视,自己会如何设计实现这些可触及的功能,由浅入深,向图形视图框架的大院又迈进一步。

打开 Qt Creator -> 示例 -> 搜索Diagram -> 打开 Diagram Scene Example 工程。在打开示例工程时,通常相关帮助页面会一同弹出来。Qt 优势之一就在于其炸天的帮助文档和示例程序。那些没有文档辅助的工程和代码,可能会让人疯掉,不到迫不得已,不建议研读。

在这里插入图片描述

从帮助文档看示例程序

如下图,Qt 示例程序具有详尽的帮助文档,文档目录与工程文件目录完全一致,可见是逐文件对应进行的讲解。你甚至不必细读,只读个概述的段落大意,便可迅速了解该程序。如果你找不到示例程序帮助的位置了,使用帮助程序的查找搜索功能,注意不是索引哦。
在这里插入图片描述

了解程序背景/功能

该示例程序的Doc中,包含了示例实现的主要功能,设计思路,层次结构,主要代码及其注释。

概述中提到,名为 Diagram Scene 的示例,是一个可以创建流程图的应用程序。程序支持流程图形状和文本的添加,流程图形状可以使用Arrow箭头进行连接。流程图形状、箭头和文本都可以设置不同的颜色,且可以改变文本的字体、样式、添加下划线等。还是建议应该编译运行程序,去实际的使用和感受一下。

Qt Graphics 框架(Graphics View Framework)被设计用来管理和展示自定义的2D图形项。涉及的基础类类型有 QGraphicsItem, QGraphicsScene 和 QGraphicsView 等。QGraphicsScene 场景会管理多个图形项,并提供一个surface 展示面。 QGraphicsView is a widget that is used to render a scene on the screen. QGraphicsView 视图是一个用于在屏幕Screen上渲染场景Scene的部件。 说QGraphicsView 是Widget部件,那是因为 QGraphicsView 的继承关系可以一直追溯到QWidget类。另外通过上述这段话,我们大抵知道了管理图形项的是Scene,而View是负责渲染场景的部件。

示例程序展示了如何创建自定义的图形场景和图形项。详细地(In Particular)展示了,
1、创建自定义图形项
2、控制鼠标事件和图形项移动
3、实现可以管理自定义图形项的图形场景
4、自定义基于painting绘制的图形项(Arrow是基于Painting的)
5、创建可移动和可编辑的文本图形项

文档化名词定义
在Microsoft Visio流程图软件中,基本流程图形状包括了,判定、流程、子流程、数据等形状和指针工具、连接线、文本等工具。本程序是简化版的流程图软件,提供了条件、流程、输入输出等形状、文本、指针、连接线。不太一样的是,本示例应用程序中将文本和形状塞到了一个选项卡中,而没有将文本与指针工具和连接线等归为工具。为了后续章节行文的方便和不造成歧义,我们统一口径,定义下文使用的名词及其含义。流程图形状记做ShapItem,连接线记做ArrowItem,文本记做TextItem。

理清程序概要设计

都说,面向对象程序设计则是一种自底向上的思维方式,但是,面向对象的程序设计也涉及自顶向下的设计方法,开发者要从程序的整体架构和功能需求出发,确定需要的类和对象,以及它们之间的关系和交互。最好先问问,若拿到同样的功能需求后,自己会怎么办,你会设计哪些类,类之间有怎样的关联。示例程序包含如下C++类设计,各类功能概述如下,

MainWindow
派生自 QMainWindow,它创建、布局和展示Widget部件,用以管理窗口部件、场景、视图、图形项之间的交互。它(forward)转发窗口部件的输入到DiagramScene对象,并在TextItem发生变动或者ShapItem、TextItem插入场景时,接收scene信号来更新相关的HMI部件。另外这个类负责删除图形项并handle处理图形项的Z向顺序,即决定项彼此重叠时的绘制drawn顺序。

DiagramItem
派生自 QGraphicsPolygonItem 多边形项。表示一个流程图形状,不包含文本和箭头。即ToolBox-Page1中的条件、流程、输入输出3种基本流程图形状。

DiagramScene
类DiagramScene,继承自 QGraphicsScene 类,其除了(In addition to)具有基类的图形项管理功能外,添加了对自定义项 DiagramItems、Arrows、DiagramTextItems 的管理功能。在这个类中,一个鼠标点击可以有三种不同的响应:鼠标位置的项移动、新项被插入、在ShapeItem项之间连接箭头。鼠标点击触发哪种操作,取决于场景所处的mode模式,其由Mode枚举类型表示,在MainWindow的输入部件的响应函数中使用setMode()函数设置当前的模式。Which action a mouse click has depends on the mode, given by the Mode enum, the scene is in. (英语语法,given by the Mode enum, the scene is in. 是 mode 的并列定语)。The type of DiagramItem, given by the DiagramItem::DiagramType function, to be created when an item is inserted is set with the setItemType() slot.(to be created when an item is inserted 是定语从句,与given… 并列修饰主语The type of DiagramItem)。

DiagramItem
DiagramItem在DiagramScene场景下代表流程图形状。它继承自QGraphicsPolygonItem类,并为每种形状持有一个polygon多边形。DiagramType枚举类型为每个流程图形状定义了一个值。该类持有与其连接的箭头(对象指针)的列表。This is necessary because only the item knows when it is being moved (with the itemChanged() function) at which time the arrows must be updated. 只有ShapeItem自身知道何时它被移动,此时,与之相连的箭头必须要重新绘制。该item类还可以使用image()成员函数在QPixmap上绘制自己。image()函数的设计和使用比较巧妙,该函数在QPixmap上绘制一个几何图形,并在示例程序中使用它,用作MainWindow工具组中各按钮的icon图标。枚举类型Type是类的唯一标识符,它被qgraphicsitem_cast()使用,用来动态强制类型转换图形项。UserType == 65536,是最小的用户自定义图形项的值,定义在超类QGraphicsItem中。

DiagramTextItem
DiagramTextItem类继承自QGraphicsTextItem,并添加了移动可编辑文本项的功能(possibility)。可编辑状态的QGraphicsTextItem被设计为,在固定位置上不可移动,在用户single单击该项时开始编辑。而派生自它的 DiagramTextItem,则通过双击开始编辑,单击仍然可用于与该项交互和移动。With DiagramTextItem the editing starts with a double click leaving single click available to interact with and move it.

Arrow
自定义类Arrow,是用以连接两个DiagramItem的ShapItem。它在其中一个图形项上绘制箭头头部,为了实现这一点,该自定义图形项需要自行绘制该箭头,并重写场景视图用以检查碰撞和选择的方法。该类派生自 QGraphicsLine 类,并在此基础上绘制箭头头部,并随着其所连接的ShapItem的移动而移动。本类中 boundingRect() 和 shape() 函数是对 QGraphicsLineItem 超类对应虚函数的重写,它们被图形场景对象用以检查冲突和选择。

分析图形视图的协同运作机制

了解图形视图机制的基本创建流程和运作流程,是阅读本示例的根本目标之一。在阅读此示例程序前,要对场景、视图、图形项等概念有基础的了解,且通过帮助文档了解示例程序的功能和结构。重点强调下,一定要编译、运行并实际去使用你要探究的示例程序,去感受它的功能,思考并猜测你所触及到的功能在该程序中如何实现的,先提起自己的兴趣来!、

如何嵌入到普通Widget程序中?

在《Qt 图形视图 /Graphics View Framework 详谈图形视图框架》一文中,我们已经意识到,类QGraphicsView 其父辈可追寻到 QWidget类;而按照辈分算的话,QGraphicsScene 得是 QGraphicsView 的二老爷爷,其直接从QObject继承;类 QGraphicsItem 与 QObject 没有任何血缘关系,它是一个根级别的类。由此可推测,能将图形视图功能融入到QWidget程序中的家伙,非QGraphicsView莫属,

MainWindow::MainWindow() {..scene = new DiagramScene(itemMenu, this);...QHBoxLayout *layout = new QHBoxLayout;layout->addWidget(toolBox);       //在水平布局中添加用以图形项和背景选择的ToolBox控件view = new QGraphicsView(scene);  //创建视图对象/以scene为可视化场景layout->addWidget(view);          //将视图添加到布局对象QWidget *widget = new QWidget;    //widget 是MainWidget中心窗口widget->setLayout(layout);      setCentralWidget(widget);        //...
}

上述使用的是QGraphicsView构造函数原型为,
在这里插入图片描述
创建一个视图对象,并设置其可视化场景为 scene 这个场景对象(也可以说是,将视图关联到场景上)。那么,场景,为啥称作可视化场景呢? 可视化场景是指在图形界面中以图形的形式展示的场景,它提供了一个二维的绘图区域,可以在其中添加、编辑和显示图形项。对象scene 是通过 对象view 来进行显示和交互的,可以将其中的图形项以图形的形式可视化在界面上。

另外瞥一眼用户操作的几个关键部件,buttonGroup、pointerTypeGroup、backgroundButtonGroup ,而且前阵子看到这里的时候,还单独为 QButtonGroup 开了一篇文章,确实挺好用的。按钮组 buttonGroup管理的是ToolBox-Page1流程图形状和文本选择按钮们,backgroundButtonGroup管理的是ToolBox-Page2背景样式选择按钮们,pointerTypeGroup管理的是工具栏中鼠标指针和连接线。上述按钮组内的按钮在被check后,其相应操作均为对scene对象进行参数设置。其他工具栏按钮和菜单动作项功能和行为类似。
补记,20231210,
通过细细品读这个示例程序,不仅可以很大程度上提升对图形视图框架的理解,也有很多其他的地方值得学习,如GroupButton的使用、QAction&QToolButton小部件图标创建、 DiagramItem::image() 函数返回与自身同形的QPixmap等,都值得留心学习。

形状Item和文本Item的插入和删除?

在软件实操中,插入一个流程图图形项或流程图文本项,只需要先在ToolBox选项卡中Check相应的按钮,然后在绘图区域(view对象代表的Widget)中,使用鼠标左键在任意位置执行单点击操作即可,如果之前设置了填充色,也会在插入时生效。被插入的项,可以任意移动其在 ‘视图中的位置’ ,更准确地说应该是在场景中的位置,可以使用Delte删除或使用工具栏的删除按钮删除。

插入操作,
通过对代码的基本分析可看到,插入操作的信号是从场景类的itemInserted发射的。DiagramScene场景定义了4种操作项的模式,

    enum Mode { InsertItem, InsertLine, InsertText, MoveItem };

step,设置项操作模式,
当我们在buttonGroup中点击了某按钮时,如果是ID为InsertTextButton的按钮则secen模式被设置为DiagramScene::InsertText,如果是判定框、流程框、输出输出框对应的按钮,则secen模式被设置为DiagramScene::InsertItem。如果我们点击的是pointerTypeGroup组中的鼠标按钮或连接线按钮,则secen模式将分别被设置为DiagramScene::MoveItem 和 DiagramScene::InsertLine 操作类型。

step,在场景对象中实时检测鼠标事件,

void DiagramScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) {if (mouseEvent->button() != Qt::LeftButton)return;DiagramItem *item;switch (myMode) {case InsertItem:item = new DiagramItem(myItemType, myItemMenu);item->setBrush(myItemColor);addItem(item);item->setPos(mouseEvent->scenePos());emit itemInserted(item);break;...

如上所示,在鼠标按压事件过滤函数中,如果检测到鼠标左键按下,且当前设置的图形项操作模式为InsertItem的话,则创建一个流程图形状项,并将其添加到场景对象中,设置图形项对象在场景中的位置。最后发出已流程图形状项插入信号,该信号在MainWindow中响应如下,将鼠标效果强设置为鼠标指针类型,进而将scene对象从InsertItem模式中退出,切换到MoveItem模式;取消buttonGroup的当前选中。插入文本项和连接线项的流程与上述插入流程图基本形状项是一致的,不再赘述。

删除操作,

void MainWindow::deleteItem() {foreach (QGraphicsItem *item, scene->selectedItems()) {if (item->type() == Arrow::Type) {scene->removeItem(item);Arrow *arrow = qgraphicsitem_cast<Arrow *>(item);arrow->startItem()->removeArrow(arrow);arrow->endItem()->removeArrow(arrow);delete item;  //要注意的是scene的removeItem操作并不释放}}//由于按住Ctrl键时可以选中多个项,且选中顺序无法保证,因此不能在一个for循环中删除多个类型foreach (QGraphicsItem *item, scene->selectedItems()) {if (item->type() == DiagramItem::Type)qgraphicsitem_cast<DiagramItem *>(item)->removeArrows();scene->removeItem(item);delete item;}
}

要注意,图形视图框架允许多个项同时被选中,且有选中顺序,因此不能在一个for循环中删除多个类型。要先删除连接线,再删除图形项和文本项。删除连接线Item时,要对关联的图形Item进行操作,同样地,在删除某个图形Item时,也要捎带着删除关联的连接线Item。

连接线Item与形状Item的如何关联?

上一节我开始对ArrowItem和ShapeItem的关联关系产生了好奇,为了搞明白这件事情,得关注DiagramScene中连接线的插入操作。

void DiagramScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)  {...case InsertLine:line = new QGraphicsLineItem(QLineF(mouseEvent->scenePos(), mouseEvent->scenePos()));line->setPen(QPen(myLineColor, 2));addItem(line);break;...
----------------------------------------------------------------------------------------------------
void DiagramScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) {if (line != 0 && 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;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();}}//line = 0;   QGraphicsScene::mouseReleaseEvent(mouseEvent);
}

连接线Item的插入,是在鼠标按压和抬起事件的协作下完成的。

在按压事件中,
只是简单的创建了 连接线Item,并将其添加到了Scene中。特别需要注意的是,这里创建的并不是我们自定义的 Arrow 对象,而是 使用Arrow的超类 QGraphicsLineItem 进行的创建。这里,line成员变量指向的 QGraphicsLineItem 对象,本质上是一个临时变量,line 的定义仅仅是为了能在释放事件函数中继续使用它。而且在释放事件处理中 line直接被delete掉了。

在释放事件中, 先来看如何求取line对应的起始项和结束项列,
执行插入操作时,在DiagramScene::mouseReleaseEvent函数的下图位置设置断点进行调试,
在这里插入图片描述
通过在Locals和Expressions调试窗口中变量的对比可以发现,startItems 包含了line对象自己(绿色框标注)和包含line->line().p1()点的形状Item对象。进一步的研究 startItems 求取语句可知,
在这里插入图片描述
而其中关键函数,QGraphicsScene::items 存在多个重载版本,这里使用的是,
在这里插入图片描述
上述程序中,iterms函数返回 pos 位置的可视的项对象,使用 默认的Qt::IntersectsItemShape模式,表示将返回那些shape形状与pos相交的所有Item对象。这里有个概念需要补充下,Items whose exact shape,具有xx特点精确shpe的图形项,怎么理解呢,我们从enum Qt::ItemSelectionMode的帮助中可以跳转到shpe的一个解释,
在这里插入图片描述
连接了shape的概念后,我们变可以明白示例程序中items(line->line().p1())的返回列表,如果line的起点是两个Item的叠加,则此时items将返回包含line的3个Item元素。通过后续代码可以看出来,无论startItems和endItems中包含多少个元素,在实际创建Arrow对象时,使用的都是first元素,这与items函数的Qt::DescendingOrder参数设置有关,此处不再细究。

只有在鼠标被释放时,才能确定起点和终点,才能判断这条连接线是否有效(即两端都连接到DiagramItem对象),如果连接无效,就不需要创建Arrow对象。示例程序中,巧妙的使用 line 对象作为引子来实现了这一功能。另外,Arrow的p1和p2也不同于line对象,后者的起点和充电直接来自于用户操作,而Arrow兑现的起点和终点是实时计算出来的,在Arrow::paint绘制过程中实现。

解下来的流程就好理解多了,将arrow对象指针分别在起点和终点ShapeItem对象中记录下来,设置arrow对象的Z向叠顺序(stacking order)值,以确保Arrow对象会被渲染在其他图形项目的下方,这样Arrow就不会遮挡住其他重要的可视元素。将arrow项对象添加到场景中。

void Arrow::updatePosition() {//QLineF line(this->mapFromItem(myStartItem, 0, 0), this->mapFromItem(myEndItem, 0, 0));QLineF line(mapFromItem(myStartItem, 0, 0), mapFromItem(myEndItem, 0, 0));setLine(line);
}

那么 updatePosition 函数这是在干啥?为了从现象级搞明白这个问题,封掉该函数的实现,重新运行测试,发现此时连接线无法完成绘制。实际上,在创建Arrow时,它自身的线条位置还未被设置,需要通过updatePosition()计算并指定。
在这里插入图片描述
帮助文档中,对updatePosition函数实现的讲解如上。 在IDE搜索其调用位置,可看到其会在Arrow对象创建后、关联的ShapeItem有大小或位置等变动时被调用。函数实现中使用mapFromItem函数功能如下,
在这里插入图片描述
结合帮助文档,每个Item都有自己的本地坐标系统,上述代码实现的功能是,将myStartItem坐标系下的Point(0,0),也即中心点(务必注意,图形项坐标系的原点是中心点,而不是左上角点),也可以称作参考原点,转化到 this item == arrow slef 坐标系下的某点,用左线段起点。20231212 初次阅读到这里时,误以为myStartItem坐标系下的Point(0,0)是左上点,因此始终不能理解line的构造思路,耽误了很久。如果能理解到那时ShapeItem的中心点,那么就好理解多了,Arrow的超类QGraphicsLineItem,其私有实现中持有一个线段,这个线段的起点和终点分别是两端ShapeItem的中心位置,再由于ArrowItem的Z值非常小,ShapeItem会对其进行部分覆盖,效果就有了。进一步,我们可以修改ArrowItem的Z值,看看效果…
在这里插入图片描述

如何绘制ShapeItem间的箭头线?

在这里插入图片描述
连接两个流程图形状的功能是我在所有问题中比较感兴趣的一个。实际操作过程,点击工具栏中的连接线按钮,然后通过鼠标左键选中一个图形,保持按住,移动鼠标到另一个图形的任意位置后松开即可。而且随着图形项的拖拽,连接线也自动跟随变动。
通过前边的文档,我们知道这里的连接线功能由Arrow类实现,而且,Arrow的基类就是绘制线段的。当注释掉Arrow类中对于paint函数的重写,发现,连接线依然可以画出来,只不过该连接线是不带箭头的一个线段,由此分析,可确定,Arrow::paint函数主要是用以绘制线段的箭头形状,

QPainterPath Arrow::shape() const
{QPainterPath path = QGraphicsLineItem::shape();path.addPolygon(arrowHead);return path;
}
//! [2]//! [3]
void Arrow::updatePosition()
{QLineF line(mapFromItem(myStartItem, 0, 0), mapFromItem(myEndItem, 0, 0));setLine(line);
}
//! [3]//! [4]
void Arrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *,  QWidget *)
{if (myStartItem->collidesWithItem(myEndItem))return;  //两个ShapeItem有重叠时不进行带箭头连接线的绘制QPen myPen = pen();myPen.setColor(myColor);qreal arrowSize = 20;    //设置三角形箭头的尺寸painter->setPen(myPen);painter->setBrush(myColor);//点坐标为float型的线段,center其含义是StartItem中心点到EndItem中心点QLineF centerLine(myStartItem->pos(), myEndItem->pos());//always:(-100,-100)、(100,-100)、(100,100)、(-100,100)、(-100,-100) //即最初创建它时设置的多边形QPolygonF endPolygon = myEndItem->polygon();//p1多边形某条边的起点QPointF p1 = endPolygon.first() + myEndItem->pos();qDebug() << "endPolygon.first:" << endPolygon.first() << ", " << "myEndItem->pos:" << myEndItem->pos();QPointF p2;             //p2多边形某条边的终点QPointF intersectPoint; //最终的计算结果 /多变形某条边与连接线centerLine之间的交叉点QLineF polyLine;        //多边形的某条边线段//遍历多边形的全部边线段for (int i = 1; i < endPolygon.count(); ++i) {p2 = endPolygon.at(i) + myEndItem->pos();polyLine = QLineF(p1, p2);//判定两条线段是否相交并计算交点值QLineF::IntersectType intersectType = polyLine.intersect(centerLine, &intersectPoint);if (intersectType == QLineF::BoundedIntersection)break;p1 = p2;}//更新覆盖Arrow插入操作时设置的线段setLine(QLineF(intersectPoint, myStartItem->pos()));//反正切函数 /计算直线自身的角度值double angle = std::atan2(-line().dy(), line().dx());//得到三角箭头尾部的点1QPointF arrowP1 = line().p1() + QPointF(sin(angle + M_PI / 3) * arrowSize, cos(angle + M_PI / 3) * arrowSize);//得到三角箭头尾部的点2QPointF arrowP2 = line().p1() + QPointF(sin(angle + M_PI - M_PI / 3) * arrowSize, cos(angle + M_PI - M_PI / 3) * arrowSize);arrowHead.clear();//构成箭头三角形的3个点arrowHead << line().p1() << arrowP1 << arrowP2;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);  //平移线条以绘制下方的虚线}
}

在DiagramItem构造函数中,风格ShapeItem类型创建PainterPath,使用的是各自本地坐标系统中坐标值,路径点压入path的顺序,也即polygon函数获取的多边形点的顺序。如上我们从红色判定框到蓝色输入输出框之间绘制箭头线的过程中,我们调试测试piant函数,其中大部分信息已经直接注释在了源码中,如下,我们再简单补充几点,
在这里插入图片描述
由于我们应用程序中创建的所有ShapItem都没有parent,因此paint函数中pos函数返回的坐标都是在Scene坐标系下的。由于Item是一个区域,因此要描述Item在场景下的位置,则必须找一个Item的点来代表Item整体,这个点就是Item的原点(its origin),也是Item本地坐标系下的 (0, 0) 点,通常它在Item的中心位置。如下图所示的绿色原点,
在这里插入图片描述
当需要求取两条 QLineF 线的交点的时候,构建centerLine和polyLine线段使用的点都是在场景坐标系下的浮点型坐标值。centerLine中心线,是StartItem中心原点到EndItem中心原点的线段,polyLine代表的是EndItem的4条边线段,通过intersect函数求取两线的交点,作为待绘制三角箭头的头部顶点。如是,完成了一个带箭头的连接线的绘制。

下水实践

鉴于本文的篇幅已经足够长,这部分内容将在新CSDN博文中展开。在前文的阅读分析过程中,通过增加打印信息、借助IDE调试窗口分析执行数据、临时改变代码执行逻辑观察执行现象等手段,也有效的提升了对示例程序本身和图形视图框架的理解。通过对DiagramScene示例程序的解读,不光是加深了对图形视图框架的理解,也get到不少其他的 Qt GUI编程小技巧,限于篇幅,不再展开。

小结Qt示例阅读方法

本文的第一笔记录是在2022年5月份写下的,拖拖拉拉到现在,已经过去1年半了,终于还是基本完成了文章的整理。在早期我曾想着对着示例的帮助文档,一点点的读下去,可是并不理想,我总被卡主,理解不了。后来,我采用了如下方法,
先粗读帮助文档,了解应用程序功能和背景。然后我结合一些结构化程序设计中,概要设计流程和详细设计的方法和流程,把需求抛给自己,以设计者的视角,思考应用程序中表现出来的功能,并写写自己的思路。一定要尝试写写,这里不追求结果,其目的在于激发思考过程,哪怕只是简单的提出几个问题,如 ,场景视图如何与传统QWidget程序柔和在一起?图形项本地坐标系的原点在哪里?线段是如何绘制的? 箭头是如何绘制的? 总之,提起自己的兴趣来,尽量摆脱 ‘读别人的程序’ 产生的不适感。

如下,是现阶段我能总结的一个如何解读示例程序的步骤,可能并不有效和高效,仅供参考,

1、确保您有程序的文档或说明文件。仔细阅读文档,了解程序的目的、功能和使用方法。
2、下载或拷贝示例程序,配置其编译和运行环境,确保便程序可以正常编译和运行。
3、了解程序结构。浏览示例程序的源代码,了解它的结构和组织方式。查看主要文件和目录,以及它们之间的关系。
4、运行程序,切身体验其展现出来的功能。更重要的是,思考如何实现你所触及的功能。
5、针对某个具体的功能,进行调试分析,借助IDE或打印,观察其执行流和数据流,理解其实现逻辑。
6、研究细节。针对某个具体的功能,结合执行流和数据流,对源码进行剖析。这个阶段将开始纵向深入,会较多地触及示例程序所属的Qt框架的机制、类、接口等,认真研习相关帮助文档,理解其本质。在阅读示例前,我们可能已有些基础的理论知识储备,现在在实践的基础上,得以对某些具体的理论点进行较深入的学习领会,使得我们走出了,理论-实践-理论交叉前行的步伐。
7、修改和定制。尝试更改一些参数或添加新功能,以更好地理解程序的工作原理。
8、学习和扩展。通过阅读示例程序的源代码和文档,学习它的实现细节和使用的技术。根据您的兴趣和需求,尝试扩展示例程序,添加新功能或进行改进。通常,我们在Get到新知识的同时,也会产生新问题需求。如此,我们便有了新的要走的路,如,在本示例的解读完成后,将开始对图形视图框架下坐标系系统的探究。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/746788.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

穿越半个世纪,探索中国数据库的前世今生

引言 在数字化潮流席卷全球的今天&#xff0c;数据库作为 IT 技术领域的“活化石”&#xff0c;已成为数字经济时代不可或缺的基础设施。那么&#xff0c;中国的数据库技术发展经历了怎样的历程&#xff1f;我们是如何在信息技术的洪流中逐步建立起自己的数据管理帝国的呢&…

Hadoop大数据应用:HDFS 集群节点扩容

目录 一、实验 1.环境 2.HDFS 集群节点扩容 二、问题 1.rsync 同步报错 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 主机架构软件版本IP备注hadoop NameNode &#xff08;已部署&#xff09; SecondaryNameNode &#xff08;已部署&#xff09; Resourc…

海川润泽AI机器视觉仪系列产品,助推“人工智能+”打开新质生产力的大门

3月5日&#xff0c;第十四届全国人民代表大会第二次会议开幕。国务院总理李强在政府工作报告&#xff0c;提出大力推进现代化产业体系建设&#xff0c;加快发展新质生产力。深入推进数字经济创新发展&#xff0c;制定支持数字经济高质量发展政策&#xff0c;积极推进数字产业化…

零基础自学C语言|动态内存管理

✈为什么要有动态内存分配 我们已经掌握的内存开辟方式有&#xff1a; int a 0;//在栈空间上开辟四个字节char arr[10] { 0 };//在栈空间上开辟10个字节的连续空间但是上述的开辟空间的方式有两个特点&#xff1a; 空间开辟大小是固定的。数组在申明的时候&#xff0c;必须…

【开源】SpringBoot框架开发公司货物订单管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 客户管理模块2.2 商品维护模块2.3 供应商管理模块2.4 订单管理模块 三、系统展示四、核心代码4.1 查询供应商信息4.2 新增商品信息4.3 查询客户信息4.4 新增订单信息4.5 添加跟进子订单 五、免责说明 一、摘要 1.1 项目…

Cap2:Pytorch转TensorRT(上:Pytorch->ONNX)

文章目录 1、pytorch导出onnx模型2、使用onnxruntime推理onnx模型3、精度对齐4、总结 深度学习框架种类繁多&#xff0c;想实现任意框架之间的模型转换是一件困难的事情。但现在有一个中间格式ONNX&#xff0c;任何框架模型都支持转为ONNX&#xff0c;然后也支持从ONNX转为自身…

案例分析篇00-【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-先导篇)

专栏系列文章&#xff1a; 2024高级系统架构设计师备考资料&#xff08;高频考点&真题&经验&#xff09;https://blog.csdn.net/seeker1994/category_12593400.html 案例分析篇01&#xff1a;软件架构设计考点架构风格及质量属性 案例分析篇11&#xff1a;UML设计考…

疫情网课管理系统|基于springboot框架+ Mysql+Java+Tomcat的疫情网课管理系统设计与实现(可运行源码+数据库+设计文档+部署说明)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 ​编辑 学生功能模块 管理员功能 教师功能模块 系统功能设计 数据库E-R图设计 lun…

Ubuntu上搭建TFTP服务

Ubuntu上搭建TFTP服务 TFTP服务简介搭建TFTP服务安装TFTP服务修改配置文件 重启服务 TFTP服务简介 TFTP是一个基于UDP协议实现的用于在客户机和服务器之间进行简单文件传输的协议&#xff0c;适用于开销不大、不复杂的应用场合。TFTP协议专门为小文件传输而设计&#xff0c;只…

虚拟游戏理财 - 华为OD统一考试(C卷)

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 题目描述 在一款虚拟游戏中生活&#xff0c;你必须进行投资以增强在虚拟游戏中的资产以免被淘汰出局。 现有一家Bank&#xff0c;它提供有若干理财产品m&#xff0c;风险及…

【PHP安全】PHP伪协议

PHP伪协议&#xff1a; file:// #访问本地文件系统http:// #访问HTTPs网址ftp:// #访问ftp URLphp:// #访问输入输出流zlib:// #压缩流data:// #数据&#xff08;RFC 2397&#xff09;ssh2:// #security shell2expect:// #处理交互式的流glob:// #查找匹配的文件路径phar:// #P…

Siamese Network(孪生神经网络)详解

Siamese和Chinese有点像。Siam是古时候泰国的称呼&#xff0c;中文译作暹罗。Siamese也就是“暹罗”人或“泰国”人。Siamese在英语中是“孪生”、“连体”的意思&#xff0c;这是为什么呢&#xff1f;十九世纪泰国出生了一对连体婴儿&#xff0c;当时的医学技术无法使两人分离…

软件功能测试内容有哪些?湖南长沙软件测评公司分享

软件功能测试主要是验证软件应用程序的功能&#xff0c;且不管功能是否根据需求规范运行。是通过给出适当的输入值&#xff0c;确定输出并使用预期输出验证实际输出来测试每个功能。也可以看作“黑盒测试”&#xff0c;因为功能测试不用考虑程序内部结构和内部特性&#xff0c;…

Orange3数据预处理(清理特征组件)

清理特征 移除未使用的属性值和无用的属性&#xff0c;并对剩余的值进行排序。 输入 数据: 输入数据集 输出 数据: 过滤后的数据集 命名属性定义有时包含在数据中不出现的值。即使原始数据中没有这种情况&#xff0c;数据过滤、选择示例子集等操作也可能移除…

(二十五)Flask之MTVMVC架构模式Demo【重点:原生session使用及易错点!】

目录&#xff1a; 每篇前言&#xff1a;MTV&MVC构建一个基于MTV模式的Demo项目&#xff1a;蹦出一个问题&#xff1a; 每篇前言&#xff1a; &#x1f3c6;&#x1f3c6;作者介绍&#xff1a;【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领…

关于Transfomer的思考

为何诞生 在说transformer是什么&#xff0c;有什么优势之类的之前&#xff0c;先谈一谈它因何而诞生。transformer诞生最重要的原因是早先的语言模型&#xff0c;比如RNN&#xff0c;由于其本身的训练机制导致其并行度不高&#xff0c;特别是遇到一些长句子的情况下。其次&…

抖音开放平台第三方开发,实现代小程序备案申请

大家好&#xff0c;我是小悟 抖音小程序备案整体流程总共分为五个环节&#xff1a;备案信息填写、平台初审、工信部短信核验、通管局审核和备案成功。 服务商可以代小程序发起备案申请。在申请小程序备案之前&#xff0c;需要确保小程序基本信息已填写完成、小程序至少存在一个…

硬件笔记(26)---- 高速电路中滤波电容的选取

先要知道电容的等效电路 其中ESL取决于电容的类型和封装&#xff0c;一般用贴片陶瓷电容为例&#xff0c;对于直插式电解电容&#xff0c;他们的ESL很大。按下表&#xff0c;封装越大&#xff0c;ESL越大&#xff0c;但是0612有些例外 0612和1206就是 长短边的区别&#xff0c;…

什么是MVC三层结构

1.MVC&#xff08;三层结构&#xff09; MVC&#xff08;Model-View-Controller&#xff09;是一种常见的软件设计模式&#xff0c;用于将应用程序的逻辑和界面分离成三个不同的组件。每个组件负责特定的任务&#xff0c;从而提高代码的可维护性和可扩展性。 以前的模式。 遇到…

【网络安全渗透】常见文件上传漏洞处理与防范

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属的专栏&#xff1a;网络安全渗透 景天的主页&#xff1a;景天科技苑 文章目录 1.文件上传漏洞1.1. 描述1.2. 危害1.3. 有关文件上传的知识1.4…