✎Qt-doc—QGraphics图形视图框架&坐标系统

Posted itzyjr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了✎Qt-doc—QGraphics图形视图框架&坐标系统相关的知识,希望对你有一定的参考价值。

目录

Graphics View Framework

图形视图框架(Graphics View Framework)

图形视图(Graphics View)提供了一个用于管理大量定制2D图形Item并与之交互的表面(surface),以及一个用于可视化Item的视图部件,支持缩放和旋转。

该框架包括一个事件传播体系结构,允许对场景中的Item进行精确的双精度交互。Item可以处理按键事件、鼠标按下、移动、释放和双击事件,还可以跟踪鼠标移动。

图形视图使用BSP(二进制空间分区)树来提供非常快速的查找Item,因此,它可以实时可视化大型场景,即使有数百万个Item。

图形视图在Qt 4.2中引入,取代了其前身QCanvas

图形视图体系结构(The Graphics View Architecture)

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

场景(The Scene)

QGraphicsScene提供图形视图场景。场景有以下责任:

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

场景用作QGraphicsItem对象的容器。通过调用QGraphicsScene::addItem()将Item添加到场景中,然后通过调用多个查找函数之一来检索Item。QGraphicsScene::items()及其重载返回点、矩形、多边形或常规矢量路径包含的或与之相交的所有项。QGraphicsScene::itemAt()返回特定点处的最顶层Item。所有Item发现功能都以降序堆叠顺序返回Item(即,第一个返回的是最上面的Item,最后一个是最下面的Item)。

QGraphicsScene scene;
QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100));
QGraphicsItem *item = scene.itemAt(50, 50, QTransform());

QGraphicsScene的事件传播体系结构规划场景事件以递交到Item,并管理Item之间的传播。如果场景在某个位置接收到鼠标按下事件,则场景会将事件传递到该位置的任何Item。

QGraphicsScene还管理某些Item状态,例如Item选择和焦点。可以通过调用QGraphicsScene::setSelectionArea()并传递任意形状来选择场景中的Item。此功能还用作QGraphicsView中橡皮筋选择(rubberband selection)的基础。要获取当前所有选定Item的列表,请调用QGraphicsScene::selectedItems()。QGraphicsScene处理的另一个状态是Item是否具有键盘输入焦点。你可以通过调用QGraphicsScene::setFocusItem()QGraphics item::setFocus()来设置Item的焦点,或者通过调用QGraphicsScene::focusItem()来获取当前焦点Item。

最后,QGraphicsScene允许你通过QGraphicsScene::render()函数将场景的部分渲染到绘制设备中。你可以在本文档后面的“打印(Printing section)”部分中阅读有关此的更多信息。

视图(The View)

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

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

视图接收来自键盘和鼠标的输入事件,并在将事件发送到可视化场景之前将这些事件转换为场景事件(在适当的情况下将使用的坐标转换为场景坐标)。

使用其变换矩阵QGraphicsView::transform(),视图可以变换场景的坐标系。这允许高级导航功能,如缩放和旋转。为了方便起见,QGraphicsView还提供了在视图坐标和场景坐标之间进行转换的函数:QGraphicsView::mapToScene()QGraphicsView::mapFromScence()

项(The Item)

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

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

Items位于本地坐标系(local coordinate system)中,与QGraphicsView一样,它还提供了许多函数来映射Item和场景之间的坐标,以及Item与Item之间的坐标。此外,与QGraphicsView一样,它可以使用矩阵:QGraphicsItem::transform()转换其坐标系。这对于旋转和缩放单个Item很有用。

Items可以包含其他Item(子Item)。父Item的转换被所有子Item继承。但是,无论一个Item的累积转换是什么,它的所有函数(例如,QGraphicsItem::contains()QGraphicsItem::boundingRect()QGraphicsItem::collissWith())仍然在局部坐标(local coordinates)中操作。

QGraphicsItem支持通过QGraphicsItem::shape()函数和QGraphicsItem::collectsWith()进行碰撞检测,这两个函数都是虚函数。通过从QGraphicsItem::shape()返回Item的局部坐标下的形状QPainterPath,QGraphicsItem将为你处理所有碰撞检测。但是,如果你想提供自己的碰撞检测,可以重新实现QGraphicsItem::collectsWith()。

“图形视图框架”包含的类(Classes in the Graphics View Framework)

“图形视图”坐标系统(The Graphics View Coordinate System)

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

图形视图中有三个有效的坐标系:Item坐标、场景坐标和视图坐标。为了简化实现,“图形视图”提供了方便的功能,允许你在三个坐标系之间进行映射。

渲染时,“图形视图”的场景坐标对应于QPainter的逻辑坐标视图坐标与设备坐标相同。在坐标系文档中,可以阅读有关逻辑坐标和设备坐标之间关系的信息。

项坐标系(Item Coordinates)

Item位于自己的本地坐标系中。它们的坐标通常以其中心点(0,0)为中心,这也是所有变换的中心(Their coordinates are usually centered around its center point (0, 0), and this is also the center for all transformations.)。Item坐标系中的几何图元通常称为Item点、Item线或Item矩形。

创建自定义Item时,只需担心Item坐标;QGraphicsScene和QGraphicsView将为你执行所有转换。这使得实现自定义Item非常容易。例如,如果你收到一个鼠标按下或拖入事件,事件位置将以Item坐标给出QGraphicsItem::contains()虚函数,如果某个点位于Item内,则返回true,否则返回false,它在Item坐标中接受一个point参数。类似地,Item的边界矩形和形状位于Item坐标中。

在Item中的位置是Item中心点在其父坐标系中的坐标;有时称为父坐标。在这种意义上,场景被视为所有无父Item的Item的“父Item”。顶层Item的位置在场景坐标中。

子坐标相对于父坐标。如果子坐标是未转换的,则子坐标与父坐标之间的差值等于父坐标中Item之间的距离。例如:如果未转换的子Item精确定位在其父Item的中心点,则两个Item的坐标系将相同。但是,如果子对象的位置为(10,0),则子对象的(0,10)点将对应于其父对象的(10,10)点。

由于Item的位置和变换相对于父Item,因此子Item的坐标不受父Item变换的影响,尽管父Item的变换隐式地变换子Item。在上例中,即使父对象被旋转和缩放,子对象的(0,10)点仍将与父对象的(10,10)点相对应。但是,相对于场景,子对象将跟随父对象的变换和位置。如果缩放父对象(2x,2x),子对象的位置将位于场景坐标(20,0),其(10,0)点将与场景上的点(40,0)相对应。

由于QGraphicsItem::pos()是少数例外之一,QGraphicsItem的函数在Item坐标中操作,而不考虑Item或其任何父Item的转换。例如,Item的边界矩形(即QGraphicsItem::boundingRect())始终以Item坐标给出。

QPointF QGraphicsItem::pos() const(少数例外之一的函数)
Returns the position of the item in parent coordinates. If the item has no parent, its position is given in scene coordinates.

场景坐标系(Scene Coordinates)

场景表示其所有Item的基础坐标系。场景坐标系描述了每个顶级Item的位置,也构成了从视图传递到场景的所有场景事件的基础。场景中的每个Item都有一个场景位置和边界矩形(QGraphicsItem::scenePos()QGraphics item::sceneBoundingRect()),以及其本地Item位置和边界矩形。场景位置描述了Item在场景坐标中的位置,其场景边界矩形构成了QGraphicsScene如何确定场景的哪些区域已更改的基础。场景中的更改通过QGraphicsScene::changed()信号传达,参数是场景矩形列表。

视图坐标系(View coordinates)

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

坐标映射(Coordinate Mapping)

通常,在处理场景中的Item时,将坐标和任意形状从场景映射到Item、从Item映射到Item或从视图映射到场景会很有用。例如,当你在QGraphicsView的视口中单击鼠标时,可以通过调用QGraphicsView::mapToScene(),然后调用QGraphcsView::itemAt()来询问场景光标下是什么Item。如果你想知道Item在视口中的位置,可以对Item调用QGraphicsItem::mapToScene(),然后在视图上调用QGraphcsView::mapFromScene()。最后,如果想查找视图椭圆内的Item,可以将QPainterPath传递给mapToScene(),然后将映射路径传递给QGraphicsScene::items()

● QPainterPath QGraphicsView::mapToScene(const QPainterPath &path) const
Returns the viewport painter path [path] mapped to a scene coordinate painter path.
● QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path,
Qt::ItemSelectionMode mode = Qt::IntersectsItemShape,
Qt::SortOrder order = Qt::DescendingOrder,
const QTransform &deviceTransform = QTransform())
Returns all visible items that, depending on [mode], are either inside or intersect with the specified [path], in a list sorted using [order].

你可以通过调用QGraphicsItem::mapToScene()和QGraphicsItem::mapFromScene。你还可以通过调用QGraphicsItem::mapToParent()和QGraphicsItem::mapFromParent()。所有映射功能都可以映射点、矩形、多边形和路径。

视图中提供了相同的映射功能,用于映射到场景和从场景映射。QGraphicsView::mapFromScene()和QGraphicsView::mapToScene()。要从视图映射到Item,请首先映射到场景,然后从场景映射到Item。

关键特性(Key Features)

缩放和旋转(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); 
    ...
;

QGraphicsView在变换视图时保持视图中心对齐。

打印(Printing)

图形视图通过其渲染函数QGraphicsScene::render()QGraphicsView::render()提供单行打印(single-line printing)。这些函数提供相同的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");

● bool QPainter::end()
Ends painting. Any resources used while painting are released. You don’t normally need to call this since it is called by the destructor.
Returns true if the painter is no longer active; otherwise returns false.
● bool QPainter::begin(QPaintDevice *device)
Begins painting the paint [device] and returns true if successful; otherwise returns false.
Notice that all painter settings (setPen(), setBrush() etc.) are reset to default values when begin() is called.

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

拖拽(Drag and Drop)

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

要从Item开始拖动,请创建一个QDrag对象,并将指针传递给启动拖动的小部件。多个视图可以同时观察Item,但只有一个视图可以开始拖动。在大多数情况下,拖动是由于按下或移动鼠标而启动的,因此在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(),Item可以启用拖放支持。要处理传入的拖动,请重新执行QGraphicsItem::dragEnterEvent()QGraphicsItem::dragMoveEvent()QGraphicsItem::dragLeaveEvent()

光标和工具提示(Cursors and Tooltips)

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

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

动画(Animation)

图形视图支持多个级别的动画。你可以使用动画框架轻松组装动画。为此,你需要将Item继承QGraphicsObject并将其与QPropertyAnimation关联。QPropertyAnimation允许设置任何QObject属性的动画

另一个选项是创建自定义Item,Item继承QObject和QGraphicsItem。该Item可以设置自己的计时器,并使用QObject::timerEvent()中的增量步进控制动画。

第三个选项(主要用于与Qt 3中的QCanvas兼容)是通过调用QGraphicsScene::advance()来推进场景,而QGraphicsScene::advance()又调用QGrahicsItem::advance()。

OpenGL渲染(OpenGL Rendering)

要启用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);

项分组(Item Groups)

通过使一个Item成为另一个Item的子Item,可以实现Item分组的最基本功能:Item将一起移动,所有转换都从父Item传播到子Item

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

组件和布局(Widgets and Layouts)

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

QGraphicsWidget

QGraphicsWidget基于QGraphicItem的功能和精益占地面积,提供了两全其美的功能: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非常相似。你可以在QGraphicsLinearLayoutQGraphicsGridLayout中管理小部件和子布局。你还可以通过自己创建QGraphicsLayout子类来轻松编写自己的布局,或者通过编写QGraphicsLayoutItem的适配器子类来将自己的QGraphicsItem项添加到布局中。

嵌入QWidget组件(Embedded Widget Support)

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

通过QGraphicsProxyWidget,GraphicsView能够深度集成客户端小部件功能,包括光标、工具提示、鼠标、平板电脑和键盘事件、子小部件、动画、弹出窗口(例如QComboBox或QCompleter),以及小部件的输入焦点和激活。QGraphicsProxyWidget甚至集成了嵌入式小部件的选项卡顺序,以便你可以在嵌入式小部件中进行选项卡输入和输出。你甚至可以将新的QGraphicsView嵌入到场景中,以提供复杂的嵌套场景。

在转换嵌入式小部件时,图形视图确保小部件独立转换分辨率,允许字体和样式在放大时保持清晰。(请注意,分辨率独立性的效果取决于样式。)

性能(Performance)

浮点指令(Floating Point Instructions)

为了准确、快速地将转换和效果应用于项目,图形视图的构建假定用户的硬件能够为浮点指令提供合理的性能。

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

因此,在某些设备上,某些类型的效果可能比预期的慢。可以通过在其他领域进行优化来弥补这种性能损失;例如,通过使用OpenGL渲染场景。然而,如果这些优化也依赖于浮点硬件的存在,那么它们本身可能会导致性能降低。

以上是关于✎Qt-doc—QGraphics图形视图框架&坐标系统的主要内容,如果未能解决你的问题,请参考以下文章

✎Qt-doc—QGraphics图形视图框架&坐标系统

✎Qt-doc—QGraphics图形视图框架&坐标系统

QGraphics图形视图框架的关系

从子小部件中删除 QGraphicsEffect [重复]

Qt - 使用 QTransform(或类似的),将内部 QRect 缩放到 QGraphics/从 QGraphics

qt获取滚动视图位置