本页内容

图形视图框架

Graphics View 提供了一个用于管理和与大量自定义 2D 图形项交互的操作面,以及一个用于可视化这些项的视图小部件,并支持缩放和旋转。

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

Graphics View 使用 BSP(二进制空间划分)树来实现极快的对象发现,因此即使场景中包含数百万个对象,也能实时进行可视化。

Graphics View 于 Qt 4.2 中推出,取代了其前身 QCanvas。

Graphics View 架构

Graphics View 为模型-视图编程提供了一种基于项的方法,这与 InterView 的便利类QTableView 、QTreeView 和QListView 非常相似。多个视图可以观察同一个场景,而该场景包含各种几何形状的项。

场景

QGraphicsScene 类提供了 Graphics View 的场景。该场景承担以下职责:

  • 提供用于管理大量对象的高效接口
  • 将事件传播至每个项目
  • 管理对象状态,例如选择和焦点处理
  • 提供未经变换的渲染功能;主要用于打印

该场景充当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()。

计算机芯片阵列

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 框架中的类

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

QAbstractGraphicsShapeItem

所有路径项的公共基类

QGraphicsAnchor

表示 QGraphicsAnchorLayout 中两个项之间的锚点

QGraphicsAnchorLayout

布局,在该布局中可以将小部件锚定在一起

QGraphicsEffect

所有Graphical Effects的基类

QGraphicsEllipseItem

可添加到 QGraphicsScene 中的椭圆项目

QGraphicsGridLayout

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

QGraphicsItem

QGraphicsScene 中所有图形项的基类

QGraphicsItemGroup

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

QGraphicsLayout

Graphics View 中所有布局的基类

QGraphicsLayoutItem

可通过继承使其自定义项由布局进行管理

QGraphicsLineItem

可添加到 QGraphicsScene 中的行项目

QGraphicsLinearLayout

用于在 Graphics View 中管理控件的水平或垂直布局

QGraphicsObject

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

QGraphicsPathItem

可添加到 QGraphicsScene 中的路径项

QGraphicsPixmapItem

可添加到 QGraphicsScene 中的位图(Pixmap)项

QGraphicsPolygonItem

可添加到 QGraphicsScene 中的多边形项

QGraphicsProxyWidget

用于将 QWidget 嵌入 QGraphicsScene 的代理层

QGraphicsRectItem

可添加到 QGraphicsScene 中的矩形项

QGraphicsScene

用于管理大量 2D 图形项的 Surface

QGraphicsSceneContextMenuEvent

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

QGraphicsSceneDragDropEvent

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

QGraphicsSceneEvent

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

QGraphicsSceneHelpEvent

请求工具提示时的事件

QGraphicsSceneHoverEvent

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

QGraphicsSceneMouseEvent

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

QGraphicsSceneMoveEvent

图形视图框架中控件移动的事件

QGraphicsSceneResizeEvent

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

QGraphicsSceneWheelEvent

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

QGraphicsSimpleTextItem

可添加到 QGraphicsScene 中的简单文本项

QGraphicsSvgItem

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

QGraphicsTextItem

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

QGraphicsTransform

用于在 QGraphicsItem 上构建高级变换的抽象基类

QGraphicsView

用于显示 QGraphicsScene 内容的控件

QGraphicsWidget

QGraphicsScene 中所有小部件项的基类

QStyleOptionGraphicsItem

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

图形视图坐标系

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

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

Graphics View 中实际涉及三个坐标系:对象坐标、场景坐标和视图坐标。为了简化您的实现,Graphics View 提供了便利函数,允许您在三个坐标系之间进行映射。

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

几种坐标系变换

项目坐标

项目位于其自身的本地坐标系中。其坐标通常以中心点 (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())始终以对象坐标表示。

场景坐标系

场景代表了其所有项的基础坐标系。场景坐标系描述了每个顶级项的位置,同时也构成了从视图传递到场景的所有场景事件的基础。 场景中的每个项除了具有本地项位置和边界矩形外,还拥有场景位置和场景边界矩形(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); }
    //...
};

这些槽可以连接到QToolButtons ,同时启用autoRepeat 。

QGraphicsView 在对视图进行变换时,该选项可保持视图中心的对齐。

另请参阅“弹性节点”(Elastic Nodes)示例,其中展示了如何实现基本缩放功能的代码。

打印

Graphics View 通过其渲染函数 `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 相同拖放功能。此外,为了方便起见,Graphics View框架为场景以及其中的每一个项目都提供了拖放支持。当视图接收到拖动操作时,它会将拖放事件转换为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()以及该场景所需的其他事件处理程序。有关Graphics View中拖放功能的更多信息,请参阅QGraphicsScene 中各事件处理程序的文档。

项目可以通过调用QGraphicsItem::setAcceptDrops()来启用拖放支持。要处理传入的拖放操作,请重写QGraphicsItem::dragEnterEvent()、QGraphicsItem::dragMoveEvent()、QGraphicsItem::dragLeaveEvent()和QGraphicsItem::dropEvent()方法。

另请参阅“拖放机器人”示例,了解 Graphics View 对拖放操作的支持情况。

光标和工具提示

与QWidget 类似,QGraphicsItem 也支持光标(QGraphicsItem::setCursor())和工具提示(QGraphicsItem::setToolTip())。当鼠标光标进入项的区域时(通过调用QGraphicsItem::contains()检测),QGraphicsView 会激活光标和工具提示。

您还可以通过调用QGraphicsView::setCursor() 直接在视图上设置默认光标。

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

动画

Graphics View 支持多层次的动画效果。您可以通过使用动画框架(Animation Framework)轻松构建动画。为此,您的项目需要继承自 `QGraphicsObject `,并为其关联 `QPropertyAnimation `。`QPropertyAnimation ` 允许对任何 `QObject ` 属性进行动画处理。

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

第三种方案(主要用于兼容 Qt 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()` 在场景中创建 `QGraphicsItemGroup`。

控件与布局

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 的类文档,以全面了解哪些功能受支持、哪些不受支持。例如,您可以通过向QGraphicsWidget 的构造函数传递Qt::Window 窗口标志来创建装饰窗口,但Graphics View目前不支持macOS上常见的Qt::Sheet 和Qt::Drawer 标志。

QGraphicsLayout

QGraphicsLayout 是专为QGraphicsWidget 设计的第二代布局框架的一部分。其API与QLayout 非常相似。您可以在QGraphicsLinearLayout 和QGraphicsGridLayout 中管理小部件和子布局。您还可以通过继承QGraphicsLayout 来轻松编写自己的布局,或者通过编写QGraphicsLayoutItem 的适配器子类,将自定义的QGraphicsItem 项添加到布局中。

嵌入式控件支持

Graphics View 提供了将任何控件无缝嵌入场景的支持。您可以嵌入简单的控件(如QLineEdit 或QPushButton ),复杂的控件(如QTabWidget ),甚至完整的主窗口。要将控件嵌入场景,只需调用QGraphicsScene::addWidget() ,或创建QGraphicsProxyWidget 的实例来手动嵌入控件。

通过QGraphicsProxyWidget ,Graphics View 能够深度集成客户端小部件的功能,包括其光标、工具提示、鼠标、数位板和键盘事件、子小部件、动画、弹出窗口(例如QComboBox 或QCompleter ),以及小部件的输入焦点和激活状态。QGraphicsProxyWidget 甚至集成了嵌入式控件的 Tab 键导航顺序,以便您通过 Tab 键在嵌入式控件之间切换。您甚至可以将新的QGraphicsView 嵌入到场景中,以构建复杂的嵌套场景。

在对嵌入式控件进行变换时,图形视图会确保该控件以分辨率独立的方式进行变换,从而在放大时保持字体和样式的清晰度。(请注意,分辨率独立性的效果取决于样式。)

性能

浮点指令

为了准确、快速地对项目应用变换和特效,Graphics View 的设计基于以下假设:用户的硬件能够为浮点指令提供合理的性能。

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

因此,在某些设备上,特定类型的特效运行速度可能会低于预期。可以通过在其他方面进行优化来弥补这种性能损失;例如,使用OpenGL渲染场景。但是,如果此类优化本身也依赖于浮点硬件的存在,则可能会导致性能下降。

© 2026 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.