图形视图框架

图形视图(Graphics View)提供了一个界面,用于管理大量定制的二维图形项目并与之交互,还提供了一个视图部件,用于将这些项目可视化,并支持缩放和旋转。

该框架包括一个事件传播架构,可为场景中的项目提供精确的双精度交互功能。项目可以处理按键事件、鼠标按下、移动、释放和双击事件,还可以跟踪鼠标移动。

图形视图使用 BSP(二进制空间分区)树来提供非常快的项目发现速度,因此,即使有数百万个项目,它也能实时可视化大型场景。

图形视图在 Qt 4.2 中推出,取代了其前身 QCanvas。

图形视图架构

图形视图提供了一种基于项的模型视图编程方法,这与 InterView 的便利类QTableViewQTreeViewQListView 非常相似。多个视图可以观察一个场景,场景包含不同几何形状的项目。

场景

QGraphicsScene 提供了图形视图场景。场景的职责如下

  • 为管理大量项目提供快速接口
  • 向每个项目传播事件
  • 管理项目状态,如选择和焦点处理
  • 提供未转换的渲染功能;主要用于打印

场景是QGraphicsItem 对象的容器。项目通过调用QGraphicsScene::addItem() 添加到场景中,然后通过调用众多项目发现函数中的一个来获取。QGraphicsScene::items() 及其重载函数会返回由点、矩形、多边形或一般矢量路径包含或与之相交的所有项目。QGraphicsScene::itemAt() 返回某一特定点的最上项。所有项目发现函数都以降序堆叠方式返回项目(即第一个返回的项目为最上,最后一个返回的项目为最下)。

QGraphicsScene scene;
QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100));

QGraphicsItem *item = scene.itemAt(50, 50, QTransform());

QGraphicsScene事件传播架构会安排场景事件传递给项目,并管理项目之间的传播。如果场景在某个位置接收到鼠标按下事件,场景就会将该事件传递给位于该位置的项目。

QGraphicsScene 此外,"场景 "还管理某些项目状态,如项目选择和焦点。您可以通过调用 () 并传递任意形状来选择场景中的项目。这一功能也是 中橡皮筋选择的基础。要获取当前所有选中项的列表,请调用 () 。 处理的另一种状态是项目是否具有键盘输入焦点。您可以通过调用 () 或 () 设置项目的焦点,或通过调用 () 获取当前焦点项目。QGraphicsScene::setSelectionArea QGraphicsView QGraphicsScene::selectedItems QGraphicsScene QGraphicsScene::setFocusItem QGraphicsItem::setFocus QGraphicsScene::focusItem

最后,QGraphicsScene 允许您通过QGraphicsScene::render() 函数将部分场景渲染到绘画设备中。您可以在本文档后面的 "打印 "部分了解更多相关信息。

视图

QGraphicsView 提供了视图部件,可将场景内容可视化。您可以将多个视图附加到同一场景,为同一数据集提供多个视口。视图部件是一个滚动区域,并提供滚动条用于浏览大型场景。要启用 OpenGL 支持,可以通过调用 () 将 设置为视口。QGraphicsView::setViewport QOpenGLWidget

QGraphicsScene scene;
myPopulateScene(&scene);
QGraphicsView view(&scene);
view.show();

视图接收来自键盘和鼠标的输入事件,并将这些事件转换为场景事件(酌情将使用的坐标转换为场景坐标),然后将事件发送到可视化场景。

视图可以使用其转换矩阵QGraphicsView::transform()转换场景坐标系。这样就可以实现缩放和旋转等高级导航功能。为方便起见,QGraphicsView 还提供了视图和场景坐标之间的转换函数:QGraphicsView::mapToScene() 和QGraphicsView::mapFromScene()。

QGraphicsItem 是场景中图形项目的基类。Graphics View 为典型图形提供了几个标准项,如矩形 ( )、椭圆 ( ) 和文本项 ( ),但最强大的 功能需要编写自定义项时才能使用。除其他功能外, 还支持以下功能:QGraphicsRectItemQGraphicsEllipseItemQGraphicsTextItem QGraphicsItem QGraphicsItem

  • 鼠标按下、移动、释放和双击事件,以及鼠标悬停事件、滚轮事件和上下文菜单事件。
  • 键盘输入焦点和按键事件
  • 拖放
  • 通过父子关系和以下方式进行分组QGraphicsItemGroup
  • 碰撞检测

项目存在于本地坐标系中,与QGraphicsView 一样,它也提供了许多在项目与场景之间以及项目与项目之间映射坐标的功能。此外,与QGraphicsView 一样,它也可以使用矩阵转换坐标系:QGraphicsItem::transform() 。这对于旋转和缩放单个项目非常有用。

项目可以包含其他项目(子项目)。父项的变换会被所有子项继承。无论项目的累积变换如何,它的所有函数(如QGraphicsItem::contains(),QGraphicsItem::boundingRect(), QGraphicsItem::collidesWith()) 仍在本地坐标下运行。

QGraphicsItem QGraphicsItem::collidesWith() 通过 () 函数和 QGraphicsItem::collidesWith() 支持碰撞检测,这两个函数都是虚拟函数。通过从 () 返回作为本地坐标的项目形状 , 将为您处理所有碰撞检测。如果您想提供自己的碰撞检测,可以重新实现 QGraphicsItem::collidesWith()。QGraphicsItem::shape QGraphicsItem::shape QPainterPath QGraphicsItem

图形视图框架中的类

这些类为创建交互式应用程序提供了一个框架。

QAbstractGraphicsShapeItem

所有路径项的共同基础

QGraphicsAnchor

代表 QGraphicsAnchorLayout 中两个项目之间的锚点

QGraphicsAnchorLayout

可在图形视图中将部件锚定在一起的布局

QGraphicsEffect

所有 Graphical Effects 的基类

QGraphicsEllipseItem

可添加到 QGraphicsScene 中的椭圆项目

QGraphicsGridLayout

在图形视图中管理部件的网格布局

QGraphicsItem

QGraphicsScene 中所有图形项的基类

QGraphicsItemGroup

将一组项目视为单个项目的容器

QGraphicsLayout

图形视图中所有布局的基类

QGraphicsLayoutItem

可被继承,以便通过布局管理自定义项

QGraphicsLineItem

可添加到 QGraphicsScene 的线条项目

QGraphicsLinearLayout

用于在图形视图中管理部件的水平或垂直布局

QGraphicsObject

所有需要信号、插槽和属性的图形项的基类

QGraphicsPathItem

可添加到 QGraphicsScene 的路径项

QGraphicsPixmapItem

可添加到 QGraphicsScene 的像素图项

QGraphicsPolygonItem

可添加到 QGraphicsScene 的多边形项

QGraphicsProxyWidget

用于在 QGraphicsScene 中嵌入 QWidget 的代理层

QGraphicsRectItem

可添加到 QGraphicsScene 的矩形项

QGraphicsScene

用于管理大量 2D 图形项目的表面

QGraphicsSceneContextMenuEvent

图形视图框架中的上下文菜单事件

QGraphicsSceneDragDropEvent

图形视图框架中的拖放事件

QGraphicsSceneEvent

所有图形视图相关事件的基类

QGraphicsSceneHelpEvent

请求工具提示时的事件

QGraphicsSceneHoverEvent

图形视图框架中的悬停事件

QGraphicsSceneMouseEvent

图形视图框架中的鼠标事件

QGraphicsSceneMoveEvent

图形视图框架中部件移动事件

QGraphicsSceneResizeEvent

图形视图框架中调整部件大小的事件

QGraphicsSceneWheelEvent

图形视图框架中的滚轮事件

QGraphicsSimpleTextItem

可添加到 QGraphicsScene 的简单文本路径项

QGraphicsSvgItem

可用于渲染 SVG 文件内容的 QGraphicsItem

QGraphicsTextItem

可添加到 QGraphicsScene 以显示格式化文本的文本项

QGraphicsTransform

用于在 QGraphicsItems 上建立高级转换的抽象基类

QGraphicsView

用于显示 QGraphicsScene 内容的小工具

QGraphicsWidget

QGraphicsScene 中所有 widget 项目的基类

QStyleOptionGraphicsItem

用于描述绘制 QGraphicsItem 所需的参数

图形视图坐标系

图形视图基于笛卡尔坐标系;项目在场景中的位置和几何形状由两个数组表示:x 坐标和 y 坐标。使用未变换视图观察场景时,场景中的一个单位在屏幕上表示为一个像素。

注意: 由于图形视图使用 Qt XML 的坐标系,因此不支持倒置 Y 轴坐标系(y 向上增长)。

在 Graphical View 中,有三种有效的坐标系在起作用:项目坐标、场景坐标和视图坐标。为简化实现过程,Graphics View 提供了方便的函数,让您可以在这三个坐标系之间进行映射。

渲染时,Graphics View 的场景坐标与QPainter逻辑坐标相对应,视图坐标与设备坐标相同。在坐标系文档中,您可以了解到逻辑坐标与设备坐标之间的关系。

项目坐标

项目生活在自己的本地坐标系中。它们的坐标通常以中心点(0,0)为中心,这也是所有变换的中心点。项目坐标系中的几何基元通常称为项目点、项目线或项目矩形。

创建自定义项目时,只需关注项目坐标即可;QGraphicsSceneQGraphicsView 将为您执行所有变换。这使得自定义项目的实现变得非常容易。例如,如果您接收到鼠标按下或拖动输入事件,事件位置将以项坐标形式给出。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()) 总是以项目坐标给出的。

场景坐标

场景代表所有项目的基本坐标系。场景坐标系描述了每个顶层项的位置,也构成了从视图传递到场景的所有场景事件的基础。场景中的每个项目都有一个场景位置和边界矩形(QGraphicsItem::scenePos(),QGraphicsItem::sceneBoundingRect()), 此外还有其本地项目位置和边界矩形。场景位置描述了项目在场景坐标中的位置,而场景边界矩形则是QGraphicsScene 确定场景中哪些区域发生了变化的基础。场景中的变化通过QGraphicsScene::changed() 信号传递,参数是一个场景矩形列表。

视图坐标

视图坐标是部件的坐标。视图坐标中的每个单位对应一个像素。这个坐标系的特别之处在于,它是相对于部件或视口而言的,不受观察场景的影响。QGraphicsView 的视口左上角始终为(0,0),右下角始终为(视口宽,视口高)。所有鼠标事件和拖放事件最初都是以视图坐标接收的,您需要将这些坐标映射到场景中才能与项目进行交互。

坐标映射

在处理场景中的项目时,经常需要将坐标和任意形状从场景映射到项目、从项目映射到项目或从视图映射到场景。例如,当您在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 QGraphicsView::setMatrix() 支持与 相同的仿射变换。通过对视图应用变换,您可以轻松添加对缩放和旋转等常用导航功能的支持。QPainter

下面的示例说明了如何在QGraphicsView 的子类中实现缩放和旋转槽:

class View : public QGraphicsView
{
Q_OBJECT
    ...
public slots:
    void zoomIn() { scale(1.2, 1.2); }
    void zoomOut() { scale(1 / 1.2, 1 / 1.2); }
    void rotateLeft() { rotate(-10); }
    void rotateRight() { rotate(10); }
    ...
};

这些槽可以连接到启用了autoRepeatQToolButtons

QGraphicsView 当您变换视图时,可保持视图中心对齐。

有关如何实现基本缩放功能的代码,请参阅弹性节点示例。

打印

图形视图通过其渲染函数QGraphicsScene::render() 和QGraphicsView::render() 提供单行打印功能。这些函数提供相同的 API:您可以通过向任一渲染函数传递QPainter ,让场景或视图将其全部或部分内容渲染到任何绘画设备中。本例展示了如何使用 QPrinter 将整个场景打印成整页。

QGraphicsScene scene;
QPrinter printer;
scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));

if (QPrintDialog(&printer).exec() == QDialog::Accepted) {
    QPainter painter(&printer);
    painter.setRenderHint(QPainter::Antialiasing);
    scene.render(&painter);
}

场景渲染函数和视图渲染函数的区别在于,一个是在场景坐标下操作,另一个是在视图坐标下操作。QGraphicsScene::render(在打印未转换的整个场景片段时,如绘制几何数据或打印文本文档时,通常首选()。QGraphicsView::render而 () 则适用于截图;它的默认行为是使用所提供的绘制器渲染视口的准确内容。

QGraphicsScene scene;
scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));

QPixmap pixmap;
QPainter painter(&pixmap);
painter.setRenderHint(QPainter::Antialiasing);
scene.render(&painter);
painter.end();

pixmap.save("scene.png");

当源区域和目标区域的大小不一致时,源内容会被拉伸以适应目标区域。通过向您使用的渲染函数传递Qt::AspectRatioMode ,您可以选择在拉伸内容时保持或忽略场景的纵横比。

拖放

由于QGraphicsView 间接继承了QWidget ,因此它已经提供了与QWidget 相同的拖放功能。此外,为了方便起见,图形视图框架还为场景和每个项目提供了拖放支持。当视图接收到拖放时,它会将拖放事件转化为QGraphicsSceneDragDropEvent ,然后转发给场景。场景会接管该事件的调度,并将其发送到鼠标光标下第一个接受拖放的项目。

要从一个项目开始拖动,可创建一个QDrag 对象,并将指针传给开始拖动的部件。多个视图可以同时观察项目,但只有一个视图可以启动拖动。大多数情况下,拖动都是通过按下或移动鼠标开始的,因此在 mousePressEvent() 或 mouseMoveEvent() 中,可以从事件中获取开始拖动的部件指针。例如

void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QMimeData *data = new QMimeData;
    QDrag *drag = new QDrag(event->widget());
    drag->setMimeData(data);
    drag->exec();
}

要拦截场景的拖放事件,可在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() 直接在视图上设置默认光标。

有关实现工具提示和光标形状处理的代码,请参阅拖放机器人示例。

动画

图形视图支持多个级别的动画。通过使用动画框架,您可以轻松组装动画。为此,您需要从QGraphicsObject 继承您的项目,并将QPropertyAnimation 与之关联。QPropertyAnimation 允许将QObject 的任何属性制作成动画。

另一种方法是创建一个继承自QObjectQGraphicsItem 的自定义项。该项目可以设置自己的计时器,并通过QObject::timerEvent() 中的递增步骤控制动画。

第三种方法主要是为了与 Qt XML 3 中的 QCanvas 兼容,即通过调用QGraphicsScene::advance() 来推进场景,而 () 又会调用QGraphicsItem::advance() 。

OpenGL 渲染

要启用 OpenGL 渲染,只需通过调用QGraphicsView::setViewport() 将新的QOpenGLWidget 设置为QGraphicsView 的视口即可。如果要使用带有抗锯齿功能的 OpenGL,则需要设置一个具有所需样本数的QSurfaceFormat (请参阅QSurfaceFormat::setSamples() )。

示例

QGraphicsView view(&scene);
QOpenGLWidget *gl = new QOpenGLWidget();
QSurfaceFormat format;
format.setSamples(4);
gl->setFormat(format);
view.setViewport(gl);

项目组

通过将一个项目作为另一个项目的子项,可以实现项目分组的最基本特征:项目将一起移动,并且所有变换都会从父项传播到子项。

此外,QGraphicsItemGroup 是一个特殊的项目,它将子项目事件处理与一个有用的界面结合在一起,用于在组中添加和删除项目。将一个项目添加到QGraphicsItemGroup 时,将保持项目的原始位置和变换,而一般情况下,项目的再父代化将导致子代相对于其新父代重新定位。为方便起见,您可以通过调用QGraphicsScene::createItemGroup() 在场景中创建QGraphicsItemGroups。

小工具和布局

Qt 4.4 通过QGraphicsWidget 引入了对几何和布局感知项的支持。这个特殊的基本项与QWidget 类似,但与QWidget 不同的是,它不是从QPaintDevice 继承而来,而是从QGraphicsItem 继承而来。这样,您就可以编写具有事件、信号和插槽、尺寸提示和策略的完整部件,还可以通过QGraphicsLinearLayoutQGraphicsGridLayout 在布局中管理部件的几何形状。

QGraphicsWidget

QGraphicsWidgetQGraphicsItem 的功能和精简的占用空间的基础上,提供了两个世界的最佳功能:来自QWidget 的额外功能,如样式、字体、调色板、布局方向及其几何图形,以及来自QGraphicsItem 的分辨率独立性和变换支持。由于图形视图使用实数坐标而非整数,因此QGraphicsWidget 的几何图形函数也可在QRectFQPointF 上运行。这也适用于框架矩形、边距和间距。例如,在QGraphicsWidget 中,指定内容边距为 (0.5, 0.5, 0.5, 0.5) 并不罕见。您既可以创建子窗口,也可以创建 "顶层 "窗口;在某些情况下,您还可以使用图形视图来创建高级 MDI 应用程序。

支持QWidget 的部分属性,包括窗口标志和属性,但并非全部。您应参阅QGraphicsWidget 的类文档,全面了解支持和不支持的属性。例如,您可以通过向QGraphicsWidget 的构造函数传递Qt::Window 窗口标志来创建装饰窗口,但 Graphics View 目前不支持在 macOS 上常见的Qt::SheetQt::Drawer 标志。

QGraphicsLayout

QGraphicsLayout 是专为 设计的第二代布局框架的一部分。它的 API 与 非常相似。你可以在 和 中管理部件和子布局。你也可以通过子类化 轻松编写自己的布局,或通过编写 的适配器子类将自己的 项目添加到布局中。QGraphicsWidget QLayout QGraphicsLinearLayout QGraphicsGridLayout QGraphicsLayout QGraphicsLayoutItem QGraphicsItem

嵌入式小工具支持

Graphics View 为在场景中嵌入任何部件提供了无缝支持。您可以嵌入简单的 Widget(如QLineEditQPushButton )、复杂的 Widget(如QTabWidget )甚至完整的主窗口。要将窗口小部件嵌入到场景中,只需调用QGraphicsScene::addWidget() 或创建QGraphicsProxyWidget 的实例即可手动嵌入窗口小部件。

通过QGraphicsProxyWidget ,Graphics View 可以深度集成客户端 widget 的功能,包括其光标、工具提示、鼠标、平板电脑和键盘事件、子 widget、动画、弹出式窗口(如QComboBoxQCompleter ),以及 widget 的输入焦点和激活。QGraphicsProxyWidget 甚至还集成了嵌入式 widget 的标签顺序,这样您就可以标签进出嵌入式 widget。您甚至可以在场景中嵌入一个新的QGraphicsView ,以提供复杂的嵌套场景。

在转换嵌入式窗口小部件时,Graphics View 可确保窗口小部件的转换与分辨率无关,从而使字体和样式在放大时保持清晰。(请注意,分辨率独立的效果取决于样式)。

性能

浮点指令

为了准确、快速地将变换和特效应用到项目中,Graphical Effects 在构建时假定用户的硬件能够提供合理的浮点指令性能。

许多工作站和台式电脑都配备了合适的硬件来加速此类计算,但一些嵌入式设备可能只提供处理数学运算的库或在软件中模拟浮点指令。

因此,在某些设备上,某些类型的效果可能比预期的要慢。通过在其他方面进行优化,例如使用OpenGL渲染场景,也许可以弥补这种性能损失。不过,任何此类优化如果也依赖于浮点硬件的存在,其本身可能会导致性能下降。

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.