C++使用Qt Charts可视化大规模点集

news2025/4/8 8:34:14

引言

数据可视化是数据分析和决策过程中的重要环节。随着数据量的不断增长,如何高效地可视化大规模数据集成为了一个挑战。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)和进一步优化数据处理算法,以应对更大规模的数据集。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2330345.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

质检LIMS系统在生态修复企业的实践 生态修复行业的质量管控难题

一、生态修复行业的质量管控新命题 在生态文明建设的大背景下&#xff0c;生态修复企业面临着复杂的环境治理挑战。土壤改良、水体净化、植被恢复等工程&#xff0c;均需以精准的实验数据支撑决策。传统实验室管理模式存在数据孤岛、流程非标、合规风险高等痛点&#xff0c;而…

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篇&#xff1a;预训练与微调&#xff1a;大模型的两阶段学习方式 摘要 近年来&#xff0c;深度学习领域的一个重要范式转变是“预训练-微调”&#xff08;Pretrain-Finetune&#xff09;的学习方式。这种两阶段方法不仅显著提升了模型性能&#xff0c;还降低了特定任务对大…

java后端对时间进行格式处理

时间格式处理 通过java后端&#xff0c;使用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技术简介 在全球碳中和目标的战略驱动下&#xff0c;新能源汽车产业正以指数级速度重塑交通出行格局。动力电池作为电动汽车的"心脏"&#xff0c;其性能与安全性不仅直接决定了车辆的续航里程、使用寿命等关键指标&#xff0c;更深刻影响着消费者对电动汽车的…

【TI MSPM0】CMSIS-DSP库学习

一、什么是CMSIS-DSP库 基于Cortex微控制器软件接口标准的数字信号处理的函数库 二、页面概览 这个用户手册用来描述CMSIS-DSP软件的函数库&#xff0c;有通用的计算处理函数给Cortex-M和Cortex-A的处理器使用 三、工程学习 1.导入工程 2.样例介绍 在Q15的格式下&#xff0c…

Vue3:初识Vue,Vite服务器别名及其代理配置

一、创建一个Vue3项目 创建Vue3项目默认使用Vite作为现代的构建工具&#xff0c;以下指令本质也是通过下载create-vue来构建项目。 基于NodeJs版本大于等于18.3&#xff0c;使用命令行进行操作。 1、命令执行 npm create vuelatest输入项目名称 2、选择附加功能 选择要包含的功…

Go语言类型捕获及内存大小判断

代码如下&#xff1a; 类型捕获可使用&#xff1a;reflect.TypeOf()&#xff0c;fmt.Printf在的%T。 内存大小判断&#xff1a;len()&#xff0c;unsafe.Sizeof。 package mainimport ("fmt""unsafe""reflect" )func main(){var i , j 1, 2f…

学透Spring Boot — 017. 处理静态文件

这是我的《学透Spring Boot》专栏的第17篇文章&#xff0c;了解更多内容请移步我的专栏&#xff1a; Postnull CSDN 学透 Spring Boot 目录 静态文件 静态文件的默认位置 通过配置文件配置路径 通过代码配置路径 静态文件的自动配置 总结 静态文件 以前的传统MVC的项目…

CMake实战指南一:add_custom_command

CMake 进阶&#xff1a;add_custom_command 用法详解与实战指南 在 CMake 构建系统中&#xff0c;add_custom_command 是一个灵活且强大的工具&#xff0c;允许开发者在构建流程中插入自定义操作。无论是生成中间文件、执行预处理脚本&#xff0c;还是在目标构建前后触发额外逻…

懂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集合进阶

集合进阶 泛型 //没有泛型的时候&#xff0c;集合如何存储数据 //结论: //如果我们没有给集合指定类型&#xff0c;默认认为所有的数据类型都是object类型 //此时可以往集合添加任意的数据类型。 //带来一个坏处:我们在获取数据的时候&#xff0c;无法使用他的特有行为。 //此…

Python高阶函数-eval深入解析

1. eval() 函数概述 eval() 是 Python 内置的一个强大但需要谨慎使用的高阶函数&#xff0c;它能够将字符串作为 Python 表达式进行解析并执行。 基本语法 eval(expression, globalsNone, localsNone)expression&#xff1a;字符串形式的 Python 表达式globals&#xff1a;可…

LLM面试题八

推荐算法工程师面试题 二分类的分类损失函数&#xff1f; 二分类的分类损失函数一般采用交叉熵(Cross Entropy)损失函数&#xff0c;即CE损失函数。二分类问题的CE损失函数可以写成&#xff1a;其中&#xff0c;y是真实标签&#xff0c;p是预测标签&#xff0c;取值为0或1。 …

JavaScript双问号操作符(??)详解,解决使用 || 时因类型转换带来的问题

目录 JavaScript双问号操作符&#xff08;??&#xff09;详解&#xff0c;解决使用||时因类型转换带来的问题 一、双问号操作符??的基础用法 1、传统方式的痛点 2、双问号操作符??的精确判断 3、双问号操作符??与逻辑或操作符||的对比 二、复杂场景下的空值处理 …

蓝桥杯 web 展开你的扇子(css3)

普通答案&#xff1a; #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); }…

聚焦楼宇自控:优化建筑性能,引领智能化管控与舒适环境

在当今建筑行业蓬勃发展的浪潮中&#xff0c;人们对建筑的要求早已超越了传统的遮风避雨功能&#xff0c;而是更加注重建筑性能的优化、智能化的管控以及舒适环境的营造。楼宇自控系统作为现代建筑技术的核心力量&#xff0c;正凭借其卓越的功能和先进的技术&#xff0c;在这几…

Ubuntu16.04配置远程连接

配置静态IP Ubuntu16.04 修改超管账户默认密码 # 修改root账户默认密码 sudo passwd Ubuntu16.04安装SSH # 安装ssh服务&#xff1a; sudo apt-get install ssh# 启动SSH服务&#xff1a; sudo /etc/init.d/ssh start # 开机自启 sudo systemctl enable ssh# 如无法连接&…

基于springboot微信小程序课堂签到及提问系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 随着信息时代的来临&#xff0c;过去的课堂签到及提问管理方式的缺点逐渐暴露&#xff0c;本次对过去的课堂签到及提问管理方式的缺点进行分析&#xff0c;采取计算机方式构建基于微信小程序的课堂签到及提问系统。本文通过阅读相关文献&#xff0c;研究国内外相关技术&a…

互联网三高-高性能之JVM调优

1 运行时数据区 JVM运行时数据区是Java虚拟机管理的内存核心模块&#xff0c;主要分为线程共享和线程私有两部分。 &#xff08;1&#xff09;线程私有 ① 程序计数器&#xff1a;存储当前线程执行字节码指令的地址&#xff0c;用于分支、循环、异常处理等流程控制‌ ② 虚拟机…