C++使用Qt Charts可视化大规模点集
引言
数据可视化是数据分析和决策过程中的重要环节。随着数据量的不断增长,如何高效地可视化大规模数据集成为了一个挑战。Qt Charts 提供了一个强大的工具集,用于创建直观的数据可视化图表。本文将探讨如何使用 C++ 和 Qt Charts 可视化大规模点集,并介绍几种优化方法以提高性能。
问题背景
在许多应用场景中,如地理信息系统(GIS)、金融数据分析和科学研究等,常常需要处理和可视化大规模点集。例如,一个包含500万个点的数据集可能用于表示地理坐标、股票交易数据或实验结果。直接使用传统的可视化方法(如逐点绘制)会导致严重的性能问题,如渲染缓慢、内存占用过高和用户界面响应迟缓。
Qt Charts 是一个功能强大的库,用于创建各种类型的图表,包括折线图、柱状图和散点图。然而,在处理大规模数据时,Qt Charts 的默认设置可能无法满足性能要求。因此,需要采用优化策略来提高性能。
本例点集存在 points.txt,存储在 D 盘根目录下。每个点由 X 坐标和 Y 坐标组成,坐标之间用逗号分隔,每行一个点。
1.2,3.4
5.6,7.8
9.0,1.2
3.4,5.6
7.8,9.0
2.3,4.5
6.7,8.9
4.5,6.7
8.9,2.3
3.4,1.2
解决方法
方法一:采样显示
采样显示是一种常用的优化方法,通过只绘制部分点来减少渲染开销。这种方法适用于数据密度较高且不需要显示每个点的场景。
示例代码
#include <QApplication>
#include <QtCharts/QChartView>
#include <QtCharts/QScatterSeries>
#include <QtCharts/QValueAxis>
#include <QFile>
#include <QTextStream>
#include <QVector>
#include <QPointF>
#include <QMessageBox>using namespace QtCharts;int main(int argc, char* argv[])
{QApplication app(argc, argv);// 创建散点图序列QScatterSeries* series = new QScatterSeries();series->setName(QStringLiteral("点集数据"));// 读取文件中的点数据QFile file("D:/points.txt");if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {QMessageBox::critical(nullptr, QStringLiteral("错误"), QStringLiteral("无法打开文件: D:/points.txt"));return -1;}QTextStream in(&file);QVector<QPointF> points;// 读取文件中的点数据while (!in.atEnd()) {QString line = in.readLine();QStringList values = line.split(',');if (values.size() == 2) {bool okX, okY;double x = values[0].toDouble(&okX);double y = values[1].toDouble(&okY);if (okX && okY) {points.append(QPointF(x, y));}}}file.close();// 如果没有数据,显示错误信息if (points.isEmpty()) {QMessageBox::critical(nullptr, QStringLiteral("错误"), QStringLiteral("文件中没有有效的点数据"));return -1;}// 动态计算坐标轴范围double minX = points[0].x();double maxX = points[0].x();double minY = points[0].y();double maxY = points[0].y();for (const QPointF& point : points) {minX = qMin(minX, point.x());maxX = qMax(maxX, point.x());minY = qMin(minY, point.y());maxY = qMax(maxY, point.y());}// 抽样:只绘制部分点int sampleRate = 100; // 每 100 个点抽取一个for (int i = 0; i < points.size(); i += sampleRate) {series->append(points[i]);}// 创建图表QChart* chart = new QChart();chart->setTitle(QStringLiteral("平面点集可视化(采样显示)"));chart->addSeries(series);// 设置图表主题chart->setTheme(QChart::ChartThemeLight);// 创建坐标轴QValueAxis* axisX = new QValueAxis;axisX->setTitleText(QStringLiteral("X 轴"));axisX->setLabelFormat("%g");axisX->setRange(minX, maxX);QValueAxis* axisY = new QValueAxis;axisY->setTitleText(QStringLiteral("Y 轴"));axisY->setLabelFormat("%g");axisY->setRange(minY, maxY);// 将坐标轴附加到图表和序列chart->addAxis(axisX, Qt::AlignBottom);chart->addAxis(axisY, Qt::AlignLeft);series->attachAxis(axisX);series->attachAxis(axisY);// 创建图表视图QChartView* chartView = new QChartView(chart);chartView->setRenderHint(QPainter::Antialiasing);// 显示图表chartView->resize(800, 600);chartView->show();return app.exec();
}

方法二:全显示
对于需要显示所有点的场景,可以通过优化渲染方式来提高性能。例如,使用 QLineSeries 代替 QScatterSeries,并调整点的大小。
示例代码
#include <QApplication>
#include <QtCharts/QChartView>
#include <QtCharts/QLineSeries>
#include <QtCharts/QValueAxis>
#include <QFile>
#include <QTextStream>
#include <QVector>
#include <QPointF>
#include <QMessageBox>using namespace QtCharts;int main(int argc, char* argv[])
{QApplication app(argc, argv);// 创建线序列QLineSeries* series = new QLineSeries();// 读取文件中的点数据QFile file("D:/points.txt");if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {QMessageBox::critical(nullptr, QStringLiteral("错误"), QStringLiteral("无法打开文件: D:/points.txt"));return -1;}QTextStream in(&file);QVector<QPointF> points;// 读取文件中的点数据while (!in.atEnd()) {QString line = in.readLine();QStringList values = line.split(',');if (values.size() == 2) {bool okX, okY;double x = values[0].toDouble(&okX);double y = values[1].toDouble(&okY);if (okX && okY) {points.append(QPointF(x, y));}}}file.close();// 如果没有数据,显示错误信息if (points.isEmpty()) {QMessageBox::critical(nullptr, QStringLiteral("错误"), QStringLiteral("文件中没有有效的点数据"));return -1;}// 动态计算坐标轴范围double minX = points[0].x();double maxX = points[0].x();double minY = points[0].y();double maxY = points[0].y();for (const QPointF& point : points) {minX = qMin(minX, point.x());maxX = qMax(maxX, point.x());minY = qMin(minY, point.y());maxY = qMax(maxY, point.y());}// 将点数据添加到线序列for (const QPointF& point : points) {series->append(point);}// 创建图表QChart* chart = new QChart();chart->setTitle(QStringLiteral("平面点集可视化(全显示)"));chart->addSeries(series);// 设置图表主题chart->setTheme(QChart::ChartThemeLight);// 创建坐标轴QValueAxis* axisX = new QValueAxis;axisX->setTitleText(QStringLiteral("X 轴"));axisX->setLabelFormat("%g");axisX->setRange(minX, maxX);QValueAxis* axisY = new QValueAxis;axisY->setTitleText(QStringLiteral("Y 轴"));axisY->setLabelFormat("%g");axisY->setRange(minY, maxY);// 将坐标轴附加到图表和序列chart->addAxis(axisX, Qt::AlignBottom);chart->addAxis(axisY, Qt::AlignLeft);series->attachAxis(axisX);series->attachAxis(axisY);// 设置点的大小QPen pen(Qt::black);pen.setWidth(1);series->setPen(pen);// 创建图表视图QChartView* chartView = new QChartView(chart);chartView->setRenderHint(QPainter::Antialiasing);// 显示图表chartView->resize(800, 600);chartView->show();return app.exec();
}

性能优化
1. 动态调整坐标轴范围
在读取数据后,动态计算数据的最小值和最大值,以确保所有点都能正确显示。这避免了手动设置不合适的坐标轴范围。
2. 使用高效的渲染方式
启用抗锯齿渲染以提高图表质量,同时使用 QLineSeries 代替 QScatterSeries 以减少渲染开销。
3. 添加进度指示器
在读取和处理大规模数据时,添加进度指示器可以提升用户体验,特别是在处理大量数据时。
#include <QProgressDialog>// 在读取数据时显示进度指示器
QProgressDialog progressDialog;
progressDialog.setLabelText(QStringLiteral("正在加载数据..."));
progressDialog.setRange(0, points.size());
progressDialog.show();int count = 0;
for (const QPointF& point : points) {series->append(point);count++;progressDialog.setValue(count);qApp->processEvents(); // 更新UI
}
总结
通过采样显示和全显示两种方法,可以有效地使用 C++ 和 Qt Charts 可视化大规模点集。采样显示适用于不需要显示每个点的场景,而全显示则通过优化渲染方式来提高性能。动态调整坐标轴范围、使用高效的渲染方式和添加进度指示器等优化措施可以进一步提升性能和用户体验。
未来的研究方向包括探索更高效的渲染技术(如 OpenGL)和进一步优化数据处理算法,以应对更大规模的数据集。
相关文章:
C++使用Qt Charts可视化大规模点集
引言 数据可视化是数据分析和决策过程中的重要环节。随着数据量的不断增长,如何高效地可视化大规模数据集成为了一个挑战。Qt Charts 提供了一个强大的工具集,用于创建直观的数据可视化图表。本文将探讨如何使用 C 和 Qt Charts 可视化大规模点集&#…...
Java观察者模式详解
观察者模式详解 一、观察者模式概述 观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会自动收到通知并更新。 核心特点 松耦合:主题和…...
Pytorch torch.utils.data.dataloader.default_collate 介绍
torch.utils.data.dataloader.default_collate 是 PyTorch 中 DataLoader 默认的 collate_fn 函数,用于将一个批次的样本数据合并成张量(Tensor)或其他结构化数据格式。以下是关于 default_collate 的详细介绍: 1. 功能 default…...
质检LIMS系统在生态修复企业的实践 生态修复行业的质量管控难题
一、生态修复行业的质量管控新命题 在生态文明建设的大背景下,生态修复企业面临着复杂的环境治理挑战。土壤改良、水体净化、植被恢复等工程,均需以精准的实验数据支撑决策。传统实验室管理模式存在数据孤岛、流程非标、合规风险高等痛点,而…...
Spring Cloud之服务入口Gateway之Route Predicate Factories
目录 Route Predicate Factories Predicate 实现Predicate接口 测试运行 Predicate的其它实现方法 匿名内部类 lambda表达式 Predicate的其它方法 源码详解 代码示例 Route Predicate Factories The After Route Predicate Factory The Before Route Predicate Fac…...
《AI大模型应知应会100篇》第6篇:预训练与微调:大模型的两阶段学习方式
第6篇:预训练与微调:大模型的两阶段学习方式 摘要 近年来,深度学习领域的一个重要范式转变是“预训练-微调”(Pretrain-Finetune)的学习方式。这种两阶段方法不仅显著提升了模型性能,还降低了特定任务对大…...
Jetpack Compose 自定义组件完全指南
Jetpack Compose 自定义组件完全指南 Compose 的声明式 UI 范式为创建自定义组件提供了前所未有的灵活性。本指南将带你从基础到高级全面掌握 Compose 自定义组件的开发技巧。 一、自定义组件基础 1.1 基本结构 一个最简单的自定义组件: Composable fun Greeti…...
java后端对时间进行格式处理
时间格式处理 通过java后端,使用jackson库的注解JsonFormat(pattern "yyyy-MM-dd HH:mm:ss")进行格式化 package com.weiyu.pojo;import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; import lombok.Data; import …...
汽车BMS技术分享及其HIL测试方案
一、BMS技术简介 在全球碳中和目标的战略驱动下,新能源汽车产业正以指数级速度重塑交通出行格局。动力电池作为电动汽车的"心脏",其性能与安全性不仅直接决定了车辆的续航里程、使用寿命等关键指标,更深刻影响着消费者对电动汽车的…...
【Code】《代码整洁之道》笔记-Chapter3-函数
第3章 函数 在编程的早期岁月,系统由程序和子程序组成。后来,到Fortran和PL/1的年代,系统由程序、子程序和函数组成。如今,只有函数存活下来。函数是所有程序中的第一组代码。本章将讨论如何写好函数。 请看代码清单3-1。在Fit…...
【TI MSPM0】CMSIS-DSP库学习
一、什么是CMSIS-DSP库 基于Cortex微控制器软件接口标准的数字信号处理的函数库 二、页面概览 这个用户手册用来描述CMSIS-DSP软件的函数库,有通用的计算处理函数给Cortex-M和Cortex-A的处理器使用 三、工程学习 1.导入工程 2.样例介绍 在Q15的格式下,…...
Vue3:初识Vue,Vite服务器别名及其代理配置
一、创建一个Vue3项目 创建Vue3项目默认使用Vite作为现代的构建工具,以下指令本质也是通过下载create-vue来构建项目。 基于NodeJs版本大于等于18.3,使用命令行进行操作。 1、命令执行 npm create vuelatest输入项目名称 2、选择附加功能 选择要包含的功…...
音频接口格式与通道
IEC 60958-3 文档结构概览(通俗版) 对于初学者来说,IEC 60958-3 的文档就像一个“数字音频传输的说明书”。它告诉设备如何把声音变成一堆0和1,再通过这些0和1的排列规则,让接收设备准确还原声音。 1. 接口格式&#…...
JS中的WeakMap
WeakMap weakmap是一种类似map的类型,但它的key是弱引用,并且key只能是对象。 weakmap和map的区别 weakmap的key只能是对象,value可以是任何值 const weakMap new WeakMap(); // 创建WeakMap实例const key1 "key1"; // 字符…...
Go语言类型捕获及内存大小判断
代码如下: 类型捕获可使用:reflect.TypeOf(),fmt.Printf在的%T。 内存大小判断:len(),unsafe.Sizeof。 package mainimport ("fmt""unsafe""reflect" )func main(){var i , j 1, 2f…...
学透Spring Boot — 017. 处理静态文件
这是我的《学透Spring Boot》专栏的第17篇文章,了解更多内容请移步我的专栏: Postnull CSDN 学透 Spring Boot 目录 静态文件 静态文件的默认位置 通过配置文件配置路径 通过代码配置路径 静态文件的自动配置 总结 静态文件 以前的传统MVC的项目…...
ARK no NIGHTS
《昨夜圆车》(ARK no NIGHTS) 于 2025 年 4 月 1 日 16:00 开服。在这款游戏中,玩家将扮演博士,带领整合运动击败罗德岛。FloorWinter 非常期待第一时间体验这款船新的游戏,于是他决定昏睡过去,直到游戏开服再醒来。 游戏开服的时…...
CMake实战指南一:add_custom_command
CMake 进阶:add_custom_command 用法详解与实战指南 在 CMake 构建系统中,add_custom_command 是一个灵活且强大的工具,允许开发者在构建流程中插入自定义操作。无论是生成中间文件、执行预处理脚本,还是在目标构建前后触发额外逻…...
指纹浏览器技术架构解析:高并发批量注册业务的工程化实践——基于分布式指纹引擎与防关联策略的深度实现
一、技术背景与行业痛点 在跨境电商、广告投放、问卷调查等场景中,批量注册与多账号矩阵运营已成为刚需。然而,主流平台(如亚马逊、Facebook、Google)的风控系统通过浏览器指纹追踪(Canvas/WebGL/WebRTC等)…...
懂x帝二手车数据爬虫-涉及简单的字体加密,爬虫中遇到“口”问题的解决
#脚本如下 import requests import pprint import timeurl https://www.dongchedi.com/motor/pc/sh/sh_sku_list?aid1839&app_nameauto_web_pc headers {User-Agent: Mozilla/5.0 }font_map {58425: 0, 58700: 1, 58467: 2, 58525: 3,58397: 4, 58385: 5, 58676: 6, 58…...
4.7学习总结 java集合进阶
集合进阶 泛型 //没有泛型的时候,集合如何存储数据 //结论: //如果我们没有给集合指定类型,默认认为所有的数据类型都是object类型 //此时可以往集合添加任意的数据类型。 //带来一个坏处:我们在获取数据的时候,无法使用他的特有行为。 //此…...
Python高阶函数-eval深入解析
1. eval() 函数概述 eval() 是 Python 内置的一个强大但需要谨慎使用的高阶函数,它能够将字符串作为 Python 表达式进行解析并执行。 基本语法 eval(expression, globalsNone, localsNone)expression:字符串形式的 Python 表达式globals:可…...
LLM面试题八
推荐算法工程师面试题 二分类的分类损失函数? 二分类的分类损失函数一般采用交叉熵(Cross Entropy)损失函数,即CE损失函数。二分类问题的CE损失函数可以写成:其中,y是真实标签,p是预测标签,取值为0或1。 …...
【团体程序涉及天梯赛】L1~L2实战反思合集(C++)
实战反思汇总记录 仔细审题,想好再写 L1-104 九宫格 - 团体程序设计天梯赛-练习集 易忽略的错误:开始习惯性地看到n就以为是n*n数组了,实际上应该是9*9的固定大小数组,查了半天没查出来 L1-101 别再来这么多猫娘了!…...
Linux Terminal Mode | canonical / nocanonical / cbreak / raw
注:本文为 “Linux 终端模式” 相关文章合辑。 略作重排,如有内容异常,请看原文。 终端输入输出的三种模式 guidao 1 前言 在进行项目开发时,需要实时读取终端输入(无需按下 Enter 键即可读取)。然而&a…...
预测分析(二):基于机器学习的数值预测
文章目录 基于机器学习的数值预测机器学习简介监督学习的任务创建第一个机器学习模型机器学习的目标——泛化过拟合现象评价函数与最优化 建模前的数据处理进一步特征变换 多元线性回归模型LASSO回归kNN算法原理算法步骤k值的选择 基于机器学习的数值预测 机器学习是人工智能的…...
JavaScript双问号操作符(??)详解,解决使用 || 时因类型转换带来的问题
目录 JavaScript双问号操作符(??)详解,解决使用||时因类型转换带来的问题 一、双问号操作符??的基础用法 1、传统方式的痛点 2、双问号操作符??的精确判断 3、双问号操作符??与逻辑或操作符||的对比 二、复杂场景下的空值处理 …...
蓝桥杯 web 展开你的扇子(css3)
普通答案: #box:hover #item1{transform: rotate(-60deg); } #box:hover #item2{transform: rotate(-50deg); } #box:hover #item3{transform: rotate(-40deg); } #box:hover #item4{transform: rotate(-30deg); } #box:hover #item5{transform: rotate(-20deg); }…...
聚焦楼宇自控:优化建筑性能,引领智能化管控与舒适环境
在当今建筑行业蓬勃发展的浪潮中,人们对建筑的要求早已超越了传统的遮风避雨功能,而是更加注重建筑性能的优化、智能化的管控以及舒适环境的营造。楼宇自控系统作为现代建筑技术的核心力量,正凭借其卓越的功能和先进的技术,在这几…...
前端视频流技术深度解析
一、视频流技术体系架构 1.1 现代视频流技术栈 1.1.1 核心协议对比 协议传输方式延迟适用场景浏览器支持HLSHTTP分片6-30s点播、直播回看全平台DASHHTTP动态适配3-15s多码率自适应Chrome/FirefoxWebRTCP2P/UDP<500ms实时通信、直播现代浏览器RTMPTCP长连接1-3s传统直播推…...
