直接讲结论:饼状图这种东西,放在仪表盘、统计面板、定期的数据汇报里,永远是最直观的那个。哪怕前端圈子把ECharts用出花了,在纯桌面端、特别是基于C++的Qt项目里,QChart依然是绕不开的正统选择。这篇就以“用QChart画饼状图”这个项目为引子,把从环境配置到交互优化再到动态更新数据的完整链路捋一遍,代码都是可以直接抄进工程跑的,不只是演示那种demo,而是能真拿去用的写法。
用QChart做饼状图的优势非常明确:它是Qt官方维护的模块,基于Graphics View框架渲染,性能上跟Qt自身的事件循环、信号槽体系天然契合,这意味着你在一个已经用Qt写业务逻辑的项目里嵌图表,不用再引入一套Javascript运行时或者第三方控件库,维护成本低,也不存在跨语言桥接的脏活。再加上QChart的API本身是面向Qt开发者设计的,数据组织、样式调整、交互响应全部走的是那套熟悉的QObject信号槽模型,上手曲线比想象中平缓得多。
这篇内容没有太高门槛,只要你会Qt的基本控件操作,比如用过QLabel、QPushButton,大概理解信号槽是怎么回事,就够了。如果你曾经用别的方式画过饼图、想做一版更原生更可控的,或者是手头正有个报表项目不知道图表模块该从哪入手,这篇会是不错的参照。
1. 为什么是QChart而不是自绘或者第三方库
1.1 自绘饼状图的痛点和QChart解决了什么
早些年做桌面报表,很多人第一反应是拿QPainter自己画饼状图。毕竟饼状图就是一个圆、几段扇形、几个标签,看起来并不复杂。但当数据量上来、交互要求多起来,自绘的复杂度会迅速失控:鼠标点到某个扇区要变色,这个命中考算法要自己写;窗口拉伸后标签跟着动,这个布局计算要自己算;加了动画效果,每个时间帧的重绘逻辑还要自己维护。等你把这些都做完,大概率会发现这时间远不如当初多学一个模块来得划算。
QChart把这些脏活都给封装好了。它内部对扇区命中检测、标签布局、图例渲染、动画过渡都有了一套现成的机制,你要做的事就变成组织数据、设置样式、挂接信号,而不是从像素层面开始思考怎么画一个扇形。
1.2 QChart在Qt模块里的定位
QChart属于Qt Charts模块,这个模块在Qt 5.7之后被正式纳入Qt标准发行套件里,不需要额外付费。绝大多数通过在线安装器安装Qt的时候,默认就会包含这个模块。它负责统计图表的创建,包括折线图、柱状图、饼状图、散点图,还附带坐标轴、图例、主题色这些外围设施。
需要说明的是,QChart对于饼状图和对于其他带坐标轴的图表的实现思路略有不同。饼状图、环形图这类统计图不需要坐标轴,系列类型是QPieSeries;折线图、柱状图则要搭配QValueAxis或QBarCategoryAxis使用。理解了这一点,你后面查询文档的时候会清爽很多——很多问题只在某个系列类型上存在,不必在同一份报错信息里纠结半天。
1.3 选型带来的长期好处
在一个交给客户的工程里,依赖越稳越好。QChart是官方模块,API相对稳定,长期维护升级有保障,不会出现第三方控件库因为作者弃更导致整个项目都要返工的情况。而且它和Qt自身的资源管理、窗口系统协调得很好,比如在使用D3D或者OpenGL渲染的窗口里混入非原生控件,经常会出现闪烁或者层级异常,QChart不会给你整这种幺蛾子。
从团队协作的角度看,项目里新进的人只要会Qt,就能很快看懂QChart的代码,不用去学某个小圈子里的第三方图表的独家写法,这能省不少交接成本。
2. 项目起步:环境准备与最简结构
2.1 Qt Charts模块的引入
新建一个Qt工程之后,如果想要用QChart,第一步不是写代码,而是确认模块已经引入。如果用qmake,在工程文件的QT配置加上charts即可:
QT += charts如果是CMake,则在CMakeLists.txt里加上这样一句:
find_package(Qt6 REQUIRED COMPONENTS Charts) target_link_libraries(your_target PRIVATE Qt6::Charts)这里的一个隐蔽细节:有些Qt安装包是可裁剪的,如果安装的时候没有勾选Charts模块,即使加了上面的配置,编译时也会报找不到头文件的错,这种问题不属于代码层面,重装补上模块就行。
在代码中,包含头文件时需要注意命名空间。QChart相关的类统一在QtCharts命名空间里,一种惯用写法是在头文件和源文件里都显式引入:
#include <QtCharts/QPieSeries> #include <QtCharts/QChartView> #include <QtCharts/QChart>如果你的工程代码里大量使用图表相关类,可以直接在头文件后加一句:
QT_CHARTS_USE_NAMESPACE这个宏会把QtCharts命名空间里的常用类引入当前作用域,好处是代码里不用到处写QtCharts::的前缀,读起来干净很多。对于饼状图这种本来就简单的小模块,我个人推荐直接用这个宏。
2.2 QChart显示挂载的两种方式
QChart本身不是一个控件,它是数据与渲染逻辑的核心对象,需要依附于一个视图才能显示在界面上。最推荐的方式是使用QChartView,它继承自QGraphicsView,你在设计器或者代码里创建好它后,再通过chart()方法获取内部的QChart指针来做设置。
第二种方式是把QChart当作QGraphicsScene里的一项,放进自定义的QGraphicsView里。这个方式适合场景里还有其他自定义图形对象、需要统一管理点击和缩放逻辑的复杂项目。对饼状图这种简单场景,用QChartView就够了,代码量更少。
边框和背景也有讲究。默认情况下QChartView带着一个白底和细边框,在嵌入到业务面板时偶尔会显得突兀。我一般会在初始化时把它设置成透明背景加无边框,具体写法后面会提到。
2.3 最小运行工程的结构示例
下面给个可以直接跑的最简工程骨架,读完后你就能理解QChart的体系结构了。
首先是头文件,假设你新建了一个MainWindow类:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: void setupPieChart(); private: Ui::MainWindow *ui; }; #endif然后是源文件:
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QtCharts/QPieSeries> #include <QtCharts/QPieSlice> #include <QtCharts/QChartView> #include <QtCharts/QChart> QT_CHARTS_USE_NAMESPACE MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); setupPieChart(); } MainWindow::~MainWindow() { delete ui; } void MainWindow::setupPieChart() { // 1. 创建系列并添加数据 QPieSeries *series = new QPieSeries(); series->append("产品A", 320); series->append("产品B", 170); series->append("产品C", 210); series->append("产品D", 100); // 2. 创建图表对象 QChart *chart = new QChart(); chart->addSeries(series); chart->setTitle("本季度产品销售占比"); chart->setAnimationOptions(QChart::SeriesAnimations); // 3. 创建视图并挂载 QChartView *chartView = new QChartView(chart, this); chartView->setRenderHint(QPainter::Antialiasing); setCentralWidget(chartView); }这个工程跑起来,就能看到一个有四个扇区的基础饼状图,带标题,带图例,窗口大小变化时图表会自动居中缩放。
一个不建议跳过的细节是渲染抗锯齿。QChartView默认不开启抗锯齿,显示效果在低分辨率屏幕下会有明显锯齿感。如果不加这一句,饼状图的弧形边缘会感觉是“阶梯”的,模糊的,画面的高级感首先就垮了。抗锯齿的开销对于饼状图这种图元数量很少的场景几乎可以忽略,所以不用犹豫,直接开启。
3. 饼状图的核心实现细节与参数控制
3.1 数据组织的本质
QPieSeries的数据模型非常朴素:一系列“值”和“标签”的组合。内部维护一个QPieSlice列表,每添加一对“标签-数值”,就生成一个切片。
数值可以是整数也可以是小数值,切片占整个饼状图的比例由所有切片的总和来决定。比如你总共加了四个数值,值分别是320、170、210和100,那么总数为800,第一个切片占40%,画出来就是一个占比百分之四十的扇区。
这里要提一个在业务上经常被忽略的点:如果所有数值的总和为0,图表不会报错,但所有扇区会消失,界面上看着就是一个空白圆环,很容易被误解为程序跑挂了。所以在数据的前置处理阶段,最好做个判断,如果总和为零或者非常接近零,就显示一个数据为空的占位提示,而不是直接塞给图表。
3.2 标签显示的常用位置和格式
饼状图的标签默认是隐藏的,你想要在扇区外侧显示“产品A 40%”这种文本,需要手动设置。
一个典型的打开方式是给系列设置标签可见,同时指定标签位置:
series->setLabelsVisible(true); series->setLabelsPosition(QPieSlice::LabelOutside);LabelInside和LabelOutside是最常用的两个位置。LabelOutside会把标签放到扇区外侧,视觉上比较清爽,但当扇区较少、标签又长的时候,标签之间可能会重叠挤在一起,这个时候可以考虑切到LabelInside,或者把标签文本精简为“名称 百分比”。
标签的文本格式也可以调整。默认显示的文本是你在append时传入的标签字符串,加上一个百分比。如果自定义格式,注意这里不是直接给一个模板字符串那么简单,需要配合槽函数处理,或者预先在label里就把要显示的内容准备好。大多数场景下,与其和默认格式较劲,不如在添加切片时就传入已经拼好文本的完整标签。
3.3 颜色体系与视觉风格
QPieSlice默认会从一组内置色板中自动分配颜色。假如你要呈现的是公司品牌色或者固定的语义色,手动指定颜色是绕不开的。
设置颜色的方式有两个层级:一是QChart的全局主题,二是针对单个切片的画刷(brush)和画笔(pen)。
全局主题是这样的:
chart->setTheme(QChart::ChartThemeBlueNcs);Qt Charts内置了好几种主题,有浅色系、深色系,甚至有高对比色系。主题会影响图表的背景、系列颜色、文字颜色、图例颜色,调整这一行就能让整个图表风格大变样。适合那种不想逐个颜色调、快速出效果的时候用。
单个切片自定义颜色则是这样:
QPieSlice *slice = series->slices().at(0); slice->setBrush(QBrush(QColor("#4F81BD"))); slice->setPen(QPen(QColor("#4F81BD")));brush管的是扇区填充颜色,pen管的是扇区边框线条颜色。我习惯把两者设成同一个颜色值,这样扇区不会有突兀的细边线,整体图形看着是一个色块的连续体。如果你刻意要做出隔断效果,比如扇区与扇区之间有一条明显白边,那把pen设成白色或者背景色就行。
3.4 切片分离与元素布局
想让某一个切片从饼状图主体中“抽出来”,做成强调效果,这在QChart里是一行代码的事:
QPieSlice *slice = series->slices().at(0); slice->setExploded(true);配合切片分离还可以加入动画效果。QChart支持两种动画档位:QChart::SeriesAnimations和QChart::AllAnimations。SeriesAnimations会在数据变化时对系列本身的属性做插值动画,比如切片角度过渡,看着非常平滑。
从实际体验讲,饼状图的动画效果是性价比很高的投资。同样的数据展示,有了平滑的角度展开动画,用户的注意力会第一时间被图表的变化吸引,汇报演示的时候多个加分项。但它确实也会占用一点点CPU资源,对于配置很弱的办公电脑,如果页面上同时渲染多个饼状图,动画建议只保留一个,避免卡顿。
还有一个容易被忽略的布局参数:饼状图在视图窗口里的尺寸和位置。可以这样调整:
series->setPieSize(0.7); series->setHoleSize(0.3);setPieSize控制整个饼状图占图表绘制区域的比例,0.7意味着占了70%,剩下的空间留给标签和图例。setHoleSize设置的是内孔尺寸,当holeSize大于0时,饼状图就变成环形图/甜甜圈图,这是财务看板里常见的效果。两者配合使用,可以把饼状图的视觉重心完全控制在想要的区域里。
3.5 图例的样式定制
图例是饼状图信息完整性的重要组成部分。默认图例会显示在图表右侧,每项对应一个切片标签和一个颜色标识。以下代码可以调整它的位置到图表底部,并关闭图例的背景框:
chart->legend()->setAlignment(Qt::AlignBottom); chart->legend()->setBackgroundVisible(false); chart->legend()->setLabelColor(QColor("#333333"));如果标签文本里已经包含了百分比信息,图例里的文字再重复一次会显得多余。这种情况下可以在添加切片时,让图例文本保持简洁,仅保留名称部分,把完整的“名称 百分比”通过setLabelsVisible打开的外侧标签来呈现,这样信息的分配就清晰了。
4. 交互优化:用信号槽让饼状图“活”起来
4.1 切片悬停高亮与离开恢复
一个静态的饼状图只完成了视觉呈现的一半,交互反馈是另一半。QChart的QPieSlice是QObject的子类,它自带hovered信号,当鼠标进入或离开切片区域时触发,这就让高亮逻辑的实现变得非常简单。
先看效果目标:鼠标悬停在某个切片上时,该切片颜色不变,其余切片透明度明显降低,形成视觉聚焦;鼠标离开后,所有切片恢复原状。
实现思路是在设置完系列后,遍历所有切片,逐一连接信号:
for (QPieSlice *slice : series->slices()) { connect(slice, &QPieSlice::hovered, this, [=](bool state) { for (QPieSlice *s : series->slices()) { QColor baseColor = s->brush().color(); if (s == slice && state) { s->setOpacity(1.0); } else { s->setOpacity(state ? 0.3 : 1.0); } } }); }这段代码的要点在于,把所有切片放进循环中统一处理,而不是只处理当前悬停的切片。只处理当前切片达不到“突出主体”的效果,因为用户感知的对比是相对其他切片而言的。透明度从1.0降到0.3时,非焦点切片会显得像“退到背景里”,这种视觉层次是最容易做出专业感的方式。
还有一个细节:在设置透明度之后,有的Qt版本中需要调用chartView->update()或chart->update()触发重绘。在我的实测中,悬停信号触发时自动重绘的时机并不一致,稳妥起见,在槽函数末尾补上一句更新调用。
4.2 点击切片触发业务操作
切片点击事件在实际项目里更常用:点某个产品扇区,下方联动显示该产品的明细表;点某个省市扇区,地图联动缩放到对应区域。QChart虽然没有直接的“sliceClicked”命名,但QPieSlice本身可以接收鼠标事件并发出信号。
在Qt高版本中,QPieSlice没有直接暴露clicked信号,但可以通过重写视图事件或者在切片上安装事件过滤器来实现。更简单直接的方案是在QChartView子类里重写mousePressEvent,然后通过chart内的series和slice命中检测来判断点击了哪个切片。
命中检测方式很朴素:先把鼠标位置从视图坐标映射到图表场景坐标,再遍历所有切片的QPainterPath是否包含该坐标点。
void CustomChartView::mousePressEvent(QMouseEvent *event) { QPointF pos = chart()->mapToValue(event->pos(), series); for (QPieSlice *slice : series->slices()) { QPainterPath path = slice->slicePath(); if (path.contains(pos)) { emit sliceClicked(slice); break; } } QChartView::mousePressEvent(event); }slicePath()返回切片对应的图形路径,这个路径基于当前图表坐标。把鼠标坐标映射进系列坐标后,再与切片路径做包含判断,就可以准确得知用户点击了哪个扇区。注意需要先调用mapToValue把视图坐标转为图表内部的场景坐标,否则坐标基准不一致,判断永远不可能正确。
4.3 数据动态更新
饼状图很少是静态的,最常见的使用场景就是定时刷新或者响应数据源变化后更新整个图。
QPieSeries提供了clear()来清空所有切片,清空后重新append即可。一个典型的定时刷新流程如下:
void refreshPieChart() { // 模拟从数据库/接口拿到的数据 QList<QPair<QString, double>> data = fetchDataFromSomewhere(); series->clear(); for (const auto &item : data) { series->append(item.first, item.second); } }页面效果上,因为开启动画的缘故,新数据带来的角度变化会以平滑过渡的方式体现,体验上是“旧图旋转缩放变成新图”,不会生硬跳变。
但这里存在一个实际的坑:clear()会丢掉旧切片的样式设置。假如你在上一轮数据中手动设置了某个切片的颜色、爆炸效果,清空之后这些设置会全部消失。如果数据源每次刷新时系列结构是固定的,更推荐的做法是遍历已有切片逐个修改数值,而不是反复clear和append。
for (int i = 0; i < series->slices().size(); ++i) { series->slices().at(i)->setValue(newValues.at(i)); }这样保留了切片对象本身,样式、颜色、悬停信号连接都不会被破坏,更新成本也更低。
4.4 一个完整的交互型饼状图类封装实践
如果项目里需要多个模块复用同一个“可悬停、可点击、动态更新”的饼状图,建议封装成一个独立的Widget类,避免在每个页面重复写一遍信号连接。
我在实际项目里惯用的封装结构是提供两个对外接口:一个setData()负责从数据源填充数据,一个clearData()负责清空。同时对外暴露sliceClicked信号,让上层业务自己决定点击切片后做什么。封装的关键是把QChartView作为内部私有成员,对外暴露的是数据接口和信号,而不是QChart对象本身。
这样做的好处是明显的:上层调用者完全不需要理解图表实现细节,只要传入“标签-数值”的数据对,就能拿到一个可交互、带样式的饼状图。多个页面复用同一个封装的饼状图时,风格天然统一,后续升级配色方案、调整动画效果也只需要改一个文件。
5. 常见问题与坑点复盘
5.1 图表没有显示出来的排查顺序
这是遇到最多的求助,十次里有八次是代码结构问题而不是模块问题。默认情况下,QPieSeries创建后必须要addSeries到QChart上,QChart必须被放进QChartView里,而QChartView本身不能是局部变量——一旦函数结束,视图被销毁,图表自然就消失了。
排查顺序依次检查:QChartView是否有父对象、是否被加进了布局;series是否被加进了chart;chart是否被set到了chartView中。少任何一环都在界面上看不到任何内容。
另一个常见低级问题是加到布局时忘了把已有的占位图文控去掉。在设计器里已经放了一个QLabel,又在代码里addWidget一个新图表,出现了重叠,看起来像是“QChart有但显示不出来”,其实是图表被盖住了。
5.2 中文乱码和控制台输出警告
Qt 5后默认字符串编码是UTF-8,一般而言中文标题在代码里直接写字符串不会有问题。但如果你是在Windows上用MSVC编译器,源文件保存编码若不是UTF-8,就极易出现乱码。
对此的有效做法有两个:一是源文件统一保存为UTF-8编码,编译器选项带上/utf-8;二是把所有用户可见字符串统一放到资源文件里做翻译管理,不直接在代码里写中文。后者对后续国际化更友好。
某些控制台会打印如“QPainter::begin() ... Painter not active”之类的警告,这通常发生在widget尚未显示时就尝试绘制。处理方式是确保图表初始化在窗口show之后进行,或者使用QTimer::singleShot(0, ...)把初始化动作延后到事件循环启动后。
5.3 饼状图边缘锯齿和显示模糊
关闭抗锯齿的后果很多人都忽略,但真正影响观感的还有另一个因素:图表所在的窗口在高DPI缩放倍数下的渲染问题。如果你的程序声明了高分屏支持,而QChartView没有正确处理设备像素比,图表边缘会出现模糊。
Qt的解决办法一般是开启如下属性:
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);加上这两个开关后,QChart在高分屏上依然锐利。如果你的程序运行平台比较杂,建议在main函数里优先加上这两行,再创建窗口对象。
5.4 扇区数据值特别大或特别小时的表现
数据值的量级差异会导致极小的扇区在视觉上几乎不可见。比如一个总值一千万的饼状图里有一个值只有1的切片,它的角度只有零点几度,肉眼根本看不清,甚至点击都点不中。
这种情况下有几种处理策略:如果是业务上必须展示的切片,可以考虑做“最小角度保底”,把占比小的切片合并为一个“其他”分类;或者在标签信息里单独用文本框补充说明微小切片的数值,而不是强行要求用户在图形上去识别。
还有一种常见处理是给微小切片设置setExploded(true),把微小的扇区从饼图主体中抽离出来,视觉效果反而醒目很多。不过注意爆炸效果在极小角度下会有随机像素抖动感,抽出的距离要适度,太大会让扇区和饼图之间出现很难看的断裂。
5.5 内存管理与性能细节
QChart系列对象一般默认由QChart接管,QChart由QChartView接管。但你手动new出来的View如果设了父对象,父对象析构时会自动销毁它,一般不需要手动delete。需要注意的反而是在反复刷新时,避免每次都new一个新的QChart和QChartView。动态刷新场景下创建这两者的开销虽然微不足道,但如果旧对象没有正确释放,长时间运行会积累出内存增长,用任务管理器看内存占用会越来越离谱。
正确的动态刷新做法是:创建一次图表和视图,之后所有数据更新都走QPieSeries的接口,不清空视图结构,不销毁重建。这既节约CPU,也避免内存泄漏的可能性。
5.6 文件部署与相关依赖
还有一个容易被带到发布环节的问题:QChart引入了额外的插件和库文件,发布时如果没有打包相关组件,客户机器上运行时会提示缺少DLL或者无法加载样式。我建议打包时使用windeployqt工具,并且把Qt Charts的几个插件目录一并带上。额外保险的做法是发布后在干净机器上做一次完整运行测试,把缺失的组件补全。
6. 实际项目中的样式优化小技巧
6.1 四种配色方案换着用
同一套饼状图放在不同的业务皮肤里,配色可以提前准备几套。我通常在颜色管理上用一个QColorList的结构体封装起来,在setTheme时传入不同的方案,这样换肤时只要切换套装,不需要改动数据逻辑。
浅色背景下一套低饱和深色系,深色背景下一套高亮色系,这两个是最基础的需求。财务管理类页面偏爱沉稳的蓝绿色系,运营大屏则用高饱和橙红做强调。多准备几套色板是有好处的,万一产品哪天决定把页面换成暗色主题,你只需要切换颜色组即可。
6.2 把背景彻底融进页面
QChart默认有白底和浅灰色网格线,如果把它嵌进一个浅灰或深色的业务面板,白色框会很突兀。可以采用以下设置让图表透明化:
chart->setBackgroundVisible(false); chart->setPlotAreaBackgroundVisible(false);配合视图自身的透明背景设置,就能做出图表完全融入周边界面的效果。
开启动画后,数据刷新时旧扇区缩小、新扇区扩大的视觉效果看起来比较专业,但有一点要注意:如果数据集非常大,扇区数量超过几十个,动画过渡过程中的CPU占用会明显上升。大数据量的情况下建议关闭动画,用瞬时渲染替代,图表依然好使,只是少了流畅感。
6.3 颜色和标签适配深色主题
深色主题下,默认的文字颜色可能是深色,导致标签在深色背景下看不清。调整一下标签颜色和文字描边即可:
series->setLabelsColor(QColor("#DDDDDD"));深色背景下也不要让扇区颜色过深,因为扇区边缘可能和背景融为一体。给所有扇区加一个亮色细描边是比较稳妥的做法:
for (auto *slice : series->slices()) { slice->setBorderColor(QColor("#FFFFFF")); slice->setBorderWidth(1.5); }6.4 导出或截图时的细节
很多项目会加一个“导出报表”的功能,把图表区域保存成图片。QChartView支持直接抓取为图片,代码如下:
QPixmap pixmap = chartView->grab(); pixmap.save("report.png");有一个细节值得注意:grab()捕获的是当前视图的渲染状态,如果动画正在进行中,导出的图片可能是动画的半帧状态,角度看着很别扭。导出前最好先把动画临时关闭,等渲染稳定后再调用grab:
bool animEnabled = chart->isAnimated(); chart->setAnimationOptions(QChart::NoAnimation); QWheelEvent *dummy = nullptr; // 渲染一次稳定帧 QPixmap pixmap = chartView->grab(); chart->setAnimationOptions(animEnabled == true ? QChart::SeriesAnimations : QChart::NoAnimation);虽然这种情况在视觉上占比不大,但在交付客户演示文件时,导出图里出现一个卡在半中间的过渡角度就很不专业。
7. 从饼状图走通到复杂图表的迁移思路
7.1 QChart各系列之间的切换成本
一旦用QChart做完了饼状图,你其实已经掌握了Qt Charts模块大半的核心使用方式。柱状图无非是把QPieSeries换成QBarSeries,再添加上坐标轴对象;折线图则是引入QLineSeries和QValueAxis;散点图又类似。信号槽、视图挂载、主题设置、图例控制这些机制是完全相通的。
对一个常规的Qt开发任务而言,掌握饼状图和柱状图往往就覆盖了绝大多数数据看板类项目的图表需求。
7.2 多图表联动的设计思考
多个QChart放在同一个页面里时,涉及的资源开销会翻倍增长,但多数情况下一个页面上同时存在三五个图表是没问题的。需要注意交互一致性:如果采用自定义的悬停高亮交互,每个图表都要采用同一套信号连接逻辑,否则不同图表割裂的交互习惯会让用户感到混乱。
7.3 关于性能与未来的取舍
饼状图本身的数据量不大,几千个数据点对QChart来说毫无压力,性能瓶颈主要出现在同时渲染多个复杂图表并播放动画的时刻。此时可以考虑关闭动画、降低抗锯齿质量,极端情况下可以换成静态图片刷新。
QChart的API在Qt 5和Qt 6之间的差异很小,如果你当前工程还在Qt 5时代,不必担心未来升级到Qt 6会重写图表代码。
有一段话希望对你有帮助:QChart并没有想象中那么神秘,本质上它就是一个把绘图数据、视觉配置和交互逻辑组织起来的容器。你花一晚上掌握饼状图的使用,带来的熟练度会直接迁移到其他图表类型上。这也是我建议新手先从这个项目入手的原因——上手难度刚好,知识覆盖面又足够宽。
最后分享一个我个人的实操体会:第一版饼状图上线后,业务方一定会提出“某个颜色换成橙色”“某个扇区拉开一点”“标签不要重叠”这类需求。所以从一开始就尽量把标签可见性、切片颜色、爆炸状态这些高频调节项做成数据驱动的配置表,而不是写死在一堆代码赋值语句里。后面你会发现,报价单多做一列颜色配置,比你在代码里修改十处要省心得多。