【Qt图形视图框架】1-图形视图框架

图形视图框架

图形视图提供了一个用于管理和与大量定制的2D图形项交互的界面,以及一个用于可视化这些项目并且支持缩放和旋转的视图小部件。该框架包括一个事件传递体系结构,它允许场景中的项目具有精确的双精度交互功能。项目可以处理按键事件、鼠标按下、移动、释放和双击事件,它们还可以跟踪鼠标移动。图形视图使用BSP(二进制空间划分)树来提供非常快速的项目查找,因此,即使有数百万个项目的大型场景它也可以实时可视,。图形视图是在Qt 4.2中引入并取代了它的前身QCanvas。

图形视图架构

图形视图提供了一个基于项目的方法来进行模型视图编程,很像便于交互的视图类QTableView, QTreeViewQListView。多个视图可以观察单个场景,场景包含各种几何形状的项目。

场景(The Scene)

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()函数将场景的一部分渲染到一个绘制设备中。

视图(The View)

QGraphicsView提供了视图窗体部件可视化QGraphicsScene中的内容。可以将多个视图附加到同一个场景为同一个数据集提供多个视图。视图小部件是一个滚动区域,提供滚动条用于在大型场景中导航。可以通过调用QGraphicsView::setViewport()来设置一个QOpenGLWidget作为viewport,以此启用OpenGL支持。
在这里插入图片描述

项目(The Item)

QGraphicsItem是场景中图形项的基类。QGraphicsView提供了几个典型形状的标准项,比如矩形(QGraphicsRectItem), 椭圆(QGraphicsEllipseItem) 以及文本 (QGraphicsTextItem), QGraphicsItem最强大的特性是可以编写自定义项目。此外QGraphicsItem支持以下特性:

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

项目存在于本地坐标系统中,类似QGraphicsViewQGraphicsItem提供了许多在项目和场景、项目和项目之间映射坐标的功能。同样,像QGraphicsView一样,它可以使用一个矩阵:QGraphicsItem::transform()来转换它的坐标系统。这对于旋转和缩放单个项目非常有用。项可以包含其他项(子项)。父项的转换会被其所有子项继承。然而,不管项目的累积转换如何,它的所有函数(例如,QGraphicsItem::contains(), QGraphicsItem::boundingRect(), QGraphicsItem::collidesWith())仍然在本地坐标中操作。QGraphicsItem通过QGraphicsItem::shape()函数和QGraphicsItem::collidesWith()函数支持碰撞检测,这两个函数都是虚函数。通过从QGraphicsItem::shape()返回项目的形状作为本地坐标QPainterPath, QGraphicsItem可以通过重新实现QGraphicsItem::collidesWith()进而实现碰撞检测。
在这里插入图片描述

图形视图框架中的类

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

类名描述
QAbstractGraphicsShapeItemCommon base for all path items
图形轨迹项目的公共基类路径
QGraphicsAnchorRepresents an anchor between two items in a QGraphicsAnchorLayout
表示QGraphicsAnchorLayout中两个项目之间的锚
QGraphicsAnchorLayoutLayout where one can anchor widgets together in Graphics View
在图形视图中可以将小部件锚定在一起的布局
QGraphicsEffectThe base class for all graphics effects
所有图形效果的基类
QGraphicsEllipseItemEllipse item that you can add to a QGraphicsScene
可以添加到QGraphicsScene的椭圆项
QGraphicsGridLayoutGrid layout for managing widgets in Graphics View
用于管理图形视图中的小部件的网格布局
QGraphicsItemThe base class for all graphical items in a QGraphicsScene
QGraphicsScene中所有图形项的基类
QGraphicsItemGroupContainer that treats a group of items as a single item
将一组项目当作一个项目进行处理的容器
QGraphicsLayoutThe base class for all layouts in Graphics View
图形视图中所有布局的基类
QGraphicsLayoutItemCan be inherited to allow your custom items to be managed by layouts
可以继承该类实现自定义项目的布局管理
QGraphicsLineItemLine item that you can add to a QGraphicsScene
可以添加到QGraphicsScene的线项目
QGraphicsLinearLayoutHorizontal or vertical layout for managing widgets in Graphics View
水平或垂直布局,用于管理图形视图中的小部件
QGraphicsObjectBase class for all graphics items that require signals, slots and properties
所有需要使用到信号、槽和属性的图形项的基类
QGraphicsPathItemPath item that you can add to a QGraphicsScene
可以添加到QGraphicsScene的轨迹项
QGraphicsPixmapItemPixmap item that you can add to a QGraphicsScene
可以添加到QGraphicsScene的Pixmap项目
QGraphicsPolygonItemPolygon item that you can add to a QGraphicsScene
可以添加到QGraphicsScene中的多边形项
QGraphicsProxyWidgetProxy layer for embedding a QWidget in a QGraphicsScene
用于在QGraphicsScene中嵌入QWidget的代理层
QGraphicsRectItemRectangle item that you can add to a QGraphicsScene
可以添加到QGraphicsScene中的矩形项
QGraphicsSceneSurface for managing a large number of 2D graphical items
用于管理大量2D图形项的场景类
QGraphicsSceneContextMenuEventContext menu events in the graphics view framework
图形视图框架中的上下文菜单事件
QGraphicsSceneDragDropEventEvents for drag and drop in the graphics view framework
用于处理在图形视图框架中拖放的事件
QGraphicsSceneEventBase class for all graphics view related events
所有图形视图相关事件的基类
QGraphicsSceneHelpEventEvents when a tooltip is requested
请求工具提示时的事件
QGraphicsSceneHoverEventHover events in the graphics view framework
在图形视图框架中的悬停事件
QGraphicsSceneMouseEventMouse events in the graphics view framework
图形视图框架中的鼠标事件
QGraphicsSceneMoveEventEvents for widget moving in the graphics view framework
在图形视图框架中小部件移动事件
QGraphicsSceneResizeEventEvents for widget resizing in the graphics view framework
在图形视图框架中调整小部件大小的事件
QGraphicsSceneWheelEventWheel events in the graphics view framework
图形视图框架中的滚轮事件
QGraphicsSimpleTextItemSimple text path item that you can add to a QGraphicsScene
可以添加到QGraphicsScene的简单文本项
QGraphicsSvgItemQGraphicsItem that can be used to render the contents of SVG files
可用于呈现SVG文件的内容的QGraphicsItem
QGraphicsTextItemText item that you can add to a QGraphicsScene to display formatted text
可以添加到QGraphicsScene以显示格式化文本的文本项
QGraphicsTransformAbstract base class for building advanced transformations on QGraphicsItems
用于在QGraphicsItems上构建高级转换的抽象基类
QGraphicsViewWidget for displaying the contents of a QGraphicsScene
用于显示QGraphicsScene内容的窗口小部件
QGraphicsWidgetThe base class for all widget items in a QGraphicsScene
QGraphicsScene中所有窗口小部件项的基类
QStyleOptionGraphicsItemUsed to describe the parameters needed to draw a QGraphicsItem
用于描述绘制QGraphicsItem所需的参数

图形视图的坐标系统

图形视图是基于笛卡尔坐标系;项目在场景中的位置和几何形状由两组数字表示:x坐标和y坐标。当使用未转换视图观察场景时,场景中的一个单元由屏幕上的一个像素表示。
备注Graphics Views使用Qt的坐标系,不支持倒y轴坐标系(y向上增长)。

在图形视图中有三种有效的坐标系统:项目坐标、场景坐标和视图坐标。Graphics View提供了相应的转换函数方便在三个坐标系之间进行映射。
渲染时,图形视图的场景坐标对应QPainter逻辑坐标,视图坐标与设备坐标相同。可以在坐标系统文档中了解逻辑坐标和设备坐标之间的关系。
在这里插入图片描述

项目坐标

项目存在于它们自己的本地坐标系中。它们的坐标通常以中心点(0,0)为中心,这也是所有变换的中心。项目坐标系中的几何图元通常被称为点项目、线项目或矩形项目。
在创建自定义项目时,项目坐标是需要考虑的;QGraphicsSceneQGraphicsView可以实现所有坐标转换。例如,如果项目接收到鼠标按下或拖动进入事件,则事件位置将以项坐标给出。QGraphicsItem::contains()虚函数,如果某个点位于项内,则返回true,否则返回false,它接受项坐标中的point参数。项目的边界矩形和外形包含在项目坐标中
项目所在的位置是项目的中心点在其父坐标系统中的坐标;有时称为父坐标。场景是所有无父项的“父”。顶级项目位于场景坐标中。
子坐标相对于父坐标。如果子坐标未被转换,则子坐标和父坐标之间的差值与父坐标中项之间的距离相同。例如:如果一个未转换的子项目被精确地定位在其父项目的中心点上,那么这两个项目的坐标系将是相同的。如果子项目在父项目坐标系中的位置是(10,0),那么子项目坐标系中的(0,10)将对应于其父项目坐标系中的(10,10)。
因为项的位置转换相对于父项,所以子项的坐标不受父项转换的影响,尽管父项的转换隐式地转换了子项。在上面的例子中,即使父节点被旋转和缩放,子节点的(0,10)点仍然与父节点的(10,10)点相对应。然而,相对于场景,子项会跟随父项的位置变换,如果父项被缩放(2x, 2x),子项的位置将位于场景坐标(20,0),它的(10,0)点将对应于点(40,0)
然而,孩子会跟随父母的转变和位置。如果父节点被缩放(2x, 2x),子节点的位置将位于场景坐标(20,0),它的(10,0)点将对应于场景上的点(40,0)。
在这里插入图片描述
除了
受父项坐标系的影响,QGraphicsItem其他函数在项坐标中操作,不需要考虑父项的转换。例如,一个项目的边界矩形(即QGraphicsItem::boundingRect())总是以项目坐标的形式给出。

场景坐标

场景代表所有项目的基本坐标系。场景坐标系统描述了每个顶级项目的位置,也形成了从视图传递到场景的所有场景事件的基础。场景中的每个项目除了它的本地项目位置和边界矩形( QGraphicsItem::pos() ,QGraphicsItem::boundingRect())。都有一个场景位置和边界矩形(QGraphicsItem::scenePos(),QGraphicsItem::sceneBoundingRect())。场景位置描述了项目在场景坐标中的位置,它的场景边界矩形构成了
如何确定场景的哪些区域发生了变化的基础。场景中的更改通过
信号进行通信,参数是一个场景矩形列表。

视图坐标

视图坐标是窗口小部件的坐标。视图坐标中的每个单元对应一个屏幕像素。这个坐标系的特殊之处在于,它是相对于窗口小部件或视口的,不受观察到的场景的影响。QGraphicsView的viewport的左上角总是(0,0),而右下角总是(viewport width, viewport height)。所有的鼠标事件和拖放事件最初都是作为视图坐标接收的,为了与项目交互,需要将这些坐标映射到场景中。

坐标映射

通常在处理场景中的项目时,将坐标和任意形状从场景映射到项目,从项目映射到项目,或者从视图映射到场景是很有用的。例如,当在QGraphicsView的viewport中点击鼠标时,可以通过调用QGraphicsView::mapToScene()QGraphicsScene::itemAt()来获取光标在场景中对应的项目。可以在项目上调用QGraphicsItem::mapToScene(),然后在视图上调用QGraphicsView::mapFromScene()获取一个项目在视图中的位置。可以在视图中将QPainterPath传递给mapToScene(),然后将映射的路径传递给QGraphicsScene::items()以查找指定范围内的项目。
可以通过调用QGraphicsItem::mapToScene()QGraphicsItem::mapFromScene()将坐标和形状映射到项目的场景中。您还可以通过调用QGraphicsItem:: maptopparent()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); }
    ...
};

当转换视图时,QGraphicsView保持视图的中心对齐。

印刷

图形视图通过其渲染函数QGraphicsScene::render()QGraphicsView::render()提供单行打印。
这些函数提供相同的API:通过将QPainter传递给其中一个渲染函数,可以让场景或视图将它们的全部或部分内容渲染到任何绘制设备中。

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继承和关联QPropertyAnimationQPropertyAnimation允许把任何QObject属性制作成动画???
另一个选择是从QObjectQGraphicsItem中继承的自定义项。项目可以设置自己的计时器,并在QObject::timerEvent()中使用增量步骤控制动画。

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(图形窗口部件)

基于QGraphicsItem的功能,QGraphicsWidget提供了两个最佳的情景:来自QWidget的额外功能,例如样式、字体、调色板、布局方向和它的几何形状,以及来自QGraphicsItem的分辨率独立性和转换支持。因为图形视图使用实数坐标而不是整数,QGraphicsWidget的几何函数也可以在QRectFQPointF上操作。这也适用于框架的矩形,边距和间距。例如,使用QGraphicsWidget,指定内容边距为(0.5,0.5,0.5,0.5)是很常见的。可以创建子窗口和“顶层”窗口;在某些情况下,您现在可以为高级MDI应用程序使用图形视图。
支持QWidget的部分属性,如窗口标志和属性,例如,可以通过将Qt::Window窗口标志传递给QGraphicsWidget的构造函数来创建装饰窗口,但是图形视图目前不支持macOS上常见的Qt::SheetQt::Drawer标志。具体可参考QGraphicsWidget

QGraphicsLayout(图形布局)

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

嵌入式小部件支持

图形视图支持在场景中嵌入任何小部件。如嵌入简单的小部件QLineEditQPushButton,或者复杂的小部件QTabWidget,甚至完整的主窗口。要将小部件嵌入到场景中,只需调用QGraphicsScene::addWidget(),或者创建一个QGraphicsProxyWidget实例来手动嵌入小部件。
通过QGraphicsProxyWidget,图形视图能够深度集成客户端小部件功能,包括它的游标、工具提示、鼠标、平板电脑和键盘事件、子小部件、动画、弹出窗口(例如QComboBoxQCompleter),以及小部件的焦点输入和激活。QGraphicsProxyWidget甚至集成了嵌入式小部件的选项卡顺序,以便在嵌入式小部件中插入或取出选项卡。甚至可以在场景中嵌入一个新的
来提供复杂的嵌套场景。

性能

浮点比较指令

为了准确、快速地对项目应用转换和效果,在构建图形视图时假定用户的硬件能够为浮点指令提供合理的性能。
许多工作站和桌面计算机都配备了适当的硬件来加速这类计算,但一些嵌入式设备可能只提供处理数学运算或模拟软件中的浮点指令的库。
因此,在某些设备上,某些类型的效果可能比预期的要慢。也许可以通过在其他领域进行优化来弥补这种性能上的损失;例如,使用OpenGL渲染一个场景。但是,如果任何此类优化也依赖于浮点硬件,那么它们本身可能会导致性能下降。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值