—— 均为自学笔记,如有错误请指正
本文参考 QGraphicsView 、QGraphicsView
一、概述
1. 简介
Graphics View框架实现了 模型-视图 结构的图形管理,能对大量图元进行管理,支持碰撞检测,坐标变换和图元组等多种方便的功能。
2. GraphicsView框架
在Graphics View绘图架构中,主要涉及到下面三个类的使用:
① 场景类(QGraphicsScene):该类提供绘图场景(Scene),场景是不可见的,是一个抽象的管理图形项的容器,可向场景中添加图形项,获取场景中的某个图形项等,主要功能如下:
- 提供管理大量图元的快速接口;
- 传播事件到场景中的每个图元;
- 提供未经变换的渲染功能,主要用于打印;
- 管理图元状态,如图元选择和焦点处理;
② 视图类(QGraphicsView):该类提供绘图的视图(View)组件,用于显示场景中的内容。可以为一个场景设置几个视图,用于对同一个数据集提供不同的观察方式;
③ 图形项类(QGraphicsItem):该类提供了一些基本的图形元件,也可在此基础上自定义图形项,它支持各种事件的响应,如鼠标事件、键盘事件、拖放事件等,以实现图形的交互功能,下面列举一些标准图元:
- QGraphicsEllipseItem 椭圆
- QGraphicsLineItem 直线
- QGraphicsPathItem 路径
- QGraphicsPixmapItem 图像
- QGraphicsPolygonItem 多边形
- QGraphicsRectItem 矩形
- QGraphicsSimpleTextItem 简单文本
- QGraphicsTextItem 文本浏览
- QGraphicsItem是图元的基类,用户可以继承QGraphicsItem实现自定义的图元;
3. GraphicsView坐标系
Graphics View基于笛卡尔坐标系。
item在场景中的位置与几何形状通过x,y坐标来表示。当使用未经变形的视图来观察场景时,场景中的一个单位等于屏幕上的一个像素。在Graphics View绘图架构中涉及到了3个坐标系,即场景坐标、视图坐标及图形项坐标。
① 场景坐标:
对应QPainter的逻辑坐标,以场景的中心为原点,X轴正方向向右,Y轴正方向向下;
② 视图坐标:
即窗口界面的物理坐标,单位是像素,其左上角为原点坐标,所有鼠标事件、拖拽事件最开始都使用视图坐标,为了和图元交互,需要转换坐标为场景坐标;
③ 图形项坐标:
图元存在于自己的本地坐标上,通常以图元的中心为原点,图元中心也是所有坐标变换的原点,图元坐标方向是X轴正方向向右,Y轴正方向向下(同上图)。
二、成员类型
1. QGraphicsScene
示例:
QGraphicsScene scene;
scene.addText("Hello, world!");
QGraphicsView view(&scene);
view.show();
视图可用于可视化整个场景,或仅可视化其中的一部分。
- 默认情况下,在第一次显示视图时会自动检测可视化区域(通过调用 QGraphicsScene::itemsBoundingRect() )。
- 要自己设置可视化区域矩形,可以调用 setSceneRect()。
这将适当地调整滚动条的范围。但是,尽管场景支持几乎无限大小,但滚动条的范围不会超过整数范围(INT_MIN、INT_MAX)。
QGraphicsView将鼠标和按键事件转化为场景事件,并转发到可视化的场景中,因此可以使用鼠标和键盘与场景中的项目进行交互。
2. QGraphicsView::CacheModeFlag
视图的缓存模式。视图可以在 QPixmap 中缓存预渲染的内容,然后将其绘制到视口上。这种缓存的目的是加快渲染速度较慢的区域的总渲染时间。例如,纹理、渐变和 alpha 混合背景的渲染速度可能非常慢,尤其是转换后的视图。 每次转换视图时,缓存都会失效。滚动时,只需要部分失效。
- CacheNone:不缓存,所有绘制都直接在视口上完成。
- CacheBackground:启用缓存背景。
3.QGraphicsView::DragMode
在试图上按下并拖动鼠标时视图的默认操作。
- NoDrag:无拖拽,鼠标事件被忽略。
- ScrollHandDrag:光标变为指向手型,拖动鼠标将滚动滚动条。 此模式在交互和非交互模式下均可使用。
- RubberBandDrag:会出现一个橡皮筋框。拖动鼠标将设置橡皮筋框范围,所有被橡皮筋覆盖的项目都会被选中。对于非交互式视图禁用此模式。
4. QGraphicsView::OptimizationFlag:
可以启用以提高视图渲染性能的标志。默认情况下,没有设置这些标志。设置标志通常会产生副作用,并且此效果可能在绘画设备和平台之间有所不同。
- DontSavePainterState:渲染时,视图会在渲染背景或前景以及渲染每个图形项时保存画家状态(QPainter::save())。
- DontAdjustForAntialiasing:禁用视图对暴露区域的抗锯齿自动调整。在QGraphicsItem::boundingRect() 边界上呈现抗锯齿线的图形项最终可能会在外部呈现部分线条。为了防止呈现伪像,视图将所有暴露区域在所有方向上扩大2个像素。 如果启用此标志,视图将不再执行这些调整,从而最小化需要重绘的区域,从而提高性能。一个常见的副作用是使用抗锯齿绘制的项目在移动时会在场景中留下绘画痕迹。
5. QGraphicsView::ViewportAnchor
当用户 调整视图大小 或 转换视图 时视图可以使用的锚点。
- NoAnchor:没有锚点,即视图使场景的位置保持不变。
- AnchorViewCenter:视图中心的场景点用作锚点。
- AnchorUnderMouse:鼠标下方的点用作锚点。
6. QGraphicsView::ViewportUpdateMode
场景内容更改时如何更新视口。
- FullViewportUpdate:当场景的任何可见部分发生变化或重新曝光时,视图将更新整个视口。这是不支持部分更新的视口(例如 QOpenGLWidget)以及需要禁用滚动优化的视口的首选更新模式。
- MinimalViewportUpdate:视图将确定需要重绘的最小视区区域,通过避免重绘未更改的区域来最小化绘制时间。 这是视图的默认模式。虽然这种方法总体上提供了最好的性能,但如果场景中有许多小的可见变化,视图最终可能会花费更多的时间来寻找最小化区域的方法而不是绘制。
- SmartViewportUpdate:视图将尝试通过分析需要重绘的区域来寻找最佳更新模式。
- BoundingRectViewportUpdate:将重新绘制视口中所有更改的边界矩形。这种模式的优点是视图只搜索一个区域的变化,最大限度地减少确定需要重绘什么的时间。缺点是没有改变的区域也需要重新绘制。
- NoViewportUpdate:视图永远不会在场景改变时更新它的视口,用户应控制所有更新。此模式禁用视图中的所有(可能很慢)图形项可见性测试,适用于需要固定帧速率或视口以其他方式从外部更新的场景。
三、属性成员
1. alignment : Qt::Alignment
当整个场景可见时(即没有可见的滚动条(视图范围 >= 场景范围)),此属性保持视图中场景的对齐方式。对齐方式将决定场景在视图中的渲染位置。
- 如果对齐方式是Qt::AlignCenter,这是默认的,场景将在视图中居中,
- 如果对齐方式是(Qt::AlignLeft | Qt::AlignTop),场景将在顶部渲染 - 视图的左角。
2. backgroundBrush : QBrush
此属性保存场景的背景画刷。它用于覆盖场景自身的背景。要为视图提供自定义背景绘图,可以改为重新实现 drawBackground()。默认为 Qt::NoBrush 模式的画刷。
3. cacheMode : CacheMode
缓存背景模式。
QGraphicsView view;
view.setBackgroundBrush(QImage("图片路径"));
view.setCacheMode(QGraphicsView::CacheBackground);
4. dragMode : DragMode
5. foregroundBrush : QBrush
此属性保存场景的前景画刷。它用于覆盖场景自身的前景。要为视图提供自定义前景绘图,可以改为重新实现drawForeground()。默认为Qt::NoBrush模式的画刷。
6. interactive : bool
视图是否允许场景交互。如果启用,此视图将设置为允许场景交互。否则不允许交互,并且将忽略任何鼠标或键事件(即,它将充当只读视图)。默认为 true。
7. optimizationFlags : OptimizationFlags
8. renderHints : QPainter::RenderHints
这些提示用于在绘制每个可见图形项之前初始化QPainter。默认情况下启用QPainter::TextAntialiasing。
QGraphicsScene scene;
scene.addRect(QRectF(-10, -10, 20, 20));
QGraphicsView view(&scene);
view.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
view.show();
9. resizeAnchor : ViewportAnchor
调整视图大小时,视图应如何定位场景。
10. rubberBandSelectionMode : Qt::ItemSelectionMode
使用橡皮筋框选择矩形选择图形项的行为。
11. sceneRect : QRectF
视图可视化的场景区域。如果未设置,或者如果设置了空 QRectF,则此属性与QGraphicsScene::sceneRect 具有相同的值,并随 QGraphicsScene::sceneRect 更改。否则,视图的场景矩形不受场景影响。
12. transformationAnchor : ViewportAnchor
视图在转换过程中应如何定位场景。
13、viewportUpdateMode : ViewportUpdateMode
四、成员函数
1. void invalidateScene()
void invalidateScene(const QRectF &rect = QRectF(), QGraphicsScene::SceneLayers layers = QGraphicsScene::AllLayers)
使场景中的矩形rect内的图层无效并安排重绘。rect内图层的任何缓存内容都将无条件地失效并重新绘制。
2. void rubberBandChanged()
【信号】 void rubberBandChanged(QRect rubberBandRect, QPointF fromScenePoint, QPointF toScenePoint)
当橡皮筋矩形改变时发出这个信号。橡皮筋矩形为rubberBandRect。参数2和参数3为拖动开始位置和拖动结束位置,都是场景中的点。当橡皮筋选择结束时,此信号将与空值一起发射。
3. void updateScene(const QList<QRectF> &rects)
立刻更新场景矩形。
4. void centerOn(const QPointF &pos)
滚动视口的内容以确保场景坐标pos在视图中居中。
5. void centerOn(const QGraphicsItem *item)
滚动视口的内容以确保图形项在视图中居中。
6. void drawBackground(QPainter *painter, const QRectF &rect)
默认调用场景的 drawBackground()。
7. void drawForeground(QPainter *painter, const QRectF &rect)
默认调用场景的 drawForeground()。使用 viewport()->update() 刷新内容。
8. void ensureVisible(const QRectF &rect, int xmargin = 50, int ymargin = 50)
滚动视口的内容使场景矩形rect可见,边距由 xmargin 和 ymargin 以像素为单位指定。 如果无法到达指定的矩形,则将内容滚动到最近的有效位置。
9. void ensureVisible(const QGraphicsItem *item, int xmargin = 50, int ymargin = 50)
滚动视口的内容使图形项可见。如果无法到达指定的图形项所在位置,则将内容滚动到最近的有效位置。
10. void fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio)
缩放视图矩阵并滚动滚动条以确保场景矩形rect适合视口。rect必须在场景rect内,否则fitInView()不能保证整个矩形都是可见的。此函数只缩放和滚动滚动条但保持视图的旋转、平移或剪切。视图根据aspectRatioMode 进行缩放。
gphv->fitInView(QRect(0,0,300,300));
11. void fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio)
确保 item 紧密地适合视图,根据 aspectRatioMode 缩放视图。
12. QGraphicsItem * itemAt(const QPoint &pos)
返回位置 pos 处的图形项,pos在视口坐标中。如果此位置有多个图形项,则此函数返回最上面的图形项。
13. QList<QGraphicsItem *> items()
返回关联场景中所有图形项的列表,按堆叠顺序降序排列。
14. QList<QGraphicsItem *> items(const QPoint &pos)
返回视图中位置 pos 处所有图形项的列表。按堆叠顺序降序排列。pos在视口坐标中。
15. QList<QGraphicsItem *> items(const QRect &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape)
包含在 rect 中或与 rect 相交的所有项目的列表。 rect 在视口坐标中。模式见:QGraphicsScene详解 第42个成员函数。项目按堆叠降序排序。
16. QList<QGraphicsItem *> items(const QPolygon &polygon, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape)
重载函数。
17. QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape)
重载函数。
18. 场景坐标转视口坐标
QPoint mapFromScene(const QPointF &point)
QPolygon mapFromScene(const QRectF &rect)
QPolygon mapFromScene(const QPolygonF &polygon)
QPainterPath mapFromScene(const QPainterPath &path)
场景坐标中的点、矩形、多边形、路径转换到视口坐标中的点、多边形、路径。
19. 视图坐标转场景坐标
QPointF mapToScene(const QPoint &point)
QPolygonF mapToScene(const QRect &rect)
QPolygonF mapToScene(const QPolygon &polygon)
QPainterPath mapToScene(const QPainterPath &path)
视图坐标转场景坐标。
20. void render(QPainter *painter, const QRectF &target = QRectF(), const QRect &source = QRect(), Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio)
使用painter将处于视图坐标中的 source 从场景渲染到处于绘制设备坐标中的 target 。
- 如果 source 是一个空矩形,这个函数将使用viewport()->rect() 来确定要绘制的内容。
- 如果目标是空矩形,则将使用画家绘制设备的完整尺寸(例如,对于 QPrinter,页面大小)。
- 源矩形内容将根据 aspectRatioMode 进行转换以适应目标矩形。
- 默认情况下,保持长宽比,并缩放源以适应目标。
此函数可用于将视图的内容捕获到绘图设备上,例如 QImage(例如,截取屏幕截图)或打印到 QPrinter。
QGraphicsScene scene;
scene.addItem(...
...
QGraphicsView view(&scene);
view.show();
...
QPrinter printer(QPrinter::HighResolution);
printer.setPageSize(QPrinter::A4);
QPainter painter(&printer);
view.render(&painter);
QRect viewport = view.viewport()->rect();
view.render(&painter,
QRectF(0, printer.height() / 2,
printer.width(), printer.height() / 2),
viewport.adjusted(0, 0, 0, -viewport.height() / 2));
21. void resetCachedContent()
重置所有缓存的内容。调用此函数将清除视图的缓存。
如果当前缓存模式为 CacheNone,则此函数不执行任何操作。当 backgroundBrush 或 QGraphicsScene::backgroundBrush 属性更改时,会自动调用此函数。如果重新实现QGraphicsScene::drawBackground() 或 drawBackground() 来绘制自定义背景,并且需要触发完全重绘,则只需调用此函数。
22. void resetTransform()
将视图转换重置为单位矩阵。
23. void rotate(qreal angle)
顺时针旋转当前视图。
24. QRect rubberBandRect()
如果当前正在使用橡皮筋进行项目选择,则此函数返回当前橡皮筋区域(在视口坐标中)。当用户不使用橡皮筋时,此函数返回(空值)QRectF()。此 QRect 的一部分可以超出视觉视口,它可以包含负值。
25. void scale(qreal sx, qreal sy)
按 (sx, sy) 缩放当前视图。
26. void setTransform(const QTransform &matrix, bool combine = false)
设置视图的转换矩阵。参数2见QPainter详解的第46个函数。
27. void shear(qreal sh, qreal sv)
通过 (sh, sv) 剪切当前视图转换。剪切效果见QTransform的第22个函数。
28. void translate(qreal dx, qreal dy)
将当前视图转换为(dx,dy)
void QGraphicsView::translate(qreal dx,qreal dy)
{
Q_D(QGraphicsView);
QTeanslate matrix = d->matrix;
matrix.translate(dx,dy);
setTranslate(matrix);
}
五、示例
1. 入门篇 <main.cpp下> <参考版>
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
#include "math.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsScene scene; // 定义一个场景,设置背景色为红色
scene.setBackgroundBrush(Qt::red);
QPen pen; // 定义一个画笔,设置画笔颜色和宽度
pen.setColor(QColor(0, 160, 230));
pen.setWidth(10);
QGraphicsRectItem *m_rectItem = new QGraphicsRectItem(); // 定义一个矩形图元
m_rectItem->setRect(0, 0, 80, 80);
m_rectItem->setPen(pen);
m_rectItem->setBrush(QBrush(QColor(255, 0, 255)));
m_rectItem->setFlag(QGraphicsItem::ItemIsMovable);
QGraphicsLineItem *m_lineItem = new QGraphicsLineItem(); // 定义一个直线图元
m_lineItem->setLine(QLineF(0, 0, 100, 100));
m_lineItem->setPen(pen);
m_lineItem->setFlag(QGraphicsItem::ItemIsMovable);
QGraphicsPathItem *m_pathItem = new QGraphicsPathItem(); // 定义一个路径图元
QPainterPath path;
path.moveTo(90, 50);
for (int i = 1; i < 5; ++i) {
path.lineTo(50 + 40 * cos(0.8 * i * M_PI), 50 + 40 * sin(0.8 * i * M_PI));
}
path.closeSubpath();
m_pathItem->setPath(path);
m_pathItem->setPen(pen);
m_pathItem->setFlag(QGraphicsItem::ItemIsMovable);
QGraphicsPolygonItem *m_polygonItem = new QGraphicsPolygonItem(); // 定义一个多边形图元
QPolygonF polygon;
polygon << QPointF(-100.0, -150.0) << QPointF(-120.0, 150.0)
<< QPointF(320.0, 160.0) << QPointF(220.0, -140.0);
m_polygonItem->setPolygon(polygon);
m_polygonItem->setPen(pen);
m_polygonItem->setFlag(QGraphicsItem::ItemIsMovable);
scene.addItem(m_rectItem); // 把矩形图元添加到场景
scene.addItem(m_lineItem); // 把直线图元添加到场景
scene.addItem(m_pathItem); // 把路径图元添加到场景
scene.addItem(m_polygonItem); // 把多边形图元添加到场景
QGraphicsView view(&scene); // 定义一个视图,并把场景添加到视图
view.resize(1024, 768);
view.show();
return a.exec();
}
2. 入门篇 <widget.cpp下><修改版>
注:
- 场景sence和view视图需要写成指针形式,即写在堆上,才不会自动结束(一闪而过)。
- 若用函数成员变量形式,即写在栈上,会自动结束(一闪而过)。
堆(Heap)与栈(Stack)的区别:
——参考 堆与栈的区别
一般有两层含义:
- 在程序内存布局场景下,堆与栈表示两种内存管理方式
- 在数据结构场景下,堆与栈表示两种常用的数据结构
在程序内存布局场景下:
- 栈:由操作系统自动分配释放 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。栈中存储的数据的生命周期随着函数的执行完成而结束。
- 堆:由开发人员分配和释放, 若开发人员不释放,程序结束时由 OS 回收,分配方式类似于链表。堆中存储的数据若未释放,则其生命周期等同于程序的生命周期。
- 区别:是操作系统对进程占用的内存空间的两种管理方式
在数据结构场景下:
- 栈:是一种运算受限的线性表。栈的基本操作包括初始化、判断栈是否为空、入栈、出栈以及获取栈顶元素等。
- 堆:是一种常用的树形结构,是一种特殊的完全二叉树,当且仅当满足所有节点的值总是不大于或不小于其父节点的值的完全二叉树被称之为堆。堆的基本操作包括建立、插入、删除。
#include "widget.h"
#include "./ui_widget.h"
#include<QGraphicsView>
#include<QPushButton>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//按钮
connect(ui->btn,&QPushButton::clicked,[=](){
//定义一个场景,设置场景背景为红色
QGraphicsScene * scene;
scene = new QGraphicsScene;
scene->setBackgroundBrush(Qt::red);
//定义一个画笔,设置画笔颜色和宽度
QPen pen;
pen.setColor(Qt::cyan);
pen.setWidth(10);
//定义一个矩形图元
QGraphicsRectItem * m_rectItem = new QGraphicsRectItem();
m_rectItem->setRect(0,0,80,80);
m_rectItem->setPen(pen);
m_rectItem->setBrush(QBrush(QColor(255, 0, 255)));
m_rectItem->setFlag(QGraphicsItem::ItemIsMovable);
//把矩形图元添加到场景
scene->addItem(m_rectItem);
//定义一个视图,把场景添加到视图
QGraphicsView *view;
view = new QGraphicsView(scene);
view->resize(1000,768);
view->show();
});
}
Widget::~Widget()
{
delete ui;
}