![]() PyQt5 84篇 PyQt5:Graphics View框架介绍 导读:本期结束! LEARN MORE 正文 本期我们先来介绍下Graphics View框架。 Graphics View提供了一个平面,用于管理和交互大量自定义的2D图形图元,以及一个用于可视化图元的视图窗口小部件,支持缩放和旋转。 该框架包括一个事件传播架构,允许场景中图元的精确双精度交互功能。图元可以处理关键事件,鼠标按下,移动,释放和双击事件,还可以跟踪鼠标移动。 Graphics View使用BSP(二进制空间分区)树来提供非常快速的图元发现,因此,即使有数百万个图元,它也可以实时显示大型场景。 01 图形视图架构 Graphics View提供了一种基于图元的模型-视图编程方法,就像QTableView,QTreeView和QListView一样。多个视图可以观察单个场景,并且场景包含具有不同几何形状的图元。 如同下面的绿巨人,视图:强壮的颜值,模型:所有的分析来源于其强大的头脑,人家也好歹也是著名物理学家啊! ![]() 在正式介绍场景、视图、图元的概念前,我们尝试通过烧螃蟹的过程,举例说明这几个概念,如下图: ![]() 在上图中,我们假设,锅就是场景、螃蟹就是图元、厨房就是视图。烧螃蟹的几个步骤如下: 1、往锅中放入4只螃蟹(向场景中加入图元) 2、场景受外部影响的同时也影响到了图元(给锅加热,螃蟹烧红了) 3、这一切都发生在厨房之中(场景在视图中) 在了解到烧螃蟹这几个步骤后我们再来说明以下几个概念。 场景(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支持以下功能:
图元位于本地坐标系中,与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个类,每个类的具体作用请参考帮助文档。 03 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()。 要从视图映射到图元,首先映射到场景,然后从场景映射到图元。 04 主要特点 缩放和旋转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()直接在视图上设置默认光标。 05 最 后 今天的内容有点枯燥,都是一些基础内容,估计很多人都没看明白。没关系我第一次看得时候也不知道说的是什么,后期我们还是会通过例子来学习。我们下期再见! 下午坐公交走神坐过了两站,我对司机说我坐过站了,车内人全站了起来,又是递烟又是说好话的,其中一个人掏出20元钱给我,说下一站让我打出租车回去,我说:唉,这坐过站了回家饭都凉了!又有几个人站起来掏出一百元给我说:你别激动,这些钱给你,等下路过饭店叫俩小菜回去喝点,我都不知道世界怎么突然变美了 |