Qt中绘图框架介绍

绘图框架类

父窗口 继承1 继承2
QGraphicsScene
QGraphicsView
QGraphicsItem QAbstractGraphicsShapeItem QGraphicsEllipseItem
QGraphicsPathItem
QGraphicsPolygonItem
QGraphicsRectItem
QGraphicsSimpleTextItem
QGraphicsItemGroup
QGraphicsLineItem
QGraphicsObject QGraphicsSvgItem
QGraphicsTextItem
QGraphicsWidget
QGraphicsPixmapItem
QGraphicsEffect QGraphicsBlurEffect
QGraphicsColorizeEffect
QGraphicsDropShadowEffect
QGraphicsOpacityEffect
QGraphicsLayoutItem QGraphicsLayout QGraphicsAnchorLayout
QGraphicsGridLayout
QGraphicsLinearLayout
QGraphicsWidget QGraphicsProxyWidget
QGraphicsAnchor
QGraphicsSceneEvent QGraphicsSceneContextMenuEvent
QGraphicsSceneDragDropEvent
QGraphicsSceneHelpEvent
QGraphicsSceneHoverEvent
QGraphicsSceneMouseEvent
QGraphicsSceneMoveEvent
QGraphicsSceneResizeEvent
QGraphicsSceneWheelEvent
QGraphicsTransform QGraphicsTransform
QGraphicsScale
QStyleOptionGraphicsItem

绘图框架的介绍

Qt为了解决QPainter绘图无法绘制复杂的图形和不能实现图件的选择、编辑、拖放、修改等问题,引入了Graphics View绘图框架。Graphics View提供了一种基于项目的模型视图编程方法。Graphics View提供了一个用于管理和大量定制的2D图形项目交互的界面,以及一个用于可视化项目并支持支持缩放和旋转的视图小部件。

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

Graphics View使用BSP(二进制空间分区)树来提供非常快速的项目发现,因此,即使有数百万个项目,它也可以实时可视化大型场景。

Graphics View在Qt 4.2中引入,取代了其前身QCanvas。

Graphics View框架主要由3个部分构成:视图(QGraphicsView)、场景(QGraphicsScene)和图形项(QGraphicsItem)

视图(QGraphicsView)

QGraphicsView为Graphics View框架提供了视图小部件,它可以可视化场景的内容。**可以为一个场景设置多个视图,用于对同一个数据提供不同的视口。而一个视图只能对应一个场景,后面设置的场景绘将前面的场景覆盖。**视图小部件是一个滚动区域,并提供滚动条用于在大型场景中导航。要启用OpenGL支持,可以通过调用QGraphicsView::setViewport() 将QOpenGLWidget设置为视口。

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

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

场景(QGraphicsScene)

QGraphicsScene提供图形视图场景。

现场有以下职责:

  • 提供用于管理大量项目的快速界面

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

  • 将事件传播到每个项目

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

  • 管理项目状态,例如选择和焦点处理

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

  • 提供未转换的渲染功能;主要用于印刷

    使用QGraphicsScene::render() 函数,可以将场景的部分渲染到绘制设备中。

图形项(QGraphicsItem)

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

QGraphicsItem支持以下功能:

  • 鼠标按下、移动、释放和双击事件,以及鼠标悬停事件、滚轮事件和上下文菜单事件

  • 键盘输入焦点和关键事件

  • 拖放

  • 通过父子关系和QGraphicsItemGroup进行分组

  • 碰撞检测

图形项是位于本地坐标系中,与QGraphicsView一样,它还提供了许多功能来映射项目和场景之间的坐标,以及从图形项到场景的坐标。此外,与QGraphicsView一样,它可以使用矩阵转换坐标系:QGraphicsItem::transform()。

图形项可以包含其他项目(子项目)。父项的转换由其所有子项继承。不管一个项的累积如何转换,它的所有函数(例如,QGraphicsItem::contains()、QGraphicsIItem::boundingRect()和QGraphicsItem::collapssWith())仍然在本地坐标中运行。

QGraphicsItem通过QGraphicsItem::shape()函数和QGraphicsIItem::collapsWith()函数进行冲突检测,这两个函数都是虚拟函数。通过从QGraphicsItem::shape()将物品的形状返回为本地坐标QPainterPath,QGraphicsItem将为您处理所有碰撞检测。但是,如果您想提供自己的碰撞检测,可以重新实现QGraphicsItem::collapsWith()。

绘图框架中的坐标系

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

注意:由于Graphics Views使用Qt的坐标系,因此不支持反转的Y轴坐标系(其中Y向上增长)。

Graphics Views中有三个有效的坐标系:图形项坐标、场景坐标和视图坐标。为了实现简单方便的操作,Graphics Views提供了方便的功能,允许在三个坐标系之间进行映射。

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

视图坐标系

视图坐标就是窗口界面的物理坐标,单位是像素。视图坐标只与widget或者视口有关,而与观察无关。视图的坐标是在左上角为坐标原点。

所有的鼠标事件、拖放事件的坐标首先是由视图视图坐标决定的,然后用户需要将这些坐标映射为场景坐标,以便图形项交互。

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

场景坐标系

**场景表示其所有项目的基准坐标系,场景中的坐标系是和视图有关系的,场景中坐标就是对应的视图中场景矩形框的位置。而场景适合视图是中心点对齐的。场景坐标系描述了每个顶级项目的位置,也构成了从视图传递到场景的所有场景事件的基础。**场景中的每个项目都有一个场景位置和边界矩形(QGraphicsItem::scenePos(),QGraphicsItem::sceneBoundingRect()),此外还有其本地项目位置和边界长方形。场景位置描述了项目在场景坐标中的位置,其场景边界矩形构成了QGraphicsScene如何确定场景中哪些区域已更改的基础。场景中的更改通过QGraphicsScene::changed()信号进行通信,参数是场景矩形的列表。

图形项坐标系

**图形项位于自己的局部坐标系中。它们的坐标通常以其中心点(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())总是以项目坐标给出。

坐标映射

通常情况下,在处理场景中的图形项时,经常会使用到坐标和任意形状从场景映射到图形项、从图形项映射到图形项或从视图映射到场景的坐标映射。因此下面就介绍一下坐标映射的函数。

  • 当您在QGraphicsView的视口中单击鼠标时,可以通过调用QGraphicsView::mapToScene(),然后调用QGraphicsScene::itemAt()来询问场景光标下的图形项。

  • 如果您想知道图形项在视口中的位置,可以对图形项调用QGraphicsItem::mapToScene(),然后对视图调用QGraphicsView::mapFromScene(最后,如果使用查找视图椭圆内的图形项,可以将QPainterPath传递到mapToScene(),然后将映射的路径传递到QGraphicsScene::items()。

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

  • 视图中提供了相同的映射功能,用于映射到场景和从场景进行映射。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); }
    ...
};

打印

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相同的拖放功能。此外,为了方便起见,图形视图框架为场景以及每个图形项都提供了拖放支持。

  • 当视图接收到拖动事件时,它会将拖放事件转换为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()和特定场景所需的任何事件处理程序。

项可以通过调用QGraphicsItem::setAcceptDrops()来启用拖放支持。要处理传入的拖动,需要重新实现QGraphicsItem::dragEnterEvent()。

官方拖放实例:https://doc.qt.io/qt-5/qtwidgets-graphicsview-dragdroprobot-example.html

光标和工具提示

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

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

官方拖放实例:https://doc.qt.io/qt-5/qtwidgets-graphicsview-dragdroprobot-example.html

动画

  • Graphics View 支持多个级别的动画。使用“动画框架”可以轻松地组合动画。为此,自定的的图形项需要继承QGraphicsObject类,并关联QPropertyAnimation。QPropertyAnimation允许为任何QObject属性设置动画。

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

  • 第三个方法主要用于与Qt 3中的QCanvas兼容,它是通过调用QGraphicsScene::advancement()来推进场景,而QGraphicsSene::advancence()又调用QGraphicsItem::advance()。

OpenGL渲染

要启用OpenGL渲染,只需通过调用QGraphicsView::setViewport()将一个新的QOpenGLWidget设置为QGraphicsView的视口。如果希望OpenGL具有抗锯齿功能,则需要设置具有所需采样数的QSurfaceFormat(请参见QSurfaceFormat::setSamples())。

图形项组

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

此外,QGraphicsItemGroup是一个特殊的图形项,它将子事件处理与函数接口相结合,这些函数接口是用于向组中添加图形项和从组中删除图形项的函数接口相。

向QGraphisItemGroup中添加一个图形项的时候,这个图形项将保持原始的位置和转换。当对图形项进行重新排序将会导致子项相对于其新父项重新定位。为了方便起见,可以通过调用QGraphicsScene::createItemGroup()在场景中创建QGraphicsItemGroup。

小工具和布局

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

QGraphicsWidget

QGraphicsWidget是基于QGraphicsItem的功能和轨迹构建的,它提供了两全其美的功能:除了有QWidget的功能,如样式、字体、调色板、布局方向及其几何结构,还支持QGraphicsItem的分辨率独立性和转换。因为Graphics View使用的是实坐标而不是整数,所以QGraphicsWidget的几何函数也可以对QRectF和QPointF进行操作。这也适用于框架矩形、边距和间距。例如,使用QGraphicsWidget,指定内容边距(0.5、0.5、0.5和0.5)。您可以创建子窗口小部件和“顶级”窗口;在某些情况下,您现在可以将图形视图用于高级MDI应用程序。

QGraphicsWidget支持QWidget的一些属性,包括窗口标志和属性,但不是全部。想了解全部应该参考QGraphicsWidget的类文档,以全面了解什么是支持的,什么是不支持的。例如,您可以通过将Qt::Window窗口标志传递给QGraphicsWidget的构造函数来创建装饰窗口,但Graphics View目前不支持macOS上常见的Qt:::Sheet和Qt::Drawer标志。

QGraphicsLayout

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

嵌入式小工具支持

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

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

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

渲染执行

浮点指令

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

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

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

你可能感兴趣的:(Qt笔记,qt,开发语言)