当前位置: 首页 > news >正文

别再只会画折线图了!用Qt Charts搞定柱状图、饼图、散点图(附完整C++源码)

Qt Charts全栈实战:5种数据可视化图表从原理到封装

在数据驱动的时代,如何将枯燥的数字转化为直观的视觉呈现,是每个开发者都需要掌握的技能。Qt Charts作为Qt官方提供的图表模块,凭借其跨平台特性和丰富的API,成为C++开发者实现数据可视化的首选方案。但很多开发者往往止步于简单的折线图应用,未能充分发挥其潜力。

1. 数据可视化基础架构设计

1.1 统一数据模型构建

高效的数据可视化始于合理的数据结构设计。我们推荐使用QStandardItemModel作为基础数据容器,它比TreeWidget更具灵活性:

QStandardItemModel *model = new QStandardItemModel(this); model->setColumnCount(5); model->setHeaderData(0, Qt::Horizontal, "ID"); model->setHeaderData(1, Qt::Horizontal, "数学"); model->setHeaderData(2, Qt::Horizontal, "语文"); model->setHeaderData(3, Qt::Horizontal, "英语"); model->setHeaderData(4, Qt::Horizontal, "平均分"); // 示例数据填充 for(int i=0; i<10; ++i) { model->setItem(i, 0, new QStandardItem(QString::number(1000+i))); model->setItem(i, 1, new QStandardItem(QString::number(70+qrand()%30))); model->setItem(i, 2, new QStandardItem(QString::number(60+qrand()%35))); model->setItem(i, 3, new QStandardItem(QString::number(65+qrand()%30))); model->setItem(i, 4, new QStandardItem( QString::number((model->item(i,1)->text().toInt() + model->item(i,2)->text().toInt() + model->item(i,3)->text().toInt())/3.0, 'f', 1))); }

1.2 图表视图封装策略

创建可复用的ChartView组件,避免重复初始化代码:

class ChartView : public QChartView { public: explicit ChartView(QWidget *parent = nullptr) : QChartView(new QChart(), parent) { chart()->setAnimationOptions(QChart::SeriesAnimations); setRenderHint(QPainter::Antialiasing); } void addSeries(QAbstractSeries *series) { chart()->addSeries(series); chart()->createDefaultAxes(); } void clear() { chart()->removeAllSeries(); auto axes = chart()->axes(); for(auto axis : axes) { chart()->removeAxis(axis); delete axis; } } };

2. 对比分析:柱状图深度应用

2.1 基础柱状图实现

柱状图最适合展示离散数据的对比关系。Qt Charts通过QBarSeries、QBarSet和QBarCategoryAxis三个核心类实现:

void createBarChart(QAbstractItemModel *model, ChartView *view) { view->clear(); QBarSeries *series = new QBarSeries(); QStringList categories; // 创建数据集 QBarSet *mathSet = new QBarSet(model->headerData(1, Qt::Horizontal).toString()); QBarSet *chineseSet = new QBarSet(model->headerData(2, Qt::Horizontal).toString()); QBarSet *englishSet = new QBarSet(model->headerData(3, Qt::Horizontal).toString()); // 填充数据 for(int row=0; row<model->rowCount(); ++row) { mathSet->append(model->index(row, 1).data().toInt()); chineseSet->append(model->index(row, 2).data().toInt()); englishSet->append(model->index(row, 3).data().toInt()); categories << model->index(row, 0).data().toString(); } series->append(mathSet); series->append(chineseSet); series->append(englishSet); view->addSeries(series); // 自定义X轴 QBarCategoryAxis *axisX = new QBarCategoryAxis(); axisX->append(categories); view->chart()->setAxisX(axisX, series); // 自定义Y轴 QValueAxis *axisY = qobject_cast<QValueAxis*>(view->chart()->axisY()); axisY->setTitleText("分数"); axisY->setRange(0, 100); }

2.2 高级柱状图变体

堆叠柱状图展示部分与整体的关系,只需将QBarSeries替换为QStackedBarSeries:

QStackedBarSeries *stackedSeries = new QStackedBarSeries(); stackedSeries->append(mathSet); stackedSeries->append(chineseSet); stackedSeries->append(englishSet); stackedSeries->setLabelsVisible(true); stackedSeries->setLabelsFormat("@value分");

百分比柱状图则使用QPercentBarSeries,自动将数值转换为百分比:

QPercentBarSeries *percentSeries = new QPercentBarSeries(); percentSeries->append(mathSet); percentSeries->append(chineseSet); percentSeries->append(englishSet); percentSeries->setLabelsVisible(true); percentSeries->setLabelsFormat("@value%");

3. 比例呈现:饼图的艺术

3.1 基础饼图实现

饼图通过QPieSeries和QPieSlice展示各部分占比:

void createPieChart(QAbstractItemModel *model, ChartView *view, int row) { view->clear(); QPieSeries *series = new QPieSeries(); series->setHoleSize(0.35); // 创建环形图效果 // 添加学科数据 for(int col=1; col<=3; ++col) { QString label = model->headerData(col, Qt::Horizontal).toString(); qreal value = model->index(row, col).data().toDouble(); QPieSlice *slice = series->append(label, value); // 动态设置颜色 static QList<QColor> colors = {Qt::blue, Qt::green, Qt::red}; slice->setBrush(colors.at(col-1)); slice->setLabelVisible(true); } view->addSeries(series); // 突出显示最大部分 QPieSlice *maxSlice = series->slices().at(0); for(QPieSlice *slice : series->slices()) { if(slice->value() > maxSlice->value()) maxSlice = slice; } maxSlice->setExploded(true); }

3.2 交互增强技巧

通过信号槽实现鼠标悬停高亮效果:

// 在创建series后添加 for(QPieSlice *slice : series->slices()) { connect(slice, &QPieSlice::hovered, [=](bool state){ slice->setExploded(state); slice->setLabelVisible(state); }); }

4. 分布观察:散点图实战

4.1 基础散点图

QScatterSeries展示二维数据分布:

void createScatterChart(QAbstractItemModel *model, ChartView *view) { view->clear(); QScatterSeries *series = new QScatterSeries(); series->setName("成绩分布"); series->setMarkerShape(QScatterSeries::MarkerShapeCircle); series->setMarkerSize(15.0); // 数学vs英语成绩散点 for(int row=0; row<model->rowCount(); ++row) { qreal math = model->index(row, 1).data().toDouble(); qreal english = model->index(row, 3).data().toDouble(); series->append(math, english); } view->addSeries(series); view->chart()->setTitle("数学与英语成绩相关性分析"); // 坐标轴设置 QValueAxis *axisX = qobject_cast<QValueAxis*>(view->chart()->axisX()); axisX->setTitleText("数学成绩"); axisX->setRange(50, 100); QValueAxis *axisY = qobject_cast<QValueAxis*>(view->chart()->axisY()); axisY->setTitleText("英语成绩"); axisY->setRange(50, 100); }

4.2 趋势线叠加

结合QSplineSeries展示数据趋势:

// 在散点图代码后添加 QSplineSeries *trendLine = new QSplineSeries(); trendLine->setName("趋势线"); QPen pen(Qt::red); pen.setWidth(2); trendLine->setPen(pen); // 简单线性回归计算(示例) qreal sumX=0, sumY=0, sumXY=0, sumX2=0; int n = model->rowCount(); for(int row=0; row<n; ++row) { qreal x = model->index(row, 1).data().toDouble(); qreal y = model->index(row, 3).data().toDouble(); sumX += x; sumY += y; sumXY += x*y; sumX2 += x*x; } qreal slope = (n*sumXY - sumX*sumY)/(n*sumX2 - sumX*sumX); qreal intercept = (sumY - slope*sumX)/n; // 添加趋势线点 trendLine->append(50, slope*50 + intercept); trendLine->append(100, slope*100 + intercept); view->chart()->addSeries(trendLine); trendLine->attachAxis(axisX); trendLine->attachAxis(axisY);

5. 高级技巧与性能优化

5.1 动态数据更新

实现实时数据可视化:

// 定时更新示例 QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, [=](){ static int row = 0; if(++row >= model->rowCount()) row = 0; // 随机更新数据 for(int col=1; col<=3; ++col) { qreal newValue = 50 + qrand() % 50; model->setData(model->index(row, col), newValue); // 更新平均分 qreal avg = (model->index(row,1).data().toDouble() + model->index(row,2).data().toDouble() + model->index(row,3).data().toDouble()) / 3; model->setData(model->index(row,4), QString::number(avg, 'f', 1)); } // 重绘图表 updateCharts(); }); timer->start(1000);

5.2 大数据量优化

当数据点超过1000时,启用OpenGL加速:

// 在创建series后设置 if(model->rowCount() > 1000) { series->setUseOpenGL(true); qDebug() << "启用OpenGL加速"; }

5.3 主题与样式定制

统一应用专业配色方案:

void applyProfessionalTheme(QChart *chart) { // 内置主题选择 chart->setTheme(QChart::ChartThemeBlueIcy); // 或完全自定义 chart->setBackgroundBrush(QBrush(QColor("#f5f5f5"))); chart->setTitleBrush(QBrush(Qt::darkBlue)); QFont font; font.setPixelSize(12); chart->setFont(font); // 坐标轴样式 for(auto axis : chart->axes()) { axis->setGridLineColor(QColor("#e0e0e0")); axis->setLabelsFont(font); } }
http://www.jsqmd.com/news/736865/

相关文章:

  • 你的Dell G15还在“发烧“吗?这个开源工具3分钟解决散热烦恼
  • 2026年4月专业的滤芯厂家推荐,评价好的滤芯,专用滤芯,量身定制更贴心 - 品牌推荐师
  • PowerShell 第11章:过滤和比较(下)Where-Object、迭代命令行模型、$_作用域与实战练习
  • SAM2VideoX:基于特征蒸馏的结构保持视频生成技术
  • 高二鲜花
  • 金融级代码扫描落地实录:从零部署VSCode 2026内建SAST引擎,72小时通过ISO 27001金融专项认证(附审计日志模板)
  • 开源AI智能体编排平台Mission Control:轻量部署与生产级管理实践
  • Cat-Catch:浏览器资源嗅探与下载的完整解决方案
  • 构建可复现的开发环境:从点文件管理到一键部署
  • 如何解锁NVIDIA显卡隐藏性能:NVIDIA Profile Inspector完整配置指南
  • 别再为多相机标定头疼了!用VisionMaster统一坐标系的保姆级教程
  • 如何轻松实现微信聊天记录永久保存:WeChatMsg个人数据管理终极指南
  • BetterGI:3分钟配置终极自动化,让你的原神体验效率提升500%
  • 如何5分钟快速搭建PlantUML Server:新手入门教程
  • 朴素贝叶斯分类器
  • PlantUML Server核心功能解析:10大实用技巧与最佳实践
  • 解放双手的提瓦特冒险:BetterGI如何让原神日常任务变得轻松有趣
  • 如何在3分钟内为视频添加专业字幕:VideoSrt开源工具终极指南
  • OASIS快速入门指南:5分钟搭建你的第一个社交模拟环境
  • 配置openclaw智能体工作流使用taotoken作为统一模型供应商
  • leetcode:最小覆盖字符串
  • Notepad++正则表达式实战:如何快速筛选出同时包含两个关键词的日志行(附零基础详解)
  • DoL-Lyra整合包:5分钟快速上手的Degrees of Lewdity美化增强版
  • Instella-3B开源模型:轻量级LLM的性能突破与实践指南
  • 信奥赛CSP-J复赛集训(模拟算法专题)(20):[NOIP 2011 提高组] 铺地毯
  • B站缓存视频一键转换终极指南:m4s-converter完整使用教程
  • 碧蓝航线Alas脚本:5分钟快速上手指南,彻底解放你的双手
  • 原位修复的最优操作尺度:分子?蛋白质?细胞?还是组织?
  • 【Docker安全红皮书更新】:27版强制网络命名空间隔离、默认拒绝模式与自动微分段(仅限企业版Early Access)
  • 为什么92%的智能座舱项目在Docker 27升级后遭遇CAN总线延迟抖动?——车规级容器实时性调优白皮书首发