场景(The Scene)
QGraphicsScene提供图形视图场景。该场景具有以下职责:
- 提供用于管理大量图元的快速界面(锅)
- 将事件传播到每个图元(把螃蟹烧熟了)
- 管理图元状态,例如选择和焦点处理
- 提供未转换的渲染功能;主要用于打印
该场景充当QGraphicsItem对象的容器(锅)。通过调用QGraphicsScene.addItem()将图元(螃蟹)添加到场景中,然后通过调用许多图元发现函数的一个来检索图元。 QGraphicsScene.items()及其重载返回由点、矩形、多边形或一般矢量路径包含或相交的所有图元。 QGraphicsScene.itemAt()返回特定点的最顶层项。所有图元发现函数都按递减堆叠顺序返回图元(即,第一个返回的图元位于最顶层,最后一个图元位于最底层)。
QGraphicsScene的事件传播体系结构调度场景事件以传递到图元,还管理图元之间的传播(把螃蟹烧熟的过程)。如果场景在某个位置接收到鼠标按下事件,则场景将事件传递给该位置处的任何图元。
QGraphicsScene还管理某些图元状态,例如图元选择和焦点。您可以通过调用QGraphicsScene.setSelectionArea()来选择场景中的图元,并传递任意形状。此功能还可用作QGraphicsView中“橡皮筋”选择框的基础。要获取所有当前所选图元的列表,请调用QGraphicsScene.selectedItems()。 QGraphicsScene处理的另一个状态是图元是否具有键盘输入焦点。您可以通过调用QGraphicsScene.setFocusItem()或QGraphicsItem.setFocus()来设置焦点,或通过调用QGraphicsScene.focusItem()获取当前焦点的图元。
最后,QGraphicsScene允许您通过QGraphicsScene.render()函数将场景的一部分渲染到绘图设备中。
注:在图形编辑应用中常会用到橡皮筋线,如选择图形的某个区域等,最常见的就是在系统桌面上用鼠标拖动,可以绘制一个类似蚂蚁线的选区,并且选区线能够跟随鼠标的移动而伸缩,因此叫作橡皮筋线(橡皮筋框)。如下图:
视图(The View)
QGraphicsView提供了视图窗口小部件(厨房),可以显示场景的内容。 您可以将多个视图附加到同一场景,以将多个视口提供到同一数据集中。 视图小部件是一个滚动区域,并提供滚动条以浏览大型场景。 要启用OpenGL支持,可以通过调用QGraphicsView.setViewport()将QGLWidget设置为视口。
视图从键盘和鼠标接收输入事件,并在将事件发送到可视化场景之前将这些事件转换为场景事件(在适当的情况下将使用的坐标转换为场景坐标)。
使用其变换矩阵QGraphicsView.transform(),视图可以变换场景的坐标系。 这允许高级导航功能,如缩放和旋转。 为方便起见,QGraphicsView还提供了在视图和场景坐标之间进行转换的功能:QGraphicsView.mapToScene()和QGraphicsView.mapFromScene()。
图元(The Item)
QGraphicsItem是场景中图形项的基类。 Graphics View为典型形状提供了几个标准图元,例如矩形(QGraphicsRectItem),椭圆(QGraphicsEllipseItem)和文本项(QGraphicsTextItem),但是当您编写自定义图元时,可以使用最强大的QGraphicsItem功能。除其他外,QGraphicsItem支持以下功能:
- 鼠标按下,移动,释放和双击事件,以及鼠标悬停事件,滚轮事件和上下文菜单事件。
- 键盘输入焦点和键事件
- 拖放
- 通过父子关系和QGraphicsItemGroup进行分组
- 碰撞检测
图元位于本地坐标系中,与QGraphicsView一样,它还提供了许多功能,用于映射图元和场景之间以及图元之间的坐标。此外,与QGraphicsView一样,它可以使用矩阵变换其坐标系:QGraphicsItem.transform()。这对于旋转和缩放单个图元很有用。
图元可以包含其他图元(子图元)。父图元的转换由其所有子图元继承。然而,无论图元的累积变换如何,其所有函数(例如,QGraphicsItem. contains(),QGraphicsItem.boundingRect(),QGraphicsItem.collidesWith())仍然在本地坐标中操作。
QGraphicsItem通过QGraphicsItem.shape()函数和QGraphicsItem.collidesWith()支持碰撞检测,它们都是虚函数。通过将图元的形状作为QGraphicsItem.shape()的局部坐标QPainterPath返回,QGraphicsItem将为您处理所有碰撞检测。但是,如果要提供自己的碰撞检测,可以重新实现QGraphicsItem. collidesWith()。
Graphics View Framework中的类
这些类提供了创建交互式应用程序的框架:
一共35个类,每个类的具体作用请参考帮助文档。
Graphics View的坐标体系
图形视图基于笛卡尔坐标系(平面直角坐标系x、y轴); 场景中的图元位置和几何图形由两个数字组成:x坐标和y坐标。 当使用未转换的视图观察场景时,场景上的一个单元由屏幕上的一个像素表示。
注意:不支持反转的Y轴坐标系(y向上增长),因为图形视图使用Qt的坐标系,也就是说x轴向右,y轴向下。如下图:
图形视图中有三个有效的坐标系:图元坐标,场景坐标和视图坐标。 为了简化您的实现,Graphics View提供了便利功能,允许您在三个坐标系之间进行映射。
渲染时,Graphics View的场景坐标对应于QPainter的逻辑坐标,视图坐标与设备坐标相同。
图元坐标(Item Coordinates)
图元存在于他们自己的本地坐标系中。它们的坐标通常以其中心点(0,0)为中心,这也是所有变换的中心,如下图:
图元坐标系中的几何图元通常称为图元点,图元线或图元矩形。
创建自定义图元时,您需要考虑图元坐标; QGraphicsScene和QGraphicsView将为您执行所有转换。这使得实现自定义图元变得非常容易。例如,如果您收到鼠标按下或拖动输入事件,则事件位置以图元坐标给出。 QGraphicsItem.contains()虚函数,如果某个点在您的图元内,则返回True,否则返回False,在图元坐标中获取一个点参数。类似地,图元的边界矩形和形状在图元坐标中。
在图元的位置是图元中心点在其父坐标系中的坐标;有时也称为父坐标。在这个意义上,场景被视为所有无父图元的“父母”。*图元的位置在场景坐标中。
子坐标是相对于父坐标的。如果子图元未转换,子坐标和父坐标之间的差异与父坐标中图元之间的距离相同。例如:如果未转换的子图元精确定位在其父项的中心点,则两个图元的坐标系统将完全相同。但是,如果孩子的位置是(10,0),则孩子的(0,10)点将对应于其父坐标的(10,10)点。
由于图元的位置和变换是相对于父项的,因此子项的坐标不受父项转换的影响,尽管父项的转换会隐式转换子项。即使父项被旋转和缩放,子项(0,10)点仍将对应于父项(10,10)点。然而,相对于场景,孩子将遵循父母的转变和位置。如果缩放父级(2x,2x),则子级的位置将位于场景坐标(20,0),并且其(10,0)点将对应于场景上的点(40,0)。
由于QGraphicsItem.pos()是少数例外之一,QGraphicsItem的函数在项坐标中运行,无论图元或其父项的任何转换如何。例如,图元的边界矩形(即QGraphicsItem.boundingRect())总是在图元坐标中给出。
场景坐标(Scene Coordinates)
场景表示其所有图元的基本坐标系。场景坐标系描述每个*图元的位置,并且还形成从视图传递到场景的所有场景事件的基础。除了本地图元pos和边界矩形之外,场景中的每个图元都有一个图元位置和边界矩形(QGraphicsItem.scenePos(),QGraphicsItem. sceneBoundingRect())。场景位置描述了图元在场景坐标中的位置,其场景边界矩形构成了QGraphicsScene如何确定场景的哪些区域已经改变的基础。场景中的变化通过QGraphicsScene.changed()信号传递,参数是场景矩形列表。
视图坐标(View Coordinates)
视图坐标是小部件的坐标。视图坐标中的每个单元对应于一个像素。这个坐标系的特殊之处在于它相对于窗口小部件或视口,并且不受观察场景的影响。 QGraphicsView视口的左上角始终为(0,0),右下角始终为(视口宽度,视口高度)。所有鼠标事件和拖放事件最初都作为视图坐标接收,您需要将这些坐标映射到场景以便与图元进行交互。
坐标映射(Coordinate Mapping)
通常在处理场景中的图元时,将场景中的坐标和任意形状映射到图元,图元之间或视图到场景都很有用。例如,当您在QGraphicsView的视口中单击鼠标时,可以通过调用QGraphicsView.mapToScene(),然后调用QGraphicsScene.itemAt()来询问场景下光标下的图元。如果您想知道图元所在视口中的位置,可以在图元上调用QGraphicsItem.mapToScene(),然后在视图上调用QGraphicsView.mapFromScene()。最后,如果您使用想要查找视图椭圆内的图元,可以将QPainterPath传递给mapToScene(),然后将映射的路径传递给QGraphicsScene.items()。
您可以通过调用QGraphicsItem.mapToScene()和QGraphicsItem.mapFromScene()来将坐标和形状映射到图元的场景中。您还可以通过调用QGraphicsItem.mapToParent()和QGraphicsItem.mapFromParent()或通过调用QGraphicsItem.mapToItem()和QGraphicsItem.mapFromItem()来调用图元的父图元。所有映射函数都可以映射点,矩形,多边形和路径。
视图中提供了相同的映射函数,用于映射到场景和从场景映射。 QGraphicsView.mapFromScene()和QGraphicsView.mapToScene()。 要从视图映射到图元,首先映射到场景,然后从场景映射到图元。
主要特点
缩放和旋转
QGraphicsView支持与QPainter通过QGraphicsView.setMatrix()相同的仿射变换。 通过对视图应用变换,您可以轻松添加对常用导航功能(如缩放和旋转)的支持。
打印
Graphics View通过其渲染函数QGraphicsScene.render()和QGraphicsView.render()提供单行打印。这些函数提供相同的API:您可以通过将QPainter传递给任一渲染函数,让场景或视图将其内容的全部或部分渲染到任何绘图设备中。
场景和视图渲染功能之间的区别在于,一个在场景坐标中操作,另一个在视图坐标中操作。QGraphicsScene.render()通常首选打印未转换场景的整个片段,例如绘制几何数据或打印文本文档。另一方面,QGraphicsView.render()适用于截屏;它的默认行为是使用提供的画家(painter)渲染视口的确切内容。
拖、放
因为QGraphicsView间接地继承了QWidget,所以它已经提供了与QWidget提供的相同的拖放功能。 此外,为方便起见,Graphics View框架还为场景和每个图元提供拖放支持。 当视图收到拖动时,它会将拖放事件转换为QGraphicsSceneDragDropEvent,然后将其转发到场景中。 场景接管此事件的调度,并将其发送到接受丢弃的鼠标光标下的第一个图元。
要从图元开始拖动,请创建QDrag对象,将指针传递给开始拖动的窗口小部件。 许多视图可以同时观察图元,但只有一个视图可以开始拖动。 在大多数情况下,拖动是由于按下或移动鼠标而启动的,因此在mousePressEvent()或mouseMoveEvent()中,您可以从事件中获取原始窗口小部件指针。
要拦截场景的拖放事件,您需要在QGraphicsItem子类中重新实现QGraphicsScene.dragEnterEvent()以及您的特定场景所需的任何事件处理程序。您可以在QGraphicsScene的每个事件处理程序的文档中阅读有关拖放图形视图的更多信息。
图元可以通过调用QGraphicsItem.setAcceptDrops()来启用拖放支持。要处理传入的拖动,请重新实现QGraphicsItem.dragEnterEvent(),QGraphicsItem.dragMoveEvent(),QGraphicsItem.dragLeaveEvent()和QGraphicsItem.dropEvent()。
光标和工具提示
与QWidget一样,QGraphicsItem也支持光标(QGraphicsItem.setCursor())和工具提示(QGraphicsItem.setToolTip())。当鼠标光标进入图元区域时(通过调用QGraphicsItem.contains()检测到),QGraphicsView将激活光标和工具提示。
您还可以通过调用QGraphicsView.setCursor()直接在视图上设置默认光标。
最后
今天的内容有点枯燥,都是一些基础内容,估计很多人都没看明白。没关系我第一次看得时候也不知道说的是什么,后期我们还是会通过例子来学习。我们下期再见!