警告
本节包含从C++自动翻译到Python的代码片段,可能包含错误。
图形视图框架¶
图形视图框架的概述,用于交互式2D图形。
Graphics View 提供了一个用于管理和与大量自定义2D图形项交互的表面,以及一个用于可视化这些项的视图小部件,支持缩放和旋转。
该框架包含一个事件传播架构,允许对场景上的项目进行精确的双精度交互能力。项目可以处理按键事件、鼠标按下、移动、释放和双击事件,它们还可以跟踪鼠标移动。
图形视图使用BSP(二进制空间分区)树来提供非常快速的项发现,因此,它可以实时可视化大型场景,即使有数百万个项。
Graphics View 在 Qt 4.2 中被引入,取代了其前身 QCanvas。
图形视图架构¶
Graphics View 提供了一种基于项目的方法来进行模型-视图编程,非常类似于 InterView 的便利类 QTableView、QTreeView 和 QListView。多个视图可以观察一个场景,而场景包含各种几何形状的项目。
场景¶
QGraphicsScene 提供了图形视图场景。该场景具有以下职责:
提供一个快速接口来管理大量项目
将事件传播到每个项目
管理项目状态,例如选择和焦点处理
提供未转换的渲染功能;主要用于打印
场景作为QGraphicsItem对象的容器。通过调用addItem()将项目添加到场景中,然后通过调用众多项目发现函数之一来检索项目。items()及其重载返回包含或与点、矩形、多边形或一般矢量路径相交的所有项目。itemAt()返回特定点的最顶层项目。所有项目发现函数按降序堆叠顺序返回项目(即,第一个返回的项目是最顶层的,最后一个项目是最底层的)。
scene = QGraphicsScene() rect = scene.addRect(QRectF(0, 0, 100, 100)) item = scene.itemAt(50, 50, QTransform())
QGraphicsScene 的事件传播架构安排场景事件传递给项目,并管理项目之间的传播。如果场景在某个位置接收到鼠标按下事件,场景会将该事件传递给位于该位置的任何项目。
QGraphicsScene 还管理某些项目状态,例如项目选择和焦点。您可以通过调用 setSelectionArea() 来选择场景上的项目,传递任意形状。此功能还用作 QGraphicsView 中橡皮筋选择的基础。要获取当前所有选定项目的列表,请调用 selectedItems() 。QGraphicsScene 处理的另一个状态是项目是否具有键盘输入焦点。您可以通过调用 setFocusItem() 或 setFocus() 来设置项目的焦点,或通过调用 focusItem() 来获取当前焦点项目。
最后,QGraphicsScene允许您通过render()函数将场景的部分渲染到绘图设备中。您可以在本文档后面的打印部分了解更多信息。
视图¶
QGraphicsView 提供了视图小部件,用于可视化场景的内容。你可以将多个视图附加到同一个场景中,以提供对同一数据集的多个视口。视图小部件是一个滚动区域,并提供了滚动条以便在大型场景中导航。要启用OpenGL支持,你可以通过调用setViewport()将QOpenGLWidget设置为视口。
scene = QGraphicsScene() myPopulateScene(scene) view = QGraphicsView(scene) view.show()
视图接收来自键盘和鼠标的输入事件,并在将这些事件发送到可视化场景之前,将其转换为场景事件(在适当的情况下将使用的坐标转换为场景坐标)。
使用其变换矩阵,transform(),视图可以变换场景的坐标系。这使得诸如缩放和旋转等高级导航功能成为可能。为了方便起见,QGraphicsView 还提供了在视图和场景坐标之间转换的函数:mapToScene() 和 mapFromScene()。
项目¶
QGraphicsItem 是场景中图形项的基础类。图形视图提供了几种标准项用于典型形状,例如矩形(QGraphicsRectItem)、椭圆(QGraphicsEllipseItem)和文本项(QGraphicsTextItem),但当你编写自定义项时,QGraphicsItem 最强大的功能才得以体现。除此之外,QGraphicsItem 支持以下功能:
鼠标按下、移动、释放和双击事件,以及鼠标悬停事件、滚轮事件和上下文菜单事件。
键盘输入焦点和按键事件
拖放
分组,既可以通过父子关系,也可以通过
QGraphicsItemGroup碰撞检测
项目存在于本地坐标系中,与QGraphicsView类似,它也提供了许多函数用于在项目与场景之间以及项目与项目之间映射坐标。此外,与QGraphicsView类似,它可以使用矩阵来变换其坐标系:transform()。这对于旋转和缩放单个项目非常有用。
项目可以包含其他项目(子项目)。父项目的变换会被所有子项目继承。然而,无论项目的累积变换如何,其所有函数(例如,contains(),boundingRect(),QGraphicsItem::collidesWith())仍然在局部坐标中操作。
QGraphicsItem 通过 shape() 函数和 QGraphicsItem::collidesWith() 支持碰撞检测,这两个都是虚函数。通过从 shape() 返回你的项目的形状作为局部坐标的 QPainterPath,QGraphicsItem 将为你处理所有的碰撞检测。然而,如果你想提供自己的碰撞检测,你可以重新实现 QGraphicsItem::collidesWith()。
图形视图框架中的类¶
这些类为创建交互式应用程序提供了一个框架。
QGraphicsEffect 类是所有图形效果的基类。
QGraphicsAnchorLayout 类提供了一个布局,可以在图形视图中将小部件锚定在一起。
QGraphicsAnchor 类表示在 QGraphicsAnchorLayout 中两个项目之间的锚点。
QGraphicsGridLayout 类提供了一个网格布局,用于在图形视图中管理小部件。
QGraphicsItem 类是 QGraphicsScene 中所有图形项的基类。
QGraphicsObject 类为所有需要信号、槽和属性的图形项提供了一个基类。
QAbstractGraphicsShapeItem 类为所有路径项提供了一个共同的基类。
QGraphicsPathItem 类提供了一个可以添加到 QGraphicsScene 的路径项。
QGraphicsRectItem 类提供了一个可以添加到 QGraphicsScene 的矩形项。
QGraphicsEllipseItem 类提供了一个可以添加到 QGraphicsScene 的椭圆项。
QGraphicsPolygonItem 类提供了一个可以添加到 QGraphicsScene 的多边形项。
QGraphicsLineItem 类提供了一个可以添加到 QGraphicsScene 的线条项。
QGraphicsPixmapItem 类提供了一个可以添加到 QGraphicsScene 的像素图项。
QGraphicsTextItem 类提供了一个文本项,您可以将其添加到 QGraphicsScene 中以显示格式化文本。
QGraphicsSimpleTextItem 类提供了一个简单的文本路径项,您可以将其添加到 QGraphicsScene 中。
QGraphicsItemGroup 类提供了一个容器,将一组项目视为单个项目。
QGraphicsItemAnimation 类为 QGraphicsItem 提供了简单的动画支持。
QGraphicsLayout 类为图形视图中的所有布局提供了基类。
QGraphicsLayoutItem 类可以被继承,以允许您的自定义项由布局管理。
QGraphicsLinearLayout 类提供了一个水平或垂直的布局,用于在图形视图中管理小部件。
QGraphicsProxyWidget 类提供了一个代理层,用于将 QWidget 嵌入到 QGraphicsScene 中。
QGraphicsScene 类提供了一个用于管理大量二维图形项目的表面。
QGraphicsSceneBspTreeIndex 类提供了一个 BSP 索引算法的实现,用于在 QGraphicsScene 中发现项目。
QGraphicsSceneEvent 类为所有与图形视图相关的事件提供了一个基类。
QGraphicsSceneMouseEvent 类在图形视图框架中提供鼠标事件。
QGraphicsSceneWheelEvent 类在图形视图框架中提供滚轮事件。
QGraphicsSceneContextMenuEvent 类在图形视图框架中提供上下文菜单事件。
QGraphicsSceneHoverEvent 类在图形视图框架中提供悬停事件。
QGraphicsSceneHelpEvent 类在请求工具提示时提供事件。
QGraphicsSceneDragDropEvent 类为图形视图框架中的拖放操作提供事件。
QGraphicsSceneResizeEvent 类提供了在图形视图框架中用于小部件调整大小的事件。
QGraphicsSceneMoveEvent 类提供了在图形视图框架中移动小部件的事件。
QGraphicsSceneIndex 类提供了一个基类,用于实现自定义索引算法,以便在 QGraphicsScene 中发现项目。
QGraphicsSceneLinearIndex 类提供了一个线性索引算法的实现,用于在 QGraphicsScene 中发现项目。
QGraphicsTransform 类是一个抽象基类,用于在 QGraphicsItems 上构建高级变换。
QGraphicsView 类提供了一个用于显示 QGraphicsScene 内容的小部件。
QGraphicsWidget 类是 QGraphicsScene 中所有小部件项的基类。
QStyleOptionGraphicsItem 类用于描述绘制 QGraphicsItem 所需的参数。
图形视图坐标系¶
图形视图基于笛卡尔坐标系;场景中项目的位置和几何形状由两组数字表示:x坐标和y坐标。当使用未转换的视图观察场景时,场景上的一个单位在屏幕上表示为一个像素。
注意
倒置的Y轴坐标系(其中y向上增长)不受支持,因为图形视图使用Qt的坐标系。
在图形视图中有三个有效的坐标系在起作用:项目坐标、场景坐标和视图坐标。为了简化您的实现,图形视图提供了方便的函数,允许您在这三个坐标系之间进行映射。
在渲染时,Graphics View的场景坐标对应于QPainter的逻辑坐标,而视图坐标与设备坐标相同。在坐标系统文档中,您可以阅读有关逻辑坐标和设备坐标之间关系的内容。
项目坐标¶
项目存在于它们自己的局部坐标系中。它们的坐标通常以其中心点(0, 0)为中心,这也是所有变换的中心。在项目坐标系中的几何原语通常被称为项目点、项目线或项目矩形。
创建自定义项目时,您只需要关注项目坐标;QGraphicsScene 和 QGraphicsView 将为您执行所有转换。这使得实现自定义项目变得非常容易。例如,如果您收到鼠标按下或拖动进入事件,事件位置将以项目坐标给出。contains() 虚函数,如果某个点在您的项目内则返回 true,否则返回 false,它接受一个项目坐标中的点参数。同样,项目的边界矩形和形状也在项目坐标中。
项目的位置是项目在其父坐标系中的中心点坐标;有时称为父坐标。从这个意义上说,场景被视为所有无父项目的“父”。顶级项目的位置在场景坐标中。
子坐标是相对于父坐标的。如果子项未经过变换,子坐标和父坐标之间的差异与父坐标中项目之间的距离相同。例如:如果一个未变换的子项正好位于其父项的中心点,那么这两个项的坐标系将是相同的。然而,如果子项的位置是(10,0),那么子项的(0,10)点将对应于其父项的(10,10)点。
因为项目的位置和变换是相对于父级的,所以子项目的坐标不受父级变换的影响,尽管父级的变换隐式地变换了子项目。在上面的例子中,即使父级被旋转和缩放,子项目的 (0, 10) 点仍然对应于父级的 (10, 10) 点。然而,相对于场景,子项目将跟随父级的变换和位置。如果父级被缩放 (2x, 2x),子项目的位置将在场景坐标 (20, 0) 处,其 (10, 0) 点将对应于场景上的点 (40, 0)。
除了pos()是少数例外之一,QGraphicsItem的函数在项目坐标中操作,无论项目或其任何父项的变换如何。例如,项目的边界矩形(即boundingRect())始终以项目坐标给出。
场景坐标¶
场景代表其所有项目的基础坐标系。场景坐标系描述了每个顶层项目的位置,并且也是从视图传递给场景的所有场景事件的基础。场景上的每个项目除了其本地项目位置和边界矩形外,还有一个场景位置和边界矩形(scenePos(),sceneBoundingRect())。场景位置描述了项目在场景坐标中的位置,其场景边界矩形形成了QGraphicsScene确定场景哪些区域已更改的基础。场景中的更改通过changed()信号进行通信,参数是场景矩形的列表。
查看坐标¶
视图坐标是小部件的坐标。视图坐标中的每个单位对应一个像素。这个坐标系的特点是它是相对于小部件或视口的,并且不受观察场景的影响。QGraphicsView视口的左上角始终是(0, 0),右下角始终是(视口宽度, 视口高度)。所有鼠标事件和拖放事件最初都是以视图坐标接收的,你需要将这些坐标映射到场景中以便与项目进行交互。
坐标映射¶
在处理场景中的项目时,将坐标和任意形状从场景映射到项目、从项目映射到项目或从视图映射到场景通常非常有用。例如,当你在QGraphicsView的视口中点击鼠标时,你可以通过调用mapToScene(),然后调用itemAt()来询问场景光标下的项目是什么。如果你想知道项目在视口中的位置,你可以在项目上调用mapToScene(),然后在视图上调用mapFromScene()。最后,如果你想找到视图椭圆内的项目,你可以将QPainterPath传递给mapToScene(),然后将映射的路径传递给items()。
您可以通过调用mapToScene()和mapFromScene()将坐标和形状映射到项目的场景中或从场景中映射出来。您还可以通过调用mapToParent()和mapFromParent()将坐标和形状映射到项目的父项目中,或者通过调用mapToItem()和mapFromItem()在项目之间进行映射。所有映射函数都可以映射点、矩形、多边形和路径。
视图中提供了相同的映射函数,用于映射到场景和从场景映射。mapFromScene() 和 mapToScene()。要从视图映射到项目,首先映射到场景,然后从场景映射到项目。
主要特点¶
缩放和旋转¶
QGraphicsView 支持与 QPainter 相同的仿射变换,通过 QGraphicsView::setMatrix() 实现。通过对视图应用变换,您可以轻松添加对常见导航功能(如缩放和旋转)的支持。
以下是如何在QGraphicsView的子类中实现缩放和旋转插槽的示例:
class View(QGraphicsView): Q_OBJECT ... # public slots def zoomIn(scale(1.2, 1.2): def zoomOut(1.2, 1.2): def rotateLeft(rotate(-10): def rotateRight(rotate(10): ...
插槽可以连接到启用了autoRepeat的QToolButtons。
QGraphicsView 在转换视图时保持视图中心对齐。
另请参阅Elastic Nodes示例,了解如何实现基本缩放功能的代码。
打印¶
Graphics View 通过其渲染函数 render() 和 render() 提供单行打印功能。这些函数提供相同的 API:您可以通过将 QPainter 传递给任一渲染函数,让场景或视图将其全部或部分内容渲染到任何绘图设备中。此示例展示了如何使用 QPrinter 将整个场景打印到一整页上。
scene = QGraphicsScene() printer = QPrinter() scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt.black), QBrush(Qt.green)) if QPrintDialog(printer).exec() == QDialog.Accepted: painter = QPainter(printer) painter.setRenderHint(QPainter.Antialiasing) scene.render(painter)
场景和视图渲染函数之间的区别在于一个在场景坐标中操作,另一个在视图坐标中操作。render()通常更倾向于用于打印未转换的整个场景片段,例如用于绘制几何数据或打印文本文档。另一方面,render()适合用于截图;其默认行为是使用提供的画家渲染视口的精确内容。
scene = QGraphicsScene() scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt.black), QBrush(Qt.green)) pixmap = QPixmap() painter = QPainter(pixmap) painter.setRenderHint(QPainter.Antialiasing) scene.render(painter) painter.end() pixmap.save("scene.png")
当源区域和目标区域的大小不匹配时,源内容会被拉伸以适应目标区域。通过向您使用的渲染函数传递一个Qt::AspectRatioMode,您可以选择在内容拉伸时保持或忽略场景的宽高比。
拖放¶
因为 QGraphicsView 间接继承了 QWidget,它已经提供了与 QWidget 相同的拖放功能。此外,为了方便起见,图形视图框架为场景以及每个项目提供了拖放支持。当视图接收到拖放操作时,它会将拖放事件转换为 QGraphicsSceneDragDropEvent,然后将其转发到场景。场景接管此事件的调度,并将其发送到鼠标光标下接受拖放的第一个项目。
要从一个项目开始拖动,创建一个QDrag对象,传递一个指向开始拖动的小部件的指针。项目可以同时被多个视图观察,但只有一个视图可以开始拖动。拖动在大多数情况下是由于按下或移动鼠标而开始的,因此在mousePressEvent()或mouseMoveEvent()中,您可以从事件中获取原始小部件指针。例如:
def mousePressEvent(self, event): data = QMimeData() drag = QDrag(event.widget()) drag.setMimeData(data) drag.exec()
要拦截场景的拖放事件,您需要在QGraphicsItem子类中重新实现dragEnterEvent()以及您的特定场景需要的任何事件处理程序。您可以在QGraphicsScene的每个事件处理程序的文档中阅读更多关于图形视图中的拖放信息。
项目可以通过调用setAcceptDrops()来启用拖放支持。为了处理传入的拖放操作,需要重新实现dragEnterEvent()、dragMoveEvent()、dragLeaveEvent()和dropEvent()。
另请参阅拖放机器人示例,以了解图形视图对拖放操作的支持。
光标和工具提示¶
与QWidget类似,QGraphicsItem也支持光标(setCursor())和工具提示(setToolTip())。当鼠标光标进入项目的区域时(通过调用contains()检测),QGraphicsView会激活这些光标和工具提示。
你也可以通过调用setCursor()直接在视图上设置默认光标。
另请参阅拖放机器人示例,了解实现工具提示和光标形状处理的代码。
动画¶
图形视图支持多个级别的动画。你可以通过使用动画框架轻松组装动画。为此,你需要让你的项目继承自QGraphicsObject并将QPropertyAnimation与它们关联。QPropertyAnimation允许对任何QObject属性进行动画处理。
另一个选项是创建一个自定义项,该自定义项继承自QObject和QGraphicsItem。该自定义项可以设置自己的计时器,并在QObject::timerEvent()中使用增量步骤控制动画。
第三个选项,主要是为了与Qt 3中的QCanvas兼容,是通过调用advance()来推进场景,这又会调用advance()。
OpenGL 渲染¶
要启用OpenGL渲染,您只需通过调用setViewport()将一个新的QOpenGLWidget设置为QGraphicsView的视口。如果您想要带有抗锯齿的OpenGL,您需要设置一个具有所需样本数的QSurfaceFormat(参见QSurfaceFormat::setSamples())。
示例:
view = QGraphicsView(scene) gl = QOpenGLWidget() format = QSurfaceFormat() format.setSamples(4) gl.setFormat(format) view.setViewport(gl)
项目组¶
通过将一个项目设置为另一个项目的子项,您可以实现项目分组的最基本功能:项目将一起移动,并且所有变换都从父项传播到子项。
此外,QGraphicsItemGroup 是一个特殊的项,它将子事件处理与用于添加和删除组中项的有用接口结合在一起。将项添加到 QGraphicsItemGroup 将保留项的原始位置和变换,而通常重新设置项的父项会导致子项相对于其新父项重新定位。为了方便起见,您可以通过场景调用 createItemGroup() 来创建 QGraphicsItemGroup。
小部件和布局¶
Qt 4.4 引入了通过 QGraphicsWidget 支持几何和布局感知项的功能。这个特殊的基项类似于 QWidget,但与 QWidget 不同,它不继承自 QPaintDevice;而是继承自 QGraphicsItem。这允许你编写具有事件、信号和槽、大小提示和策略的完整小部件,并且你还可以通过 QGraphicsLinearLayout 和 QGraphicsGridLayout 在布局中管理小部件的几何形状。
QGraphicsWidget¶
在QGraphicsItem的功能和轻量级基础上,QGraphicsWidget提供了两者的最佳结合:来自QWidget的额外功能,如样式、字体、调色板、布局方向及其几何形状,以及来自QGraphicsItem的分辨率独立性和变换支持。由于Graphics View使用真实坐标而不是整数,QGraphicsWidget的几何函数也操作QRectF和QPointF。这也适用于框架矩形、边距和间距。例如,使用QGraphicsWidget指定内容边距为(0.5, 0.5, 0.5, 0.5)并不罕见。您可以创建子部件和“顶层”窗口;在某些情况下,您现在可以使用Graphics View进行高级MDI应用程序。
一些QWidget的属性被支持,包括窗口标志和属性,但不是全部。你应该参考QGraphicsWidget的类文档以获取完整支持的概述。例如,你可以通过将Qt::Window窗口标志传递给QGraphicsWidget的构造函数来创建装饰窗口,但Graphics View目前不支持在macOS上常见的Qt::Sheet和Qt::Drawer标志。
QGraphicsLayout¶
QGraphicsLayout 是专门为 QGraphicsWidget 设计的第二代布局框架的一部分。它的 API 与 QLayout 非常相似。你可以在 QGraphicsLinearLayout 和 QGraphicsGridLayout 中管理小部件和子布局。你也可以通过自己子类化 QGraphicsLayout 来轻松编写自己的布局,或者通过编写 QGraphicsLayoutItem 的适配器子类将自己的 QGraphicsItem 项目添加到布局中。
嵌入式小部件支持¶
Graphics View 提供了无缝支持,可以将任何小部件嵌入到场景中。您可以嵌入简单的小部件,例如 QLineEdit 或 QPushButton,复杂的小部件如 QTabWidget,甚至完整的主窗口。要将您的小部件嵌入到场景中,只需调用 addWidget(),或者创建一个 QGraphicsProxyWidget 的实例来手动嵌入您的小部件。
通过 QGraphicsProxyWidget,Graphics View 能够深度集成客户端小部件的功能,包括其光标、工具提示、鼠标、平板和键盘事件、子部件、动画、弹出窗口(例如,QComboBox 或 QCompleter),以及小部件的输入焦点和激活。QGraphicsProxyWidget 甚至集成了嵌入小部件的标签顺序,以便您可以在嵌入的小部件之间切换。您甚至可以在场景中嵌入一个新的 QGraphicsView 以提供复杂的嵌套场景。
在转换嵌入式小部件时,Graphics View 确保小部件的转换是独立于分辨率的,从而在放大时保持字体和样式的清晰。(请注意,分辨率独立的效果取决于样式。)
性能¶
浮点指令¶
为了准确快速地应用变换和效果到项目上,Graphics View 的构建基于用户硬件能够为浮点指令提供合理性能的假设。
许多工作站和台式计算机配备了适合加速此类计算的硬件,但一些嵌入式设备可能仅提供库来处理数学运算或在软件中模拟浮点指令。
因此,在某些设备上,某些类型的效果可能比预期的要慢。可能可以通过在其他领域进行优化来弥补这种性能损失;例如,通过使用OpenGL来渲染场景。然而,如果这些优化本身也依赖于浮点硬件的存在,那么它们可能会导致性能下降。