若该文为原创文章,转载请注明原文出处  

各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究

红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…(点击传送门) 


前话

  使用到Qt的视图框架。


Qt视图框架介绍

简介

  图形视图框架(The Graphic View Framework)用于管理和与大量定制的二维图形项目交互,以及用于可视化项目的视图小部件,支持缩放和旋转。
  Qt开发技术:图形视图框架(一)基本介绍_QGraphic

  该框架包括一个事件传播体系结构,允许对场景中的项目进行精确的双精度交互。项目可以处理按键事件、鼠标按下、移动、释放和双击事件,还可以跟踪鼠标移动。
  图形视图使用BSP(Binary Space Partitioning,即二进制空间分区)树提供非常快读得项目发现,因此,它可以实时现实大型场景,即使有数百万个项目。
  图形视图提供了一种基于项目得模型视图变成方法,多个视图可以观察单个场景,并且场景包含不同几何图形的项目。

场景(The Scene)

  QGraphicsScene提供了图形视图场景,场景有如下职责:

  • 为管理大量项目(图元项目)提供快速界面;(实测同一个区域不能重叠多了,绘制会卡顿)
  • 将事件传播到每个项;
  • 管理项目状态,如选择和焦点处理;
  • 提供未转换的渲染功能;主要用于打印;
      该场景用作QGraphicsItem对象的容器。通过调用QGraphicsItem::additem() 将项添加到场景中,然后通过调用多个项发现函数之一来检索项。QGraphicsItem::items()及其重载返回由点、矩形、多边形或常规矢量路径包含或与之相交的所有项。QGraphicsItem::itemAt()返回特定点的最上面的项。所有项目发现功能都按降序堆叠顺序返回项目(即第一个返回的项目是最上面的,最后一个项目是最下面的)。
QGraphicsScene scene;
QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100))
QTransform transform;;
QGraphicsItem *item = scene.itemAt(50, 50, transform);
// item == rect

  QGraphicsScene的事件传播体系结构安排将场景事件传递到项目,并管理项目之间的传播。如果场景在某个位置接收到鼠标按下事件,则场景将事件传递到该位置的任何项目。
  QGraphicsScene还管理某些项状态,例如项选择和焦点。可以通过调用QGraphicsScene::setSelectionArea(),传递任意形状来选择场景中的项目。该功函数还可用作QGraphicsView中橡皮擦的选择区域。要获取所有当前选定项的列表,请调用QGraphicscene::selecteditems()。QGraphicsScene处理的另一个状态是项是否具有键盘输入焦点。可以通过调用QGraphicScene::setFocusItem()或 QGraphicsItem::setFocus()对项设置焦点,也可以通过调用QGraphicscene::focusItem()获取当前焦点项。
  最后,QGraphicScene允许您通过QGraphicScene::render()函数将部分场景渲染到绘制设备中。可以“打印”部分了解更多有关此内容的信息。

视图(The View)

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

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

  在将事件发送到可视化场景之前,视图从键盘和鼠标接收输入事件,并将其转换为场景事件(在适当的情况下将使用的坐标转换为场景坐标)。
  用转换矩阵QGraphicsView::transform(),视图可以转换场景的坐标系。这允许高级导航功能,如缩放和旋转。为了方便起见,qgraphicsView还提供了在视图和场景坐标之间转换的函数:QGraphicsView::mapToScene()和 QGraphicsView::mapFromScene()。
  Qt开发技术:图形视图框架(一)基本介绍_QGraphic_02

项目(The Item):图形元素

QGraphicsItem是场景中图形项的基类。图形视图为典型形状提供了几个标准项,例如矩形(QGraphicsRectItem)、椭圆(QGraphicsEllipsItem)和文本项(QGraphicsTextItem),但在编写自定义项时,最强大的QGraphicsItem功能可用。除此之外,QGraphicsItem还支持以下功能:

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

  项目位于本地坐标系中,与QGraphicsView类似,它还提供许多功能,用于在项目和场景之间以及从项目到项目之间映射坐标。此外,与QGraphicsView一样,它可以使用矩阵:QGraphicsItem::Transform()转换其坐标系。这对于旋转和缩放单个项目很有用。
  项可以包含其他项(子项)。父项的转换由其所有子项继承。但是,不管一个项的累积转换如何,它的所有函数(例如,QGraphicsItem::contains()QGraphicsItem::boundingRect()QGraphicsItem::collectsWith() 仍在本地坐标中操作。
  QGraphicsItem支持通过QGraphicsItem::shape()函数和QGraphicsItem::collipswith() 进行冲突检测,这两个函数都是虚拟函数。通过从QGraphicsItem::shape() 中返回项目的形状作为本地坐标QPaineterPath,QGraphicsItem将为您处理所有冲突检测。但是,如果您希望提供自己的碰撞检测,则可以重新实现QGraphicsItem::CollipsWith()
  Qt开发技术:图形视图框架(一)基本介绍_图形视图_03

Qt图形视图框架中的类

  这些类为创建交互式应用程序提供了一个框架。
  Qt开发技术:图形视图框架(一)基本介绍_QGraphic_04


图形视图坐标系

  图形视图基于笛卡尔坐标系;场景中项目的位置和几何图形由两组数字表示:X坐标和Y坐标。使用未转换视图观察场景时,场景中的一个单元由屏幕上的一个像素表示。
  注意:由于图形视图使用qt坐标系,因此不支持倒Y轴坐标系(Y向上增长)。
  图形视图中有三个有效的坐标系**:项目坐标、场景坐标和视图坐标**。为了简化实现,图形视图提供了方便的功能,允许您在三个坐标系之间进行映射。
  渲染时,图形视图的场景坐标对应于QPainer的逻辑坐标,视图坐标与设备坐标相同。后续会说明逻辑坐标和设备坐标之间的关系。
  Qt开发技术:图形视图框架(一)基本介绍_QGraphic_05

项目坐标

  项目在自己的本地坐标系中。它们的坐标通常以中心点(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()),除了其本地项pos和边界矩形之外。场景位置描述了项目在场景坐标中的位置,其场景边界矩形构成了QGraphicsScene如何确定场景的哪些区域已更改的基础。场景中的更改通过QGraphicsScene::changed()信号进行通信,参数是场景矩形的列表。

视图坐标

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

坐标映射

  在处理场景中的项目时,通常可以将坐标和任意形状从场景映射到项目、从项目映射到项目或从视图映射到场景。例如,在QGraphicsView的视区中单击鼠标时,可以通过调用QGraphicsView::mapToScene(),然后调用QGraphicsScene::ItemAt()来询问场景光标下的项目。如果要知道某个项在视区中的位置,可以对该项调用QGraphicsItem::MapToScene(),然后在视图上调用QGraphicsView::MapFromSecene()。最后,如果要查找视图椭圆中的项目,可以将QPaineterPath传递给mapToScene(),然后将映射的路径传递给QGraphicScene::items()。
  通过调用QGraphicsItem::MapToScene()和QGraphicsItem::mapFromScene(),可以将坐标和形状映射到项的场景或从中映射。还可以通过调用QGraphicsItem::mapTopParent()和qgraphicsItem::mapFromParent()映射到项的父项,或者通过调用QGraphicsItem::MapToItem()和QGraphicsItem::mapFromItem()在项之间映射。所有映射函数都可以映射点、矩形、多边形和路径。
  视图中有相同的映射函数,可用于映射到场景或从场景映射到场景。QGraphicsView::mapFromSecene()和QGraphicsView::mapToScene()。要从视图映射到项目,首先映射到场景,然后从场景映射到项目。


关键特征

缩放和旋转

  QGraphicsView支持与QPainer通过QGraphicsView::setMatrix()进行的相同的仿射转换。通过对视图应用转换,您可以轻松地添加对常见导航功能(如缩放和旋转)的支持。
以下是如何在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。
  转换视图时,QGraphicsView保持视图中心对齐。
  有关如何实现基本缩放功能的代码,后续会有示例描述。

项目组

  通过使一个项目成为另一个项目的子项目,您可以实现项目分组最基本的特性:

  • 项目将一起移动
  • 所有转换都从父项目传播到子项目。

  此外,QGraphicsItemGroup是一个特殊的项,它将子事件处理与用于向组中添加和删除项的有用接口结合在一起。将项添加到QGraphicsItemGroup将保留该项的原始位置和转换,而通常重新设置项将导致子项相对于其新父项重新定位自身。为了方便起见,可以通过调用QGraphicScene::CreateItemGroup()通过场景创建QGraphicsItemGroups。

小部件和布局

Qt4.4通过QGraphicsWidget引入了对几何和布局感知项的支持。这个特殊的基项类似于QWidget,但与QWidget不同,它不是从QPaintDevice继承的,而是从QGraphicsItem继承的。这允许您编写带有事件、信号和槽、大小提示和策略的完整小部件,还可以通过QGraphicsLinearLayout和QGraphicsGridLayout在布局中管理小部件的几何图形。

QGraphicsWidget

  基于QGraphicsItem的功能和精简的占地面积,QGraphicsWidget提供了两个方面的最佳功能:QWidget的额外功能,例如样式、字体、调色板、布局方向及其几何图形,以及QGraphicsItem的分辨率独立性和转换支持。因为图形视图使用实坐标而不是整数,所以QGraphicsWidget的几何函数也在QRectF和QPointF上运行。这也适用于框架矩形、边距和间距。例如,对于QGraphicsWidget,指定内容页边距(0.5、0.5、0.5、0.5)并不少见。您可以创建子窗口和“顶级”窗口;在某些情况下,您现在可以为高级MDI应用程序使用图形视图。
  一些QWidget的属性是受支持的,包括窗口标志和属性,但不是全部。参考QGraphicsWidget的类文档,以全面了解什么是受支持的,什么是不受支持的。例如,可以通过将Qt::window标志传递给QGraphicsWidget的构造函数来创建装饰窗口,但图形视图当前不支持MacOs上常见的Qt::Sheet和Qt:: Drawer标志。

QGraphicsLayout

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

嵌入式小部件支持

  图形视图为在场景中嵌入任何小部件提供无缝支持。您可以嵌入简单的小部件,如QLineEdit或QPushButton,复杂的小部件,如QTabWidget,甚至完整的主窗口。要将小部件嵌入到场景中,只需调用QGraphicScene::AddWidget(),或创建QGraphicsProxyWidget实例手动嵌入小部件。
  通过QGraphicsProxyWidget,图形视图能够深入集成客户端小部件的功能,包括其光标、工具提示、鼠标、平板电脑和键盘事件、子小部件、动画、弹出窗口(如QComboBox或QCompeter),以及小部件的输入焦点和激活。QGraphicsProxyWidget甚至集成了嵌入式小部件的选项卡顺序,这样您就可以在嵌入式小部件中插入和取出选项卡。甚至可以将新的QGraphicsView嵌入到场景中,以提供复杂的嵌套场景。
  当转换嵌入的小部件时,图形视图确保小部件独立地转换分辨率,允许字体和样式在放大时保持清晰。(请注意,分辨率独立性的效果取决于样式。)


性能

浮点指令

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