ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Qt5 QtChart二维图表开发实战:从工程配置到实时曲线封装方案

Qt5 QtChart二维图表开发实战:从工程配置到实时曲线封装方案

在 Qt5 里做二维图表,我最早是用 QPainter 自己画网格、画刻度的,数据稍微多一点、需求稍微改一版,我就想把之前的代码全推翻。后来切到 QtChart,才意识到很多重复劳动官方早就帮你做好了。这篇想聊的是我基于 Qt5 + QtChart 绘制二维图表的完整实践:从工程配置、核心类拆解,到折线图/柱状图/饼图/实时刷新曲线的可运行代码,再到实际项目里踩过的坑,以及一套轻量封装方案。适合刚上手 Qt 的初学者,也适合正在用 QPainter 手绘图表的界面开发者。

1. 为什么二维图表选 QtChart,而不是 QPainter 手绘

1.1 先聊聊市面上几个方案的区别

很多人刚开始接触 Qt 绘图时,第一反应是用 QPainter 画坐标轴、画网格线、再画数据曲线。这个方案不是不能做,而是维护成本太高。坐标轴的刻度怎么算、标签怎么放、图例怎么画、鼠标悬停显示数值怎么做、缩放后刻度怎么重排,这些问题全都要自己解决。我第一版监控界面就是这么写的,后来要加曲线颜色、加图例、加双坐标轴,光是坐标换算的代码就写了一百多行,最后还是拆掉重写了。

除了手绘,常见选择还有 Qwt 和 QCustomPlot。Qwt 是老牌库,功能确实全,但风格偏老,Qt 高版本适配也需要额外配置,新人上手容易被编译问题劝退。QCustomPlot 轻量、性能好、文档实例多,适合对图表有重度定制需求的项目,但它是第三方库,升级维护和许可证都是需要考虑的因素。QtChart 是官方模块,和 QWidget、QSS、事件体系天然一致,配置起来最省心。

我用一张表总结这几种方案的差别。

方案上手成本图表交互维护风险适合场景
QPainter 手绘高基本没有完全自己维护画个简单示意图
Qwt中高较强第三方,更新慢老项目、专业工业图
QCustomPlot中很强第三方,依赖作者更新海量数据、重度定制
QtChart低够用官方维护,随 Qt 升级监控曲线、统计报表

1.2 什么场景值得用它

QtChart 最适合的场景是“二维图表”里那 90% 的常规需求:实时监控曲线、性能走势、统计柱状图、占比饼图、散点分布。官方把坐标轴、图例、主题、动画、缩放这些底层能力都封装好了,你只需要把数据填进去,图表就能正常展示。

它的边界也要认清:QtChart 不是万能的。它本身没有 3D 图表能力,复杂 K 线图需要自己用 QLineSeries 或者自定义绘制去拼,海量数据点的渲染性能也不如专门做高性能绘图的第三方库。如果项目明确需要上千个点、频繁全量刷新,建议先做原型测试,再决定是不是要换方案。至少在我做过的上位机和数据看板里,QtChart 完全够用。

2. Qt5 工程里把 charts 模块挂进来的正确姿势

2.1 qmake 和 CMake 两种写法

Qt Charts 从 Qt 5.7 开始成为官方模块,开源版也能直接用。用 qmake 的话,只需要在.pro文件里加一行charts:

QT += core gui charts greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = ChartDemo TEMPLATE = app SOURCES += main.cpp HEADERS += chartwidget.h

注意这里的顺序不影响结果,但charts一定要写,不写的话编译时头文件能找到,链接阶段却会报一堆 undefined reference。这个错我见过不少新手踩。

如果是 CMake 工程,需要显式查找 Charts 组件并链接。Qt5 里 Charts 的 CMake 包名是Qt5Charts,所以最稳妥的写法是:

find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets) find_package(Qt5Charts REQUIRED) add_executable(ChartDemo main.cpp chartwidget.cpp) target_link_libraries(ChartDemo PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Charts )

如果你用的在线安装器安装 Qt,记得勾选 Charts 模块,这个模块体积不大,但是默认安装不一定勾选。Linux 下如果用发行版仓库安装 Qt,也需要单独安装对应的 Qt Charts 开发包。

2.2 跑通一个最小窗口

先别急着往上堆业务,先把一个能跑的最小窗口搭起来,验证环境没问题。下面这段代码是我每次都用来测环境的模板:

#include <QApplication> #include <QtCharts/QChartView> #include <QtCharts/QLineSeries> #include <QtCharts/QValueAxis> using namespace QtCharts; int main(int argc, char *argv[]) { QApplication app(argc, argv); QLineSeries *series = new QLineSeries(); series->append(0, 6); series->append(2, 4); series->append(3, 8); series->append(7, 4); series->append(10, 7); QChart *chart = new QChart(); chart->addSeries(series); chart->legend()->hide(); chart->createDefaultAxes(); chart->setTitle(QStringLiteral("最简单的折线图")); chart->setAnimationOptions(QChart::SeriesAnimations); QChartView *chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); chartView->resize(800, 500); chartView->show(); return app.exec(); }

这里有个关键点:createDefaultAxes()必须放在addSeries(series)之后调用,而且系列里要有数据。因为 QtChart 会根据已有数据范围来创建坐标轴,如果数据是空的,轴就不会有合理的范围。这个顺序写反了,你会在界面上看到一条“看不见”的曲线。

setRenderHint(QPainter::Antialiasing)也很重要,主要为了曲线边缘平滑。QChartView 本身继承自 QGraphicsView,所以它可以直接作为窗口显示,也可以放在任何布局里。动画我一般只在原型阶段开,正式跑大量数据的时候会关掉。

3. 拆解核心类:QChart/QChartView/轴/系列,搞懂再写代码

3.1 QChart 和 QChartView 的分工

很多新手刚接触 QtChart 时会搞不清 QChart 和 QChartView 的区别,经常把两个类混着用。打个比方,QChart 更像是画布本身,它负责管理系列、坐标轴、图例、主题这些图表内容;QChartView 则是一块嵌在窗口里的画框,负责把画布显示出来,同时接收鼠标键盘事件。

把图表放进界面的标准方式是:

QChartView *chartView = new QChartView(chart); ui->layout->addWidget(chartView);

QChart 本身是 QGraphicsWidget,不是普通 QWidget,所以一般不会直接把 QChart 塞进 QWidget 布局里,而是通过 QChartView 来承载。如果你需要把图表嵌入一个已有场景中,也可以把 QChart 添加到 QGraphicsScene 里,但大多数场景直接用 QChartView 就够了。

3.2 坐标轴不要总是用默认的

createDefaultAxes()虽然方便,但很多时候满足不了需求。至少在我做的项目里,最终都会换成手动创建坐标轴,因为默认轴的范围、刻度数量、标签格式经常不符合业务要求。

QtChart 提供三种常用轴:

  • QValueAxis:数值轴,适合连续数值。
  • QBarCategoryAxis:分类轴,适合柱状图、离散分类。
  • QDateTimeAxis:时间轴,适合按时间显示的数据。

手动添加轴的写法是:

QValueAxis *axisX = new QValueAxis(); axisX->setRange(0, 100); axisX->setTitleText(QStringLiteral("时间")); axisX->setLabelFormat("%d"); QValueAxis *axisY = new QValueAxis(); axisY->setRange(0, 100); axisY->setTitleText(QStringLiteral("数值")); chart->addAxis(axisX, Qt::AlignBottom); chart->addAxis(axisY, Qt::AlignLeft); series->attachAxis(axisX); series->attachAxis(axisY);

这里要强调的是:addAxis只是把轴放到图表的指定位置,它并不会自动关联到某个系列。必须再调series->attachAxis(axisX),轴和系列才会建立关系。很多初学者只加了轴,忘记 attach,结果图表上显示不出数据。多系列共用同一个 Y 轴时,每个系列都要调一次 attachAxis。

3.3 图例、主题、动画这些“看起来小”的配置

图例默认是显示在图表右侧的。有的场景不需要图例,可以chart->legend()->hide()。需要显示的话,可以调整位置:

chart->legend()->setVisible(true); chart->legend()->setAlignment(Qt::AlignBottom); chart->legend()->setLabelColor(Qt::darkGray);

主题很实用。QtChart 内置了几个主题,切深色主题做夜间看板很方便:

chart->setTheme(QChart::ChartThemeDark);

动画方面,QChart::SeriesAnimations会在数据变化时做过渡动画,看起来效果不错,但数据刷新频率高的时候会拖累性能。实时曲线建议关掉,或者只在初始加载时开一次。

4. 四种常用二维图表的落地代码:折线、柱状、饼图、实时曲线

4.1 折线图:多个系列共用或独立坐标轴

折线图是 QtChart 里最常用的图,也是我先讲的原因。单系列折线很简单,多系列折线只需要创建多个QLineSeries,统一加到 chart 上。多组数据数值范围不同的时候,需要拆分左右两个 Y 轴。

示例场景是监控页同时展示“CPU 温度”和“内存占用”,温度在 0 到 100,内存占用也在 0 到 100,但它们不是同一个量纲,仪表盘习惯上分左右轴显示:

QLIneSeries *tempSeries = new QLineSeries(); tempSeries->setName(QStringLiteral("温度")); QLIneSeries *memSeries = new QLineSeries(); memSeries->setName(QStringLiteral("内存")); for (int i = 0; i < 50; ++i) { tempSeries->append(i, 45 + 20 * qSin(i / 5.0)); memSeries->append(i, 30 + 15 * qCos(i / 6.0)); } chart->addSeries(tempSeries); chart->addSeries(memSeries); QValueAxis *axisX = new QValueAxis(); axisX->setRange(0, 50); chart->addAxis(axisX, Qt::AlignBottom); tempSeries->attachAxis(axisX); memSeries->attachAxis(axisX); QValueAxis *axisY = new QValueAxis(); axisY->setRange(0, 100); chart->addAxis(axisY, Qt::AlignLeft); tempSeries->attachAxis(axisY); QValueAxis *axisYRight = new QValueAxis(); axisYRight->setRange(0, 100); chart->addAxis(axisYRight, Qt::AlignRight); memSeries->attachAxis(axisYRight);

双轴的核心逻辑很简单:每个系列需要知道自己挂在哪个轴上,轴可以复用,也可以各用各的。这里最容易犯的错是忘记把 axisX 也 attach 给 memSeries,结果只有温度曲线有 X 轴,内存曲线画不出来。

4.2 柱状图:统计类需求别用折线硬凹

柱状图是统计报表里另一类高频需求。QtChart 用QBarSeries+QBarSet实现,QBarSet代表一组柱子。

QBarSet *setA = new QBarSet(QStringLiteral("一季度")); QBarSet *setB = new QBarSet(QStringLiteral("二季度")); *setA << 12 << 18 << 15 << 22; *setB << 8 << 15 << 20 << 17; QBarSeries *barSeries = new QBarSeries(); barSeries->append(setA); barSeries->append(setB); QChart *chart = new QChart(); chart->addSeries(barSeries); chart->setTitle(QStringLiteral("销售统计")); QBarCategoryAxis *axisX = new QBarCategoryAxis(); axisX->append(QStringList() << QStringLiteral("一月") << QStringLiteral("二月") << QStringLiteral("三月") << QStringLiteral("四月")); chart->addAxis(axisX, Qt::AlignBottom); barSeries->attachAxis(axisX); QValueAxis *axisY = new QValueAxis(); axisY->setRange(0, 30); chart->addAxis(axisY, Qt::AlignLeft); barSeries->attachAxis(axisY);

柱状图的 X 轴基本都配QBarCategoryAxis,Y 轴用QValueAxis。这里要注意,添加分类轴时给的是字符串列表,顺序就是柱子在图表上的顺序。setRange(0, 30)的 30 要大于数据最大值 22,不然最上面一根柱子会顶到边界。柱子宽度可以用barSeries->setBarWidth(0.5)调整,默认 0.5 左右,看起来比较舒服。

4.3 饼图:少分类占比,不建议超过 6 块

饼图用QPieSeries,它不需要坐标轴,直接加序列就能显示。

QPieSeries *pieSeries = new QPieSeries(); pieSeries->append(QStringLiteral("已完成"), 80); pieSeries->append(QStringLiteral("进行中"), 15); pieSeries->append(QStringLiteral("未开始"), 5); // 把第一块分离出来 QPieSlice *slice = pieSeries->slices().at(0); slice->setLabelVisible(true); slice->setExploded(true); slice->setBrush(QColor("#5470c6")); QChart *chart = new QChart(); chart->addSeries(pieSeries); chart->setTitle(QStringLiteral("任务进度"));

我一般会把第一块设置为分离状态,这样饼图重点更突出。要让标签显示百分比,可以设置:

slice->setLabel(QStringLiteral("已完成 80%"));

或者依赖默认标签。饼图适合分类少、占比清晰的场景,分类超过六七个以后会变得很挤,不如换柱状图。

4.4 实时刷新曲线:监控界面的性能要点

监控界面最常见的需求是曲线每隔一段时间更新一个点。很多人的第一版代码是这样写的:每次拿series->append()追加一个新点,然后就完了。数据少的时候没问题,但跑上一小时,series 里会积累几万个点,界面越来越卡。

我的建议是固定一个显示窗口大小,超过上限后整批替换数据,不要逐点 remove。下面是一个用QTimer+QLineSeries::replace的滚动曲线写法:

QLineSeries *series = new QLineSeries(); QVector<QPointF> buffer; const int maxPoints = 300; QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, [=]() { static double x = 0.0; x += 0.1; double y = qSin(x) + 0.2 * qrand() / double(RAND_MAX); buffer.append(QPointF(x, y)); if (buffer.size() > maxPoints) { buffer.remove(0, buffer.size() - maxPoints); } series->replace(buffer); }); timer->start(100);

replace(buffer)是用一整份新的点集替换旧的,比反复append + remove的性能好很多。这里有个更进一步的门道:当 X 轴范围固定时,可以用axisX->setRange(x - maxRange, x),让曲线始终停留在可视窗口内,而不是随着 X 值一直往右拉。监控看板几乎都会用到这个小技巧。

4.5 散点图和面积图,顺手就一起做了

散点图用QScatterSeries,和折线图的用法几乎一样,区别是它不连线,可以控制点的大小和形状。

QScatterSeries *scatter = new QScatterSeries(); scatter->setName(QStringLiteral("样本")); scatter->setMarkerSize(10.0); scatter->setBorderColor(QColor("#ee6666")); scatter->setBrush(QColor(238, 102, 102, 80)); for (int i = 0; i < 30; ++i) { scatter->append(rand() % 100, rand() % 100); } chart->addSeries(scatter); chart->createDefaultAxes();

面积图需要两条边界线,一条上边界、一条下边界,然后合成QAreaSeries:

QLineSeries *upper = new QLineSeries(); QLineSeries *lower = new QLineSeries(); // 填充上下两条线的数据... QAreaSeries *area = new QAreaSeries(upper, lower); area->setName(QStringLiteral("面积")); area->setBrush(QColor(84, 112, 198, 60)); chart->addSeries(area);

面积图需要注意透明度,颜色最好半透明,不然下层数据全被遮完了。

5. 交互与视觉细节:坐标显示、缩放框选、导出图片

5.1 鼠标悬停和点击显示数值

二维图表只有“画出来”是不够的,用户需要知道某个数据点具体是多少。QLineSeries和QScatterSeries都继承了QXYSeries,它提供了hovered和clicked信号。

我常用 hovered 信号做一个状态栏坐标显示:

connect(series, &QXYSeries::hovered, this, [this](const QPointF &point, bool state) { if (state) { ui->statusLabel->setText( QStringLiteral("x = %1, y = %2") .arg(point.x()) .arg(point.y())); } });

如果想要点击某个点弹出详情,用clicked信号:

connect(series, &QXYSeries::clicked, this, [this](const QPointF &point) { QMessageBox::information( this, QStringLiteral("数据点"), QStringLiteral("x = %1\ny = %2") .arg(point.x()) .arg(point.y())); });

如果需要在图表空白区域显示十字线或者 tooltip,仅仅靠 series 的信号不够,需要响应 QChartView 的鼠标事件,然后通过chart->mapToValue()把视图坐标换算成图表数据坐标。这个属于进阶定制,二维图表的常规项目里不一定会用到,但知道有这层映射关系,后面遇到不会懵。

5.2 框选放大和滚轮缩放

QChartView 本身带了橡皮筋框选放大能力,只要设置一下:

chartView->setRubberBand(QChartView::RectangleRubberBand);

这样鼠标左键拖一个矩形框,图表就会放大到框选区域。想要恢复原始大小,调用:

chart->zoomReset();

如果希望鼠标滚轮直接缩放,需要继承 QChartView 重写wheelEvent。我试过最简单的方法:

void MyChartView::wheelEvent(QWheelEvent *event) { if (event->angleDelta().y() > 0) { m_chart->zoomIn(); } else { m_chart->zoomOut(); } event->accept(); }

这里有个踩坑点:滚轮缩放默认围绕鼠标当前位置进行,但 QtChart 的 zoomIn 是按比例放到中心点。想让缩放中心跟随鼠标,要用chart->zoomIn()配合mapToValue换算,逻辑上稍微绕一点。一般情况下默认缩放中心也够用。

5.3 导出图片:一个 grab 就搞定

把图表导出成图片,QtChart 的体验确实好。既然 QChartView 是 QWidget 体系里的控件,直接这样拿图像:

QPixmap pixmap = chartView->grab(); pixmap.save(QStringLiteral("chart.png"));

如果要导出透明背景的图片,需要在保存前让图表背景透明:

chart->setBackgroundVisible(false);

如果图表带着白色默认背景,导出的 PNG 就会有个白底。做汇报截图、做报表导出时,透明背景往往更讨喜。

6. 数据量大就卡、中文乱码?我把踩过的坑整理成排查表

6.1 数据量一大就卡顿,先砍这三个开关

实时曲线的卡顿排查,我一般按优先级砍三轮。

第一,关动画。设置QChart::NoAnimation,动画在实时刷新场景里基本没有意义。

第二,关抗锯齿。chartView->setRenderHint(QPainter::Antialiasing, false)能提升一点绘制速度,代价是曲线边缘有锯齿。如果机器性能一般,先用这个换流畅度。

第三,控制数据点数量。任何图表都不可能无限制显示几百万个点。客户端数据显示讲究“聚焦有效信息”,超过窗口范围的点就该被删除或者做抽稀。上节用replace控制maxPoints就是这个目的。

如果数据量非常大,QtChart 还提供了 OpenGL 加速,以QLineSeries为例可以开启series->setUseOpenGL(true),不过它会限制一些交互和绘制能力。真正跑到几十万点、需要频繁刷新的场景,我建议直接评估 QCustomPlot。

现象可能原因处理方案
每秒刷新后越来越卡series 点数无限增长固定点数,用 replace 截断
缩放时卡顿明显动画仍然开启关闭动画
曲线锯齿明显但不想开抗锯齿绘制量太大减少数据点或开启 OpenGL
多个系列同时刷新,界面卡死每个系列各刷各的合并数据更新,减少 paint 次数

6.2 坐标轴刻度“乱跳”或显示不下

坐标轴刻度问题几乎是图表需求里最常被提的。默认轴的刻度数量是自动算的,碰到小数或者特别大的数字,标签经常不忍直视。最常见的处理是手动设置刻度:

axisX->setTickCount(10); axisX->setMinorTickCount(5); axisX->setLabelFormat("%.1f"); axisY->setRange(0, 100); axisY->applyNiceNumbers();

applyNiceNumbers()是我特别推荐的一个函数,它会自动把 Y 轴的刻度修整成“漂亮”的整数,比如 0、20、40、60,而不是 0、17.3、34.6 这种。做数据看板时,美观度提升非常明显。

如果 X 轴分类标签太长显示不下,可以考虑旋转标签:

axisX->setLabelsAngle(-45);

或者用QBarCategoryAxis的标签换行。这个细节在柱状图里很常见,不处理的话文字会叠在一起。

6.3 中文乱码和坐标轴标题不显示

Qt5 以后默认 Unicode,正常不会乱码。如果你遇到中文乱码,先检查源文件编码是不是 UTF-8,然后字符串都用QStringLiteral包起来。不要自己用QTextCodec去瞎转,转着转着就乱了。

坐标轴标题不显示,常见原因是轴太短、字体太小,或者 title 默认不可见。可以先确认标题名:

axisX->setTitleText(QStringLiteral("时间")); axisX->setTitleVisible(true); axisX->setTitleFont(QFont(QStringLiteral("Microsoft YaHei"), 10));

注意轴标题默认会给一定的留白,如果图表区域太小,标题可能被挤到看不见。这种情况下要调大图表 margins 或者窗口高度。

6.4 常见编译和运行问题速查

这里把我踩过的一些问题整理成清单,照着排查能省不少时间。

  • 编译过了,但运行崩溃:查看series是否在栈上创建,然后又被 chart 接管;建议统一用 new 创建。
  • createDefaultAxes()之后自定义轴不生效:createDefaultAxes()会创建默认轴并存到 chart 里,如果你又手动addAxis同一个方向,需要先把默认轴移除,或者干脆别用createDefaultAxes()。
  • 找不到QChart名称:记得#include <QtCharts/QChart>,并且using namespace QtCharts;或QT_CHARTS_USE_NAMESPACE。
  • CMake 找不到 Charts:确认安装了 Qt Charts 模块,CMake 用find_package(Qt5Charts REQUIRED)。
  • 删除 series 后 crash:先chart->removeSeries(series),再delete series,不要直接 delete 一个还在 chart 里的对象。

7. 项目里别裸写:一个轻量 ChartWidget 封装思路

7.1 为什么我建议做封装

如果你只是写一个演示程序,直接在界面里 new 一个 QChartView 没有任何问题。但项目里图表通常会在多个页面里重复出现,而且业务方今天要加一条曲线、明天要改一个颜色,每次都在业务代码里到处改 chart 操作,维护成本会迅速膨胀。我建议把图表的创建、坐标轴配置、数据更新、导出、清空这些动作收敛到一个类里。

封装还有一个隐藏好处:如果以后真要把 QtChart 换成 QCustomPlot,业务层只需要面对同一个接口,内部实现改掉就行,界面代码不用动。这个解耦价值在中期项目里会体现得非常明显。

7.2 接口长这样

我常用的一套接口设计是这样的:

class ChartWidget : public QWidget { Q_OBJECT public: explicit ChartWidget(QWidget *parent = nullptr); void addLineSeries(const QString &name, const QColor &color); void updateLineData(const QString &name, const QVector<QPointF> &points); void appendLinePoint(const QString &name, const QPointF &point); void clearAllSeries(); void setChartTitle(const QString &title); void exportChart(const QString &filePath); private: QChartView *m_chartView = nullptr; QChart *m_chart = nullptr; QValueAxis *m_axisX = nullptr; QValueAxis *m_axisY = nullptr; QHash<QString, QLineSeries *> m_seriesMap; QList<QColor> m_colorPalette; };

对外暴露的不是 QLineSeries 指针,而是曲线名字。业务层传数据是:

chartWidget->updateLineData(QStringLiteral("CPU"), points);

不用关心内部是 QLineSeries 还是 QScatterSeries,也不用关心坐标轴怎么配。这个设计在多人协作项目里特别有用。

7.3 实现细节和自动配色

在构造函数里,我会统一创建 chart、chartView、坐标轴,并设置好主题和抗锯齿。addLineSeries负责从调色板取色、创建 series、写入m_seriesMap,并且 attach 到公共轴。

自动配色用一个固定颜色表循环即可:

m_colorPalette << QColor("#5470c6") << QColor("#91cc75") << QColor("#fac858") << QColor("#ee6666") << QColor("#73c0de");

取色逻辑可以简单一点:

int index = m_seriesMap.size() % m_colorPalette.size(); QColor color = m_colorPalette.at(index); series->setColor(color);

updateLineData的核心就是找到对应 series,然后调replace(points)。如果 series 不存在,可以自动调用addLineSeries,这样业务方在初始化时少写一个流程。

clearAllSeries要清空数据但保留坐标轴:

for (QLineSeries *series : qAsConst(m_seriesMap)) { series->clear(); }

不要每次都把 axis 也删了,因为坐标轴配置通常是稳定的。

这段封装我在两个项目里改过三版,最大的体会是:第一次写图表代码时不要把图表对象散落在业务类里,否则后期每个界面的轴配置都会翻车。把“数据更新”作为唯一入口,内部负责加轴、配色、清理,这是最省心的做法。QtChart 本身不是万能的,真到了海量数据或者复杂交互的时候,该换 QCustomPlot 也不要硬撑。但从二维监控图和统计报表这个范围来看,先用它把业务跑通,性价比非常高。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进