Graphics View Framework
Graphics View提供了一个用于管理大量定制2D图形项并与之交互的表面,以及一个用于可视化这些项的视图部件,支持缩放和旋转。
该框架包括一个事件传播架构,允许场景中的项目具有精确的双精度交互能力。元素可以处理按键事件、鼠标按下、移动、释放和双击事件,它们还可以跟踪鼠标移动。
Graphics View使用BSP(二叉空间划分)树来提供非常快速的项目发现,因此,它可以实时可视化大型场景,即使有数百万个项目。
Graphics View在Qt 4.2中引入,取代了它的前身QCanvas。
一、Graphics View 架构
Graphics View提供了一种基于项的模型视图编程方法,很像InterView的便利类QTableView, QTreeView和QListView。多个视图可以观察同一个场景,场景中包含不同几何形状的物品。
Scene
QGraphicsScene provides the Graphics View scene. The scene has the following responsibilities:
- Providing a fast interface for managing a large number of items
- Propagating events to each item
- Managing item state, such as selection and focus handling
- Providing untransformed rendering functionality; mainly for printing
The scene 是 QGraphicsItem对象的容器。通过调用QGraphicsScene::addItem()将图形项添加到场景中,然后通过调用众多物品发现函数中的一个来检索物品。QGraphicsScene::items()及其重载函数返回由点、矩形、多边形或一般向量路径包含或与之相交的所有元素。QGraphicsScene::itemAt()返回指定位置的最顶层元素。所有物品发现函数都以降序堆叠的方式返回物品(即,第一个返回的物品在最上面,最后一个返回的物品在最下面)。
QGraphicsScene scene;
QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100));
QGraphicsItem *item = scene.itemAt(50, 50);
// item == rect
QGraphicsScene的事件传播架构调度场景事件以交付到项目,并管理项目之间的传播。如果场景在某个位置接收到鼠标按下事件,则场景会将事件传递给位于该位置的任何元素。
QGraphicsScene还管理某些项目状态,例如项目选择和焦点。你可以通过调用QGraphicsScene::setSelectionArea()来选择场景中的元素,传入一个任意形状的参数。此功能也用作QGraphicsView中橡皮筋选择的基础。要取得当前所有选中项的列表,调用QGraphicsScene::selectedItems()。QGraphicsScene处理的另一个状态是项目是否具有键盘输入焦点。您可以通过调用QGraphicsScene::setFocusItem()或QGraphicsItem::setFocus()来设置项上的焦点,或者通过调用QGraphicsScene::focusItem()来获取当前的焦点项。
最后,QGraphicsScene允许您通过QGraphicsScene::render()函数将场景的部分渲染到绘图设备中。您可以在本文档后面的打印部分中阅读有关这方面的更多信息。
View
QGraphicsView提供了view小部件,它可以可视化场景的内容。你可以为同一个场景添加多个视图,从而为同一个数据集提供多个视口。view部件是一个滚动区域,提供了在大型场景中导航的滚动条。要启用OpenGL支持,可以调用QGraphicsView::setViewport()将QGLWidget设置为视口。
QGraphicsScene scene;
myPopulateScene(&scene);
QGraphicsView view(&scene);
view.show();
The view接收来自键盘和鼠标的输入事件,并将这些事件转换为场景事件(将使用的坐标转换为场景坐标),然后将事件发送到可视化场景。
使用它的转换矩阵QGraphicsView::transform(),视图可以转换场景的坐标系统。这允许高级导航功能,如缩放和旋转。为了方便,QGraphicsView还提供了在视图和场景坐标之间进行转换的函数:QGraphicsView::mapToScene()和QGraphicsView::mapFromScene()。
Item
QGraphicsItem是场景中图形元素的基类。图形视图为典型形状提供了一些标准项,例如矩形(qgraphicsrectem)、椭圆(QGraphicsEllipseItem)和文本项(QGraphicsTextItem),但在编写自定义项时,可以使用最强大的QGraphicsItem功能。此外,QGraphicsItem支持以下特性:
- Mouse press, move, release and double click events, as well as mouse hover events, wheel events, and context menu events.
- Keyboard input focus, and key events
- Drag and drop
- Grouping, both through parent-child relationships, and with QGraphicsItemGroup
- Collision detection
Items存在于一个局部坐标系中,像QGraphicsView一样,它也提供了许多函数来映射物品和场景之间的坐标,以及物品到物品之间的坐标。而且,像QGraphicsView一样,它可以使用矩阵QGraphicsItem::transform()转换其坐标系统。这对于旋转和缩放单个元素非常有用。
Items可以包含其他条目(子条目)。父项的变换会被其所有的子项继承。然而,不管元素的累积变换如何,它的所有函数(例如QGraphicsItem::contains()、QGraphicsItem::boundingRect()、QGraphicsItem::collidesWith())仍然在局部坐标中操作。
QGraphicsItem通过QGraphicsItem::shape()函数和QGraphicsItem::collidesWith()函数支持碰撞检测,这两个函数都是虚函数。通过从QGraphicsItem::shape()返回项目的形状作为局部坐标QPainterPath, QGraphicsItem将为你处理所有碰撞检测。但是,如果你想提供自己的碰撞检测,可以重新实现QGraphicsItem::collidesWith()。
二、Graphics View Framework 中的类
这些类提供了创建交互式应用程序的框架。
– | – |
---|---|
QGraphicsEffect | The base class for all graphics effects |
QGraphicsAnchor | Represents an anchor between two items in a QGraphicsAnchorLayout |
QGraphicsAnchorLayout | Layout where one can anchor widgets together in Graphics View |
QGraphicsGridLayout | Grid layout for managing widgets in Graphics View |
QAbstractGraphicsShapeItem | Common base for all path items |
QGraphicsEllipseItem | Ellipse item that you can add to a QGraphicsScene |
QGraphicsItem | The base class for all graphical items in a QGraphicsScene |
QGraphicsItemGroup | Container that treats a group of items as a single item |
QGraphicsLineItem | Line item that you can add to a QGraphicsScene |
QGraphicsObject | Base class for all graphics items that require signals, slots and properties |
QGraphicsPathItem | Path item that you can add to a QGraphicsScene |
QGraphicsPixmapItem | Pixmap item that you can add to a QGraphicsScene |
QGraphicsPolygonItem | Polygon item that you can add to a QGraphicsScene |
QGraphicsRectItem | Rectangle item that you can add to a QGraphicsScene |
QGraphicsSimpleTextItem | Simple text path item that you can add to a QGraphicsScene |
QGraphicsTextItem | Text item that you can add to a QGraphicsScene to display formatted text |
QGraphicsLayout | The base class for all layouts in Graphics View |
QGraphicsLayoutItem | Can be inherited to allow your custom items to be managed by layouts |
QGraphicsLinearLayout | Horizontal or vertical layout for managing widgets in Graphics View |
QGraphicsProxyWidget | Proxy layer for embedding a QWidget in a QGraphicsScene |
QGraphicsScene | Surface for managing a large number of 2D graphical items |
QGraphicsSceneContextMenuEvent | Context menu events in the graphics view framework |
QGraphicsSceneDragDropEvent | Events for drag and drop in the graphics view framework |
QGraphicsSceneEvent | Base class for all graphics view related events |
QGraphicsSceneHelpEvent | Events when a tooltip is requested |
QGraphicsSceneHoverEvent | Hover events in the graphics view framework |
QGraphicsSceneMouseEvent | Mouse events in the graphics view framework |
QGraphicsSceneMoveEvent | Events for widget moving in the graphics view framework |
QGraphicsSceneResizeEvent | Events for widget resizing in the graphics view framework |
QGraphicsSceneWheelEvent | Wheel events in the graphics view framework |
QGraphicsTransform | Abstract base class for building advanced transformations on QGraphicsItems |
QGraphicsView | Widget for displaying the contents of a QGraphicsScene |
QGraphicsWidget | The base class for all widget items in a QGraphicsScene |
QStyleOptionGraphicsItem | Used to describe the parameters needed to draw a QGraphicsItem |
QGraphicsSvgItem | QGraphicsItem that can be used to render the contents of SVG files |
三、Graphics View 坐标系统
图形视图基于笛卡儿坐标系;项目在场景中的位置和几何形状由两组数字表示:x坐标和y坐标。当使用未变换的视图观察场景时,场景中的一个单位由屏幕上的一个像素表示。
注意:由于图形视图使用Qt的坐标系统,因此不支持反向y轴坐标系统(y向上增长)。
在图形视图中有三个有效的坐标系统:项目坐标、场景坐标和视图坐标。为了简化实现,Graphics View提供了方便的函数,允许你在三个坐标系统之间进行映射。
渲染时,图形视图的场景坐标对应于QPainter的逻辑坐标,视图坐标与设备坐标相同。在坐标系统文档中,您可以阅读有关逻辑坐标和设备坐标之间的关系。
Item 坐标
元素存在于自己的局部坐标系统中。它们的坐标通常以中心点(0,0)为中心,这也是所有变换的中心。物品坐标系中的几何基元通常被称为物品点、物品线或物品矩形。
创建自定义item时,item的坐标是你需要担心的;QGraphicsScene和QGraphicsView将为您执行所有转换。这使得实现自定义项非常容易。例如,如果你收到了一个鼠标按下或拖动进入事件,事件的位置会在元素的坐标中给出。QGraphicsItem::contains()虚函数,如果某个点在项目内部,则返回true,否则返回false,它接受项目坐标中的point参数。类似地,项目的边界矩形和形状是在项目的坐标中。
At item的位置是item在其父坐标系中的中心点的坐标;有时也被称为父坐标。在这个意义上,场景被视为所有无父项的“父”。顶级物品的位置在场景坐标中。
子元素坐标是相对于父元素坐标的。如果子元素未进行转换,则子元素坐标与父元素坐标之间的差值与父元素坐标中项目之间的距离相同。例如:如果一个未变换的子项精确地定位在父项的中心点上,那么两个子项的坐标系统将是相同的。如果子节点的位置是(10,0),那么子节点的(0,10)点将对应于父节点的(10,10)点。
因为元素的位置和变换是相对于父元素的,所以子元素的坐标不受父元素变换的影响,尽管父元素的变换会隐式地变换子元素。在上面的例子中,即使父元素被旋转和缩放,子元素的(0,10)点仍然对应于父元素的(10,10)点。然而,相对于场景,子元素会跟随父元素的变换和位置。如果父元素被缩放(2x, 2x),子元素的位置将在场景坐标(20,0)处,而它的(10,0)点将对应于场景中的(40,0)点。
QGraphicsItem::pos()是少数例外之一,QGraphicsItem的函数在物品坐标中操作,无论物品是什么,也不管它的父元素是什么变换。例如,一个项目的边界矩形(即QGraphicsItem::boundingRect())总是以项目的坐标给出。
Scene 坐标
场景代表所有项目的基础坐标系统。场景坐标系统描述了每个顶层项目的位置,同时也构成了从视图发送到场景的所有场景事件的基础。场景中的每个项目都有一个场景位置和边界矩形(QGraphicsItem::scenePos(), QGraphicsItem::sceneBoundingRect()),除了它的局部项目位置和边界矩形。场景位置描述了项目在场景坐标中的位置,它的场景边界矩形构成了QGraphicsScene如何确定场景的哪些区域发生了变化的基础。场景的变化通过QGraphicsScene::changed()信号传达,参数是场景矩形的列表。
View 坐标
视图坐标是小部件的坐标。视图坐标中的每个单位对应一个像素。这个坐标系统的特别之处在于它是相对于widget(或视口)的,不受观察到的场景的影响。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()。要从视图映射到物品,首先要映射到场景,然后再从场景映射到物品。
四、关键特性
Zooming and rotating(缩放旋转)
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); }
...
};
插槽可以连接到启用autoRepeat的QToolButtons。
当你转换视图时,QGraphicsView保持视图中心对齐。
请参阅elasticnodes示例,以获取如何实现基本缩放功能的代码。
Printing(打印)
图形视图通过其渲染函数QGraphicsScene::render()和QGraphicsView::render()提供单行打印。这些函数提供了相同的API:你可以通过将QPainter传递给任意一个渲染函数,让场景或视图将它们的全部或部分内容渲染到任何绘制设备中。这个例子展示了如何使用QPrinter将整个场景打印到一个完整的页面中。
QGraphicsScene scene;
scene.addRect(QRectF(0, 0, 100, 200), QPen(Qt::black), QBrush(Qt::green));
QPrinter printer;
if (QPrintDialog(&printer).exec() == QDialog::Accepted) {
QPainter painter(&printer);
painter.setRenderHint(QPainter::Antialiasing);
scene.render(&painter);
}
场景和视图渲染函数之间的区别是一个在场景坐标中操作,另一个在视图坐标中操作。QGraphicsScene::render()通常用于未变换地打印场景的整个片段,例如绘制几何数据或打印文本文档。另一方面,QGraphicsView::render()适合截屏;它的默认行为是使用提供的painter渲染视口的内容。
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,你可以选择在内容拉伸时保持或忽略场景的纵横比。
Drag and Drop(拖放)
因为QGraphicsView间接地继承了QWidget,它已经提供了QWidget提供的相同的拖放功能。此外,为了方便,图形视图框架提供了对场景和每个项目的拖放支持。当视图接收到拖动时,它将拖放事件转换为QGraphicsSceneDragDropEvent,然后将其转发到场景。场景接管了这个事件的调度,并将其发送给鼠标光标下接受drop的第一个项目。
要从一个元素开始拖动,创建一个QDrag对象,向开始拖动的部件传递一个指针。多个视图可以同时观察项目,但只有一个视图可以启动拖动。在大多数情况下,拖动是从按下或移动鼠标开始的,因此在mousePressEvent()或mouseMoveEvent()中,可以从事件中获得widget的初始指针。例如:
void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
QMimeData *data = new QMimeData;
data->setColor(Qt::green);
QDrag *drag = new QDrag(event->widget());
drag->setMimeData(data);
drag->start();
}
为了拦截场景的拖放事件,您需要在QGraphicsItem子类中重新实现QGraphicsScene::dragEnterEvent()和特定场景需要的任何事件处理程序。您可以在每个QGraphicsScene的事件处理程序的文档中阅读有关拖放图形视图的更多信息。
项目可以通过调用QGraphicsItem::setAcceptDrops()来启用拖放支持。要处理传入的拖动,请重新实现QGraphicsItem::dragEnterEvent()、QGraphicsItem::dragMoveEvent()、QGraphicsItem::dragLeaveEvent()和QGraphicsItem::dropEvent()。
请参阅拖放机器人示例,以演示图形视图对拖放操作的支持。
Cursors and Tooltips(游标和工具提示)
与QWidget类似,QGraphicsItem也支持游标(QGraphicsItem::setCursor())和工具提示(QGraphicsItem::setToolTip())。当鼠标光标进入物品区域(通过调用QGraphicsItem::contains()检测到)时,QGraphicsView会激活游标和工具提示。
你也可以通过调用QGraphicsView::setCursor()直接在视图上设置默认游标。
请参阅实现工具提示和光标形状处理的拖放机器人示例代码。
Animation(动画)
Graphics View支持多个级别的动画。你可以通过使用动画框架轻松地组装动画。为此,您需要您的项目继承QGraphicsObject并将QPropertyAnimation与它们关联。QPropertyAnimation允许对任何QObject属性进行动画。
另一种选择是创建一个自定义项,它继承自QObject和QGraphicsItem。该项目可以设置自己的定时器,并在QObject::timerEvent()中以增量步骤控制动画。
第三种选择是通过调用QGraphicsScene::advance()来推进场景,它又会调用QGraphicsItem::advance(),这主要是为了在Qt 3中与QCanvas兼容。
OpenGL Rendering(OpenGL渲染)
要启用OpenGL渲染,只需通过调用QGraphicsView::setViewport()设置一个新的QGLWidget作为QGraphicsView的视口。如果你想要OpenGL抗锯齿,你需要OpenGL样本缓冲支持(参见QGLFormat::sampleBuffers())。
例如:
QGraphicsView view(&scene);
view.setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
Item Groups(组合)
通过将一项作为另一项的子元素,可以实现项分组最基本的特性:所有项都在一起移动,所有变换都可以从父元素传播到子元素。
此外,QGraphicsItemGroup是一个特殊项,它将子事件处理与用于向组中添加和从组中删除项的有用接口结合在一起。将一个元素添加到QGraphicsItemGroup将保持元素的原始位置和变换,而重新排列元素通常会导致子元素相对于新的父元素重新定位。为了方便,你可以通过场景调用QGraphicsScene::createItemGroup()来创建QGraphicsItemGroups。
Widgets and Layouts(组件和布局)
Qt 4.4 通过 QGraphicsWidget 引入了对几何和布局感知项目的支持。这个特殊的基础项类似于QWidget,但与QWidget不同的是,它不是继承自QPaintDevice;而不是来自QGraphicsItem。这允许您编写具有事件、信号和插槽、大小提示和策略的完整窗口组件,您还可以通过QGraphicsLinearLayout和QGraphicsGridLayout在布局中管理窗口组件的几何形状。
QGraphicsWidget
基于QGraphicsItem的功能和精简功能,QGraphicsWidget 提供了两者的精华:来自QWidget的额外功能,如样式、字体、调色板、布局方向及其几何形状,以及来自QGraphicsItem的分辨率独立性和转换支持。因为图形视图使用真实坐标而不是整数,所以QGraphicsWidget 的几何函数也可以操作QRectF和QPointF。这也适用于frame rects、margin和spacing。例如,使用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中管理部件和子布局。你也可以通过自己继承GraphicsLayout 来轻松编写自己的布局,或者通过编写QGraphicsLayoutItem的适配器子类来将自己的QGraphicsItem项目添加到布局中。
Embedded Widget Support(嵌入式Widget支持)
图形视图为将任何小部件嵌入场景提供无缝支持。可以嵌入简单的小部件(如QLineEdit或QPushButton),复杂的小部件(如QTabWidget),甚至是完整的主窗口。要将小部件嵌入到场景中,只需调用QGraphicsScene::addWidget(),或创建一个QGraphicsProxyWidget实例来手动嵌入小部件。
通过QGraphicsProxyWidget, Graphics View能够深度集成客户端widget功能,包括其光标,工具提示,鼠标,平板电脑和键盘事件,子widget,动画,弹出窗口(例如,QComboBox或QCompleter),以及widget的输入焦点和激活。QGraphicsProxyWidget甚至集成了嵌入式widget的选项卡顺序,以便您可以用选项卡进出嵌入式widget。您甚至可以将新的QGraphicsView嵌入到您的场景中,以提供复杂的嵌套场景。
在转换嵌入式小部件时,Graphics View确保小部件独立转换分辨率,允许字体和样式在放大时保持清晰。(注意,分辨率独立性的效果取决于样式。)
五、性能
浮点指令
为了准确、快速地为元素应用变换和特效,图形视图的构建假设用户的硬件能够为浮点指令提供合理的性能。
许多工作站和桌面计算机都配备了适当的硬件来加速这种计算,但一些嵌入式设备可能只提供处理数学运算或在软件中模拟浮点指令的库。
因此,在某些设备上,某些类型的效果可能比预期的要慢。可以通过在其他方面进行优化来弥补这种性能损失;例如,通过使用OpenGL渲染一个场景。但是,如果这种优化本身也依赖于浮点硬件,那么性能就会下降。