QT性能优化之QT6框架高性能图形视图框架快速展示百万图元大规模场景

简介:

本文展示了使用QT图形视图框架在一个场景中绘制出百万个图元的程序的效果以及源代码;本文还介绍了QT图形视图框架的一些实用功能和QT图形视图框架的整体结构;本文还介绍了QT大场景应用有哪些可能的优化改进空间。本文还展示了如何通过线程池初始化百万个图元而不影响用户界面及时响应用户操作。
QT能否快速展示千万级图元的大规模场景? 看完补天云的这个实测视频就有结果了! QT性能优化之QT6框架高性能图形视图框架快速展示千万图元大规模场景。本文稍后会提供实际效果演示视频链接。

正文:

QT绘制百万个图元的场景的效果展示

QT绘制百万图元大场景
备注:

(a)1000行1000列,总共100万个图元。注意存在水平滚动条和垂直滚动条。

(b)由于创建了100个图元对象,使得这个初始化过程比较费时间。在这个实例中使用QT线程池避免了漫长的初始化过程影响到了界面操作的用户体验。

一旦初始化过程完成之后,可以通过滚动条或者鼠标控制随意滚动,几乎感受不到延迟。

QT绘制百万图元大场景
备注:

(a)100行10000列,总共100万个图元。注意存在水平滚动条和垂直滚动条。

QT能否快速展示千万级图元的大规模场景? 看完补天云的这个实测视频就有结果了! QT性能优化之QT6框架高性能图形视图框架快速展示千万图元大规模场景

本文描述之运行效果对应视频如下:

QT性能优化之QT6框架高性能图形视图框架快速展示千万图元大规模场景

QT绘制百万个图元的场景的源代码

主窗口ButianyunWidget.h文件:

class QGraphicsScene;
class QGraphicsView;
class QGraphicsItem;
class QLabel;
class QThreadPool; class ButianyunWidget : public QWidget
{
Q_OBJECT public:
ButianyunWidget(QWidget *parent = nullptr);
~ButianyunWidget(); signals:
//场景初始化部分完成的信号
void sig_scene_items_ready(QVector<QGraphicsItem*>* items, int count); private slots:
//场景初始化部分完成的槽函数
void slot_scene_items_ready(QVector<QGraphicsItem*>* items, int count); private:
//场景
QGraphicsScene* scene;
//视图
QGraphicsView* view;
//线程池
QThreadPool* pool;
//状态标签
QLabel* label;
//记录现在已经完成了多少个图元的创建
int ready_count;
};

主窗口ButianyunWidget.cpp文件:

void butianyun_init_scene(ButianyunWidget* widget, QThreadPool* pool);

ButianyunWidget::ButianyunWidget(QWidget *parent)
: QWidget(parent)
, ready_count(0)
{
scene = new QGraphicsScene(this);
QVBoxLayout* main_layout = new QVBoxLayout();
setLayout(main_layout); label = new QLabel("Initializing...");
main_layout->addWidget(label); view = new QGraphicsView(scene);
main_layout->addWidget(view); //创建好空的界面之后立即把界面显示出来
showMaximized(); //链接信号和槽。
connect(this, &ButianyunWidget::sig_scene_items_ready,
this, &ButianyunWidget::slot_scene_items_ready); //创建线程池来在后台执行初始化操作
//避免漫长的初始化过程影响到对用户界面操作的及时响应
pool = new QThreadPool(this);
butianyun_init_scene(this, pool);
} ButianyunWidget::~ButianyunWidget()
{
} void ButianyunWidget::slot_scene_items_ready(QVector<QGraphicsItem*>* items, int count)
{
ready_count += items->length();
view->setUpdatesEnabled(false);
for (int i = 0; i < items->length(); i++)
{
scene->addItem(items->at(i));
}
view->setUpdatesEnabled(true);
delete items;
if (ready_count < count)
{
label->setText(QString("Initializing %%1 ...").arg(ready_count * 100 / count));
}
else
{
label->setText(QString("total %1 items ready").arg(count));
}
}

在线程池中初始化场景中的百万个图元对象:

//场景中行的数量和列的数量。1000行x1000列,总共100万个图元。
#define BUTIANYUN_CELL_ROW_COUNT 1000
#define BUTIANYUN_CELL_COLUMN_COUNT 1000 //每个网格的宽度和高度
#define BUTIANYUN_CELL_WIDTH 10.0
#define BUTIANYUN_CELL_HEIGHT 10.0 //线程池中的线程使用的堆栈字节数
#define BUTIANYUN_STACK_SIZE_BYTES (1024 * 1024 * 64)
//线程池中最大的线程数量
#define BUTIANYUN_THREADPOOL_MAX_THREAD_COUNT 16 //在线程池中初始化场景中的所有图元
void butianyun_init_scene(ButianyunWidget* widget, QThreadPool* pool)
{
//这些图元使用的颜色。
const QColor COLORS[] = {
QColor(255, 0, 0), QColor(0, 255, 0), QColor(0, 0, 255),
QColor(255, 255, 0), QColor(255, 0, 255), QColor(0, 255, 255),
QColor(128, 0, 0), QColor(0, 128, 0), QColor(0, 0, 128),
QColor(128, 128, 0), QColor(128, 0, 128), QColor(0, 128, 128)
};
const int COLOR_COUNT = sizeof(COLORS)/sizeof(QColor);
pool->setStackSize(BUTIANYUN_STACK_SIZE_BYTES);
pool->setMaxThreadCount(BUTIANYUN_THREADPOOL_MAX_THREAD_COUNT); //每列中的所有网格使用一个线程进行初始化。
for (int j = 0; j < BUTIANYUN_CELL_COLUMN_COUNT; j++)
{
//具体初始化操作的LAMBDA表达式
auto f = [=] {
//创建具体的图元对象之后保存到这个数组中。
QVector<QGraphicsItem*>* items = new QVector<QGraphicsItem*>();
items->resize(BUTIANYUN_CELL_ROW_COUNT); //每一个网格中包含一个图元。
for (int i = 0; i < BUTIANYUN_CELL_ROW_COUNT; i++)
{
//计算图元的坐标和大小。
qreal x = j * BUTIANYUN_CELL_WIDTH + BUTIANYUN_CELL_WIDTH * 0.1;
qreal y = i * BUTIANYUN_CELL_HEIGHT + BUTIANYUN_CELL_HEIGHT * 0.1;
qreal w = BUTIANYUN_CELL_WIDTH * 0.8;
qreal h = BUTIANYUN_CELL_HEIGHT * 0.8; //创建椭圆图元或者矩形图元。
if ((i + j) % 2)
{
QGraphicsEllipseItem* item = new QGraphicsEllipseItem();
item->setRect(x, y, w, h);
item->setBrush(QBrush(COLORS[(i * BUTIANYUN_CELL_COLUMN_COUNT + j) % COLOR_COUNT]));
(*items)[i] = item;
}
else
{
QGraphicsRectItem* item = new QGraphicsRectItem();
item->setRect(x, y, w, h);
item->setBrush(QBrush(COLORS[(i * BUTIANYUN_CELL_COLUMN_COUNT + j) % COLOR_COUNT]));
(*items)[i] = item;
}
} //这一列的所有图元创建完成之后给主窗口发送信号通知。
emit widget->sig_scene_items_ready(items, BUTIANYUN_CELL_COLUMN_COUNT * BUTIANYUN_CELL_ROW_COUNT);
}; //在线程池中启动这个LAMBDA表达式所表示的工作任务。
pool->start(f);
}
}

QT图形视图框架是什么?

图形视图提供了一个表面用于管理大量自定义的二维图形条目并与之进行交互,还提供了一个视图窗口用于可视化这些图形条目,并提供缩放和旋转支持。

这个框架包含了事件传播体系,这个体系允许对场景中的条目的双精度的交互能力。条目能够处理键盘事件、鼠标按下、移动、释放和双击事件,条目还能跟踪鼠标运动。

图形视图使用BSP(二叉空间剖分)树来提供很快速的条目发现能力,作为这种能力的一个结果,它还能实时的可视化拥有数以百万计的宏大场景。

QT图形视图框架有哪些实用功能?

这里介绍一下QT图形视图框架中的图形条目(QGraphicsItem)的能力。

图形条目组合管理能力

图形条目可以包含另外的图形条目。也就是若干图形条目可以组装成一个整体,既能够对整体进行各种常用操作,也能对部分进行各种常用操作。这个特点使得QT图形视图框架能够表达一些包含多个部件和多层次部件的复杂图形对象。图形条目组合管理能力使得一个复杂图形对象整体上可以看做是一个通过图形条目对象之间的父子关系建立起来的树状结构,而且每一个子节点都可以进行管控。

QT图元条目组合管理

QT图元条目组合管理

图形条目分组管理能力。

图形视图框架提出了图形条目分组的概念,并设计了QGraphicsItemGroup这个类型来支持分组管理的概念。在QT图形视图框架中,QGraphicsItemGroup是QGraphicsItem的派生类,因此图形条目分组是一种特殊的图形条目。一个分组中可以包含多个具体图形条目。图形条目分组管理能力使得使用树状结构来描述复杂场景中的层次结构关系变得非常便利。

QT图元条目分组管理

QT图元条目分组管理

图形条目的几何变换能力

支持各种几何变换操作,包括平移、缩放和旋转等操作,还支持拖放操作(Drog & Drop),动画操作。

QT图形条目几何变换

QGraphicsItem类型提供了设置几何变换的函数接口。

QT图形条目几何变换

图形条目事件传播和处理能力

支持事件传播和处理,包括各种鼠标事件、键盘事件等。 QGraphicsItem类型提供了较多的事件处理有关的虚函数,派生类型可以通过重写某个虚函数来响应图形条目上的鼠标键盘事件等QT事件。

QT图形条目事件传播和处理

图形条目对象信号与槽能力

QGraphicsItem并不是QObject的派生类型,因此普通的图形条目类型并不支持QT对象的信号与槽的连接能力。但是QT图形视图框架还提供了QGraphicsItem的一个特殊的派生类型QGraphicsObject类型,这个类型同时也是QObject类型的派生类型。因此使用QGraphicsObject及其派生类型创建的对象,既能够放到图形视图场景中使用,也能够当做一个QT对象来发挥QT信号与槽的价值。

图形视图与QWidget组合能力

QT图形视图框架中的图形场景QGraphicsScene已经能够完整的管理场景中的图形条目QGraphicsItem,包括将绘制出整个场景的展示结果。但是这一部分是图形视图框架相对比较独立的核心结构和功能。

QT图形视图与QWidget窗口组合

QT图形视图框架中的图形视图QGraphicsView类型的祖先类型是QWidget类型,因此QGraphicsView类型使得QT图形场景和图形条目的渲染结果能够直接在视图中进行展示。而这个视图也就是QGraphicsView对象,其类型本身就是一个QT窗口类型,可以直接嵌入到使用QWidget的任何普通窗口应用程序中作为界面的一部分,或者就将这个视图作为主窗口。

QT图形视作为独立窗口

另外QGraphicsView类型是QAbstractScrollArea类型的直接派生类型,可以支持水平滚动条和垂直滚动条的相关操作。

图形视图整体几何变换能力

QGraphicsView类型本身提供了场景整体的几何变换能力。这使得QT图形视图框架不仅支持图形条目图元级别的几何变换,还支持图形场景整体的几何变换。这是普通QWidget窗口没有直接的功能。

QT图形视图整体几何变换

QT大场景应用有哪些可能的优化改进空间?

100万图元大场景内存占用有待优化

QT图形视图框架尽管可以轻松支持百万数量级的图元,如果计算机内存足够大则可以支持更多数量的图元。 比如本文介绍的使用QT图形视图框架绘制百万个图元的场景的实例程序运行时,消耗掉了将近2GB的内存。

QT图形视图框架100万大场景内存占用

这个应用程序直接在初始化时创建了100万个图形条目,占用了较多内存。整个场景中的图形条目数量巨大,无法在完整展现在窗口可见区域,必须拖动滚动条才能看到各个区域的图形条目。如果能够进一步优化程序,使得初始化时只创建出可见区域中的图形条目,在场景滚动过程中动态的创建所需的可见区域的图形条目,那么程序的初始化时间成本和内存消耗成本可能能够得到大幅度的降低。

GPU硬件加速性能有待优化

QT图形视图框架默认情况下没有启用OpenGL支持,因此无法使用GPU硬件加速能力。下图是本文介绍的QT绘制百万个图元的场景的实例运行时的CPU和GPU占用情况,可以看到这个进程没有使用GPU硬件加速。

QT图形视图框架默认不启用GPU硬件加速

QT图形视图框架可以通过OpenGL支持来使用GPU硬件加速能力。

通过如下代码启用OpenGL支持,从而使得一个使用QT图形视图框架的QT应用程序能够充分借助GPU硬件加速能力来提升应用程序的性能。

QT图形视图框架启用GPU硬件加速
在本文介绍的QT绘制百万个图元的场景的实例代码中加上上述代码之后再次运行后快速滚动图形场景则可以看到已经使用到GPU硬件加速能力。

QT图形视图框架启用GPU硬件加速
但是美中不足的是,从这个实例运行情况来看,似乎使用GPU硬件加速之后,CPU占用率也增加了不少。有待进一步优化大场景应用程序。

下面这个视频展示了如何使用QT框架实现快速展示千万级别的图元的大规模场景。
QT性能优化之QT6框架高性能图形视图框架快速展示千万图元大规模场景

QT图形视图框架的整体结构

从整体上来讲,QT图形视图框架包含了三个组成部分:

图形场景(QGraphicsScene)

负责图形场景的整体控制,以及场景中的图形条目的管理工作。

ü 图形条目(QGraphicsItem)

负责具体图元的绘制和几何变换控制以及事件处理。

ü 图形视图(QGraphicsView)

负责图形场景的用户界面交互操作。

下图是<<项目实战原理源码界面美化视频课程QT/QSS/QML/C++/STL >>中的一个截图,可能对理解QT图形视图框架的整体结构有一些帮助。


QT图形视图框架整体结构(C++类)

如果您想系统学习QT框架常规技术,可以看一下这个课程。
QT5/QSS/QML/C++界面美化原理源码项目实战视频课程

如果您C++基础比较好,可以看一下这个课程。
QT5 QSS QML 原理源码 界面美化 项目实战 视频课程

如果您想系统学习QT6性能优化技术,可以看一下这个课程。
QT性能优化实战 QML优化 QT高性能 QT6系列视频课程

如果您认为这篇文章对您有所帮助,请您一定立即点赞+喜欢+收藏,本文作者将能从您的点赞+喜欢+收藏中获取到创作新的好文章的动力。如果您认为作者写的文章还有一些参考价值,您也可以关注这篇文章的作者。

【QT性能优化】QT性能优化之QT6框架高性能图形视图框架快速展示百万图元大规模场景的更多相关文章

  1. Qt之图形视图框架

    简述 图形视图(Graphics View)提供了一个平台,用于大量自定义2D图元的管理与交互,并提供了一个视图部件(view widget)来显示可以缩放和旋转的图元. 框架包括一个事件传播架构,支 ...

  2. Qt开发技术:图形视图框架(一)基本介绍

    前话   使用到Qt的视图框架.   Qt视图框架介绍 简介   图形视图框架(The Graphic View Framework)用于管理和与大量定制的二维图形项目交互,以及用于可视化项目的视图小 ...

  3. Qt 2D绘图之五:图形视图框架的结构和坐标系统

    一.图形视图框架的结构 在前面讲的基本绘图中,我们可以自己绘制各种图形,并且控制它们.但是,如果需要同时绘制很多个相同或不同的图形,并且要控制它们的移动.检测它们的碰撞和叠加:或者我们想让自己绘制的图 ...

  4. Qt开发技术:图形视图框架(二)场景QGraphicsScene、QGraphicsItem与QGraphicsView详解

    前话   Qt的图形视图框架,最核心的三个类为:QGraphicsScene.QGraphicsItem与QGraphicsView.   基于图形框架的高级白板软件Demo QGraphicsSce ...

  5. Qt图形视图框架公开课资料

    接受CSDN学院的邀请,讲一次公开课,主题是Qt图形视图框架,报名链接在这里:http://edu.csdn.net/huiyiCourse/detail/228. 内容有两部分:自定义Item和拖放 ...

  6. 用Qt图形视图框架开发拼图游戏

    用Qt的图形视图框架(Graphics View Framework)做了一个拼图游戏DEMO,演示了: QGraphicsView.QGraphicsScene.QGraphicsItem的基本用法 ...

  7. Qt 2D绘图之六:图形视图框架的事件处理与传播

    一.简介 图形视图框架中的事件都是首先由视图进行接收,然后传递给场景,再由场景传递给相应的图形项.而对于键盘事件,它会传递给获得焦点的图形项,可以使用QGraphicsScene类的setFocusI ...

  8. QT 图形视图框架

    https://blog.csdn.net/qq769651718/article/details/79357936 使用QPushButton.QLabel.QCheckBox等构成GUI的控件或自 ...

  9. Qt动画与Qt坐标小记

    Qt动画 转载自: <http://jingyan.baidu.com/article/154b46315757b628ca8f4116.html> 和  <http://blog. ...

  10. Qt图形视图体系结构

    导读:本文主要翻译自QT 5.9.3GraphicsView官方文档 一.GraphicsView框架简介 QT4.2开始引入了Graphics View框架用来取代QT3中的Canvas模块,并作出 ...

随机推荐

  1. [oeasy]python0091_仙童公司_八叛逆_intel_8080_altair8800_牛郎星

    编码进化 个人电脑 计算机 通过电话网络 进行连接 极客 利用技术 做一些有趣的尝试 极客文化 是 认真研究技术的 文化 计算机 不再是 高校和研究机构高墙里面的 神秘事物 而是 生活中常见的 家用电 ...

  2. oeasy教您玩转vim - 36 - # 插入字符

    ​ 插入字符 回忆上节课内容 正则表达式 行头行尾 ^ 意味着行开头 $ 意味着行结尾 任意字符 . 代表任意字符 [a-z] 代表任意小写字母 字符数量 * 代表 0 到任意多个前字符 + 代表 1 ...

  3. Python 结合opencv实现图片截取和拼接

    实践环境 python 3.6.2 scikit-build-0.16.7 win10 opencv_python-4.5.4.60-cp36-cp36m-win_amd64.whl 下载地址: ht ...

  4. pyspark初步了解

    spark的运行角色: 分布式代码的流程分析 pythononspark原理

  5. 【WSDL】02 四种客户端调用方式

    WSDL概念和一些语法内容: https://www.w3school.com.cn/wsdl/index.asp SOAP概念: https://www.runoob.com/soap/soap-t ...

  6. 【Linux】快速文件交互 lrzsz

    首先需要安装依赖: yum install -y lrzsz 没有此依赖,Linux提示找不到命令: [root@localhost ~]# rz -bash: rz: 未找到命令 [root@loc ...

  7. 【SpringMVC】05 RestFul风格

    什么是RestFul风格? 一个资源定位和资源操作的风格,不是标准,也不是协议, 基于此风格的路径访问可以隐藏真实的参数传递,以提高网站的安全访问 以往的请求参数: jdbc:mysql://loca ...

  8. XML 教程——检视阅读

    基本 XML 简介 XML 指可扩展标记语言(eXtensible Markup Language). XML 被设计用来传输和存储数据.HTML 被设计用来显示数据. 什么是 XML? XML 指可 ...

  9. 如何在Windows10电脑上打开3D建模STL文件

    相关: https://www.bilibili.com/video/BV1gD4y1h7tj/

  10. 树莓派3b+ 安装windows10 arm版本的方法及使用体验

    首先,我再网上找到了一个很详细的为树莓派3b安装windows10 arm的教程,实际操作下来发现并不可行. 最后找到了可行的教程: 第3章 将Windows10镜像写入TF卡:https://zhu ...