威尼斯wns·8885556(vns认证网站)-BinG百科

当前位置:首页 > 新闻资讯 > 技术动向

QT绘图之图形视图

发布者:威尼斯wns·8885556    时间:2021-01-13 14:19:11


在Qt中我们可以通过QWidget的派生类,重写paintEvent()函数,使用QPainter绘制我们想要的任何内容。这种方法对于从QWidget派生的窗口部件很理想,但如果要绘制大量独立的项,向用户提供与图形项交互(如鼠标交互等,通常需要大量的工作。


图形视图架构,则完美地满足了基于图形项的高性能绘制和交互的需求。


在Qt4中,图形视图基于Qt中GUI和QtCore模块,该架构的设计取代并超越了Qt3的QCanvas。它提供了一个平台,用于大量自定义2D图元的管理与交互,使用一个BSPBinary Space Partitioning - 二叉空间分割)树,以提供对图形元素的快速查找。正因如此,它可以使超大的场景实时地可视化,即使其包含数百万的图元。


框架包括一个事件传播架构,支持场景(Scene)中的图元(Item)进行精确的双精度交互功能。图元可以处理键盘事件、鼠标按下、移动、释放和双击事件,同时也能跟踪鼠标移动。



1

图形视图架构


图形视图的 场景视图、 图元,作为图形视图架构的三要素。其中每个图元从抽象类(QGraphicsItem)派生而来,场景(QGraphicsScene)作为模型容纳管理各个绘制项,视图(QGraphicsView) 用来可视化显示数据,如有需要,我们可以在多个不同的视图内显示同一场景。


场景


QGraphicsScene提供了图形视图场景。它提供了一个快速的接口,用于管理大量图元、向每个图元传递事件、管理图元的状态,如:选中、焦点处理等。


场景是QGraphicsItem对象的容器。通过调用QGraphicsScene::addItem()将图元添加到场景中后,你就可以通过调用场景中的不同的查找函数来查找其中的图元。


QGraphicsScene::items()函数及其重载函数可返回所有图元。包括:点、矩形等;通过QGraphicsScene::itemAt()接口返回在特定点上最上面的图元。所有找到的图元按照层叠递减的排列顺序(即:最先返回的图元是最顶层的,最后返回的则是最底层的)


QGraphicsScene的事件传递机制负责将场景事件传递给图元,同时也管理图元之间的传递。如果场景在某个位置得到一个鼠标按下事件,就将该事件传递给这个位置上的图元。QGraphicsScene同时还管理某些图元的状态,例如:图元的选中和焦点。


视图


提供一个可视的窗口,用于显示场景中的图元。对于同一个场景,可以提供多个不同/相同的视图来显示其元素。


QGraphicsView是可滚动的窗口部件,用来提供滚动条以浏览大的场景。如需使用OpenGL,可用QGraphicsView::setViewport()将视图设置为QGLWidget。如果你希望OpenGL具有反锯齿,则需要OpenGL支持采样缓冲。视图的类层次图如下图:

图1.png


图元


QGraphicsItem是场景中图元的基类。其中提供了一些典型形状的标准图元,例如:矩形 、椭圆 、文本项 。当然你也可以自定义图元项。除此之外,QGraphicsItem还支持以下特性:鼠标按下、移动、释放和双击事件,以及鼠标悬浮事件、滚轮事件和上下文菜单事件;键盘输入焦点和键盘事件;拖放;分组:通过父子关系或QGraphicsItemGroup;碰撞检测。


每个图元项都有一个z值,z值较大的项会被绘制在z值较小的项之上,这样就可以调整覆盖图元项的显示顺序;每个图元项可以是场景或者另一个项的子对象,当对一个项进行了矩阵变换,该项的所有子对象会自动的应用该变换,递归应用至最深层次的子孙对象。


如果让子项忽略所有父项的变换,可以通过调用QGraphicsItem::setFlag(QGraphicsItem::ItemIgnoresTransformations)来实现。Qt中图元项的类层次结构如下图所示(具体用法请参见Qt帮助文档)

图2.png
图3.png


2

图形视图坐标系


图形视图基于笛卡儿坐标系,场景中图元的位置和几何形状由两组数据来表示:X坐标和Y坐标。当使用一个未转换的视图来观察一个场景,场景中的一个单元将会由场景上的一个像素表示。图形视图中使用了三种有效的坐标系 图元坐标、  场景坐标 视图坐标


图元坐标


图元的绘制在自己的局部坐标系。它们的坐标通常围绕它们的中心点(0, 0),并且这也是所有转换的中心。创建自定义图元时,只需考虑图元坐标即可。QGraphicsScene和QGraphicsView会为你实现所有相关的转换,这样一来,实现自定义图元就容易多了。图元绑定的矩形(boundingRect())或形状区域(shape())也是项坐标系统的。


子坐标是相对于父坐标而言的,如果子坐标没有转换,那么子坐标和父坐标的差异就和图元在父坐标中的距离一样,图元的位置(pos)是图元的中心点在其父坐标系下的坐标,有时也被称为父坐标。


如果这个图元直接加到场景中则它的位置在场景坐标中,由于图元的位置和转换是相对于父图元来说的,因此,虽然父图元的转换隐式地转换了子图元,但是子图元的坐标不会因其转换而受影响。不过相对于场景来说,子图元将随着父图元进行转换和偏移 。图元坐标系如下图所示:


图4.png


场景坐标系


场景为所有图元提供了基础坐标系。场景坐标系描述了每个顶层图元的位置,同时构成了从视图传递到场景中所有事件的基础。其中每个图元都有一个场景位置以及坐标系下的矩形边界(QGraphicsItem::scenePos()QGraphicsItem::sceneBoundingRect()),场景坐标系如下图所示:


图5.png


视图坐标系


视图坐标是窗口的坐标,视图坐标中的每个单位对应一个像素。对于该坐标系来说较特殊的一点是:它相对于视口,不会受被观察的场景所影响。QGraphicsView以窗口的左上角作为自己坐标系的原点总是(0, 0),右下角总是(width, height)。所有的鼠标事件和拖拽事件都以视图坐标接收到的,你需要将这些坐标映射到场景,以便于和图元进行交互。视图坐标系如下图:

图6.png


坐标映射


图7.png


3

事  件


当QGraphicsView接收到Qt鼠标、键盘和拖放事件(QMouseEvent、QKeyEvent、QDragEvent等)时,它会将其转换为QGraphicsceneEvent子类的实例,并转发到它显示的QGraphicscene。然后,场景再将事件转发到相关图元项,即:视图->场景->图元。图形视图中事件的类层次如下:


图8.png


4

动  画


图形视图在几个层面上提供了对动画的支持。实现图元动画有三种方法:


1)  使用Qt中的Animation Framework框架,QPropertyAnimation可以为任何QObject属性实现动画效果,所以你的图元必须继承QObject和QGraphicsItem。

2)  创建一个自定义图元,从QObjcet和QGraphicsItem继承,该图元可以通过定时来驱动实现动画。

3)  调用QGraphicsScene::advance()从而依次调用QGraphicsItem::advance();


5

示  例


自定义图元示例

图9.png


构建一个场景视图示例


向场景中添加了一个矩形,一个圆:

图10.jpg


* 注: 本文介绍基于Qt4.8.6.


FreeX 产品微信公众号

威尼斯wns·8885556微信公众号

威尼斯wns·8885556视频号