ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Qt6安装配置Qt Charts实战:从组件安装到动态折线图实现

Qt6安装配置Qt Charts实战:从组件安装到动态折线图实现 很多朋友第一次在Qt6里写图表程序常有个错觉觉得Qt自带绘图功能折线图柱状图应该开箱即用。实际一搜教程才发现Qt Charts并不在Qt6的默认安装列表里直接#include QChartView编译必定报错。这篇就把从零把QtChart组件搞进Qt6工程并且真正画出第一张图的完整链路捋一遍包括组件安装、工程配置、核心API用法、动态更新数据以及我实际踩过的几个编译/运行坑希望能帮你少走弯路。1. 为什么Qt6一定要单独装QtChart模块Qt从5.x时代开始就把各类功能拆成了独立模块核心框架、控件、网络、数据库各归各的Qt Charts也不例外。到了Qt6这个拆分逻辑没变而且对模块间的版本一致性要求更高你在CMake里写了find_package(Qt6 COMPONENTS Charts)如果系统里压根没装这个模块CMake会直接报错连编译都进不去。Qt Charts到底能做什么简单说它提供了一整套基于Qt Graphics View Framework的图表绘制组件支持折线图、样条曲线、面积图、散点图、柱状图、饼图、极坐标图、箱线图等常见类型还带图例、坐标轴、动画交互这些现成能力。相比自己用QPainter一笔一笔画Qt Charts把大量底层工作封装好了你只需要关注数据和样式这在做上位机监控、数据采集分析、仪器仪表界面、教学演示程序时非常省事。需要说明的是Qt Charts在Qt6里不是默认随主框架一起安装的。用在线安装器qt-online-installer安装Qt6时组件树里有一层叫Additional Libraries里面才能找到Qt Charts这个勾选项。很多人第一次装Qt6觉得我全选安装了结果这一项还是漏了因为安装器默认的展开层级比较深不点开根本看不到。还有一种情况是公司内网、离线环境只能用离线安装包某些离线包里压根不带Qt Charts这时候就得走源码构建或手动补装的路子。判断你当前Qt6环境里到底有没有QtChart最直接的办法是去安装目录下找库文件。以Windows为例MinGW版本一般在C:\Qt\6.x.x\mingw_64\lib下能看到Qt6Charts.dll和Qt6Chartsd.dlld结尾是调试版同时C:\Qt\6.x.x\mingw_64\include\QtCharts这个头文件目录也必须存在。两个位置都对了才说明组件真的装上了。这个验证方法比在Qt Creator里看Kit信息更可靠后面排查问题也用得上。2. 把组件装进Qt6的三种路子与实操细节2.1 在线安装器与维护工具补装最推荐的方式如果你当初是用在线安装器装的Qt6装完之后桌面或开始菜单里会有一个MaintenanceTool.exe维护工具它在Qt安装目录的根目录下。用它补装组件是最省事的方式启动MaintenanceTool选择添加或移除组件。等它读取完远程软件源展开Qt- 你已安装的版本号比如Qt 6.5.3 -Additional Libraries。勾选Qt Charts确认后等下载安装即可。这里有两个细节容易被忽略。第一Additional Libraries里的可选项和你当初安装时选择的编译器套件有关如果你机器上同时装了MSVC和MinGW两个套件记得把对应的Qt Charts都勾上否则切换编译套件后照样找不到模块。第二安装过程中如果网络不稳定组件可能下载不完整导致库文件存在但编译时出现奇怪的链接错误。碰到这种情况重跑一次维护工具、把Qt Charts取消勾选应用一次再重新勾选安装通常能解决。还有一点值得专门提醒维护工具里的软件源默认是官方仓库国内网络环境下经常卡在正在读取仓库这一步。如果你的网络访问官方源确实慢可以用镜像源在维护工具设置里把临时仓库地址替换成国内镜像站对应的Qt路径再把默认存储库留空。镜像站的具体URL可以搜到下文的案例https://mirrors.xxx.com/qt/注意替换成你实际使用的镜像地址并且路径最后要带斜杠。设置完成后需要重新启动维护工具才能生效。2.2 离线环境中手动补装拷贝法有些环境完全隔离外网没法用在线安装器这时候如果有一台同样版本Qt、同样编译器套件且已经装好Qt Charts的机器可以采取拷贝法把这台机器的Qt Charts相关文件整体拷贝到目标机器的对应Qt目录里。需要拷贝的内容包括include\QtCharts整个头文件目录lib\cmake\Qt6Charts整个CMake配置目录lib\Qt6Charts.dll和lib\Qt6Chartsd.dll动态库lib\Qt6Charts.lib/lib\Qt6Chartsd.lib导入库MSVC版本需要mkspecs\modules\qt_lib_charts.priqmake需要的pri文件translations目录下和qtcharts相关的qm翻译文件可选拷贝完成后建议运行一次qmake -query确认Qt前缀路径没问题然后新建一个测试工程编译验证。这个办法在有严格内网管控的项目里我实际用过只要源和目标机器的Qt主版本、小版本以及编译器套件完全一致成功率很高。如果两边小版本不一致比如一个6.5.0一个6.5.2虽然一般也能用但最好还是保持一致降低出幺蛾子的概率。2.3 源码自行构建Qt Charts最后的手段如果连拷贝的来源机器都没有那就只剩自己编译Qt Charts源码这一条路了。Qt Charts的源码在官方代码仓库里对应Qt6分支的源码拉下来之后用CMake构建git clone -b 6.5 https://code.qt.io/cgit/qt/qtcharts.git cd qtcharts mkdir build cd build cmake .. -DCMAKE_PREFIX_PATHC:/Qt/6.5.3/mingw_64 cmake --build . --parallel 8 cmake --install .CMAKE_PREFIX_PATH要指向你已经装好的Qt6根目录。这样一个好处是源码版本和你的Qt版本完全对齐但代价是编译耗时较长而且如果Qt6是商业授权还要自己确认一下源码使用的开源协议是否合规。这个方法我个人建议非必要不用属于兜底方案。多数情况下前两种方式已经能解决问题。3. 工程里接入QtChartCMake与qmake两套配置组件装好了接下来就是在工程里把它看到。这一步看起来简单但报错的时候也最让人头疼因为不同的构建系统写法不一样混着写就会出问题。3.1 CMake方式Qt6官方推荐如果你的项目是CMake工程CMakeLists.txt里的写法是这样的cmake_minimum_required(VERSION 3.16) project(ChartDemo) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOMOC ON) find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Charts) qt_standard_project_setup() qt_add_executable(ChartDemo main.cpp ) target_link_libraries(ChartDemo PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Charts )核心就是两处find_package里必须加上Chartstarget_link_libraries里必须有Qt6::Charts。容易犯的错是只写find_package(Qt6 REQUIRED)不指定COMPONENTS这样都能通过但链接不到Qt6::Charts目标链接阶段会报一堆找不到符号的错误。我第一次用Qt6写图表程序就犯过这个错编译提示看不懂后来才发现是find_package的COMPONENTS没写全。另外CMake的CMAKE_AUTOMOC要开着因为Qt Charts相关的类头文件里用到Q_OBJECT宏moc处理是必需的。Qt6里qt_standard_project_setup()这个函数会帮你把AUTOMOC、AUTORCC、AUTOUIC都设置好比自己手动写那几行更省心。3.2 qmake方式老项目迁移常见老项目很多还是qmake体系写法相对简单。在.pro文件里加一行QT charts然后重新qmake并构建就可以了。这里要特别提醒qmake认QT chartsCMake认Qt6::Charts两套写法的模块感知机制完全不同。有的教程会混合出现你要是把QT charts塞进CMakeLists.txt里CMake会直接当未知变量忽略掉反过来在.pro里写find_package也一样没用。转换工程时要先把构建系统想清楚别两边都写或者两边都漏。3.3 版本与编译器匹配最隐蔽的坑Qt6对模块和主框架的版本匹配要求比Qt5严格。具体表现是你机器上如果有多套Qt6比如6.2.4和6.5.2CMAKE_PREFIX_PATH指到了6.2.4但组件安装在6.5.2里CMake能顺利找到Qt6的配置却找不到Charts组件报错信息会提示Found package configuration file ... but it set Qt6_FOUND to FALSE。这种时候检查一下CMAKE_PREFIX_PATH指向的Qt目录里是不是真的包含了Qt Charts。另一个隐蔽坑是编译器套件的一致性。同一个Qt版本既有MSVC构建又有MinGW构建库文件不通用。你用MinGW套件编译链接器去找libQt6Charts.a但装的是MSVC版的Qt6Charts.lib根本对不上号。排查这类问题有个笨但有效的办法在Qt安装目录里搜索Qt6Charts看它在你当前使用的那个套件子目录下是否存在比如C:\Qt\6.5.3\msvc2019_64\lib或C:\Qt\6.5.3\mingw_64\lib。确保组件和编译套件在同一个版本目录下这是硬性条件。4. 从零写一个能跑起来的折线图示例配置做完该见真章了。下面这个例子是最基础的折线图完整展示一次性创建图表、添加数据、显示到窗口的全过程。#include QApplication #include QtCharts/QChartView #include QtCharts/QLineSeries #include QtCharts/QChart #include QMainWindow // Qt6中需要打开Qt Charts命名空间否则要写全限定名 QT_CHARTS_USE_NAMESPACE int main(int argc, char *argv[]) { QApplication app(argc, argv); // 1. 准备数据序列 QLineSeries *series new QLineSeries(); series-setName(CPU温度); series-append(0, 36.2); series-append(1, 38.5); series-append(2, 41.0); series-append(3, 39.8); series-append(4, 43.2); series-append(5, 45.1); // 2. 创建图表对象并添加序列 QChart *chart new QChart(); chart-addSeries(series); chart-setTitle(CPU温度曲线); chart-legend()-setVisible(true); chart-legend()-setAlignment(Qt::AlignBottom); // 3. 自动创建坐标轴并挂载到序列上 chart-createDefaultAxes(); chart-axes(Qt::Horizontal).first()-setTitleText(时间(s)); chart-axes(Qt::Vertical).first()-setTitleText(温度(℃)); // 4. 用QChartView承载图表 QChartView *chartView new QChartView(chart); chartView-setRenderHint(QPainter::Antialiasing); // 5. 放到主窗口显示 QMainWindow window; window.setCentralWidget(chartView); window.resize(800, 600); window.show(); return app.exec(); }这段代码里每一块都值得细说。QLineSeries是折线序列数据按点追加append(x, y)的两个参数分别对应x轴和y轴的值。这里注意Qt Charts里点的坐标是double精度别用int硬传比如append(0, 36.2)是合法的写append(0.0, 36.2)更严谨。createDefaultAxes()是个偷懒函数它会根据已添加的序列类型自动创建一组坐标轴并把轴和序列关联起来。看起来很方便但它默认创建的轴是QValueAxis轴的范围会自动取序列数据的最小最大值并留一点边距。实际项目里你会发现默认轴的刻度不一定理想比如希望x轴从0到10、步进2希望y轴从30到50、步进5这些后面要手动创建轴再替换。QT_CHARTS_USE_NAMESPACE这个宏值得解释一下。Qt Charts在Qt6中把类放在了QtCharts命名空间里如果你不用这个宏所有的QLineSeries、QChart都得写成QtCharts::QLineSeries、QtCharts::QChart。用了宏之后当前编译单元内的名字查找会优先考虑QtCharts命名空间代码简洁很多。这个宏在Qt5.14之后引入Qt6里是官方推荐写法。QChartView是QGraphicsView的子类它内部管理了一个QGraphicsScene图表本质上是一个可交互的图形项。这里给它开了QPainter::Antialiasing抗锯齿折线边缘会平滑很多。不加这行曲线放大后会有明显的锯齿感尤其是在高分屏上。运行效果一个800x600的窗口标题叫CPU温度曲线底部有图例x轴y轴都有文字标签中间是一条平滑的折线连接6个数据点。窗口可以用鼠标拖拽平移、滚轮缩放这些交互是QChartView默认自带的不用额外写代码。如果你要同时显示多条曲线addSeries可以调用多次。每条序列有自己的名称图例会为每个序列生成一个条目。颜色方面Qt Charts有默认调色板第一条蓝、第二条红、第三条绿如果觉得颜色不协调可以手动设置QPenQPen pen(QColor(0, 128, 255)); pen.setWidth(2); series-setPen(pen);柱状图、饼图的思路类似只是序列类型换成QBarSeries、QPieSeries数据组织方式不同。柱状图需要先创建QBarSet把各类别数据塞进去再通过QBarCategoryAxis作为x轴饼图直接pieSeries-append(标签, 数值)就行。一旦理解了序列 - 图表 - 视图三层结构换图表类型就只是换类的事情了。5. 自定义坐标轴别被createDefaultAxes骗了createDefaultAxes虽然省事但它生成的轴往往不是你想要的样子。它会自动选择轴类型但对于大多数业务场景来说坐标轴的刻度间隔、显示范围、轴标签精度都需要按实际需求调。手动创建坐标轴的标准姿势是这样的QValueAxis *axisX new QValueAxis(); axisX-setRange(0, 10); axisX-setTickCount(6); axisX-setLabelFormat(%.1f); axisX-setTitleText(时间(s)); QValueAxis *axisY new QValueAxis(); axisY-setRange(30, 50); axisY-setTickCount(5); axisY-setLabelFormat(%.1f℃); axisY-setTitleText(温度(℃)); chart-setAxisX(axisX, series); chart-setAxisY(axisY, series);关键点是setAxisX(axisX, series)的第二个参数它表示这条轴要关联到哪个序列上。图表里可以同时存在多条序列不同序列可以挂不同的坐标轴比如一条看温度、一条看湿度量纲不一样。这时候再用createDefaultAxes就完全满足不了了必须手动创建并显式关联。setLabelFormat(%.1f)是很多人容易忽略的地方默认情况下轴标签会按%g格式显示小数位数看数据情况显示出来的数字长短不齐特别丑。统一设成%.1f或者根据实际业务取整数视觉上会干净很多。另外有一个冷门但好用的点QValueAxis支持反向坐标轴axisX-setReverse()这在某些图表场景比如深度剖面图里非常实用。还有axisX-setGridLineVisible(false)可以关掉网格线让图表更清爽。坐标轴的精度问题也值得一说。Qt Charts在缩放时会根据当前视图范围动态重新计算刻度标签如果轴范围特别大比如0到100000默认的%g格式会显示成100000这样很长的数字此时可以设成%.0e用科学计数法或者用自定义的格式化回调。这块没有统一答案完全取决于业务需要。6. 动态更新图表数据定时采集场景的实时刷新方案做上位机和监控界面时图表数据往往不是一次性写死的而是不断追加的。Qt Charts里更新数据有两种思路局部刷新和整体重建。6.1 追加新数据点最直观的做法是每来一个新采样点就往序列里append一个点。但要注意如果直接往QLineSeries里不停追加序列内部的数据容器会不断重新分配内存数据点一多比如几千几万个点界面会明显卡顿。更好的做法是先调用series-removePoints(0, count)移除头部旧数据再追加新数据同时把坐标轴范围跟着平移// 每秒追加一个数据点最多保留100个点 void appendNewData(qreal x, qreal y) { const int MAX_POINTS 100; series-append(x, y); // 删除超出范围的旧数据 if (series-count() MAX_POINTS) { series-removePoints(0, series-count() - MAX_POINTS); } // 动态调整x轴范围始终显示最近的一段 axisX-setRange(x - 60, x); }这套滚动窗口方案是我在实际项目里比较推荐的。removePoints比清空重建要高效因为Qt Charts只更新受影响的可视区域不会整个重绘。不过这里有个细节removePoints一次调用删除多个点时序列的信号会合并触发但如果每删除一个点就触发一次信号性能同样受影响。稳妥做法是把数量算好removePoints(0, series-count() - MAX_POINTS)这样一次性删到位。6.2 用QTimer驱动定时刷新动态数据的来源通常是定时器或者Socket回调下面用QTimer做一个最简单的演示框架class ChartWindow : public QMainWindow { Q_OBJECT public: ChartWindow() { // ...创建chart、series、axis等... timer new QTimer(this); connect(timer, QTimer::timeout, this, ChartWindow::updateData); timer-start(1000); // 每秒刷新一次 } private slots: void updateData() { static qreal t 0; t 1.0; qreal y qSin(t / 5.0) * 5 40; // 模拟温度波动 appendNewData(t, y); } };这里要注意QTimer的刷新频率不要超过你的数据产生频率也没必要追求过高的刷新率。屏幕往往只有60Hz你1毫秒刷新一次屏幕也显示不过来反而白白占用CPU。一般UI刷新控制在10Hz~20Hz以上数据采样频率和UI更新频率可以分离用两个定时器或者缓冲队列解耦这样就算数据源再快UI也只是按需更新。6.3 隐藏的性能雷区动态更新时最影响性能的问题其实是坐标轴范围频繁变化导致整张图全量重绘而不仅仅是新增点区域的重绘。实测下来每秒只更新一处区域大约几十个像素宽和整图重绘CPU占用差距可以到几个百分点。我通常的做法是优先调整轴范围而不是重建轴对象。也就是说不要在每次刷新时new QValueAxis再setAxisX那样会创建大量临时对象还容易造成内存泄漏。正确的做法是在初始化时创建一个轴实例后续更新只调用setRange改范围。另外如果数据点极其密集上百万个QLineSeries的性能再怎么优化也不乐观这时候有两个方向一是用QXYSeries::replace()方法替换整段数据这个方法会尽量复用内部缓冲区比先清空再追加快很多二是对原始数据做抽稀downsampling只保留可视范围内的关键点最常用的算法是LTTBLargest Triangle Three Buckets可以把几百万个点压缩成几千个点而视觉上几乎无损。这个方法写起来有点复杂度就不展开代码了但结论是明确的Qt Charts本身不是为百万级大数据量设计的数据处理策略要前置考虑。7. 现场排错组件装好却编译失败的几个典型问题最后把这几年我收集到的Qt Charts相关报错集中梳理一遍很多问题在搜索引擎上问了半天其实原因非常简单。7.1 CMake找不到Charts组件报错长这样CMake Error at CMakeLists.txt:5 (find_package): Found package configuration file: .../Qt6Config.cmake but it set Qt6_FOUND to FALSE.网络上回答最多的原因是没有安装Qt Charts但实际排查时要先分清是整个Qt6都没被CMake找到还是只有Charts没找到。最简单的方法是把find_package拆开find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) find_package(Qt6 REQUIRED COMPONENTS Charts)如果第一行能过、第二行报错问题基本锁定在Charts组件确实没安装或者找错了Qt路径两行都不过则是CMAKE_PREFIX_PATH指向错误。这时候在CMakeLists里加一行输出看看message(STATUS CMAKE_PREFIX_PATH${CMAKE_PREFIX_PATH})确认路径里包含你期望的Qt6目录并且该目录下lib/cmake里有Qt6Charts文件夹。7.2 链接时一堆未定义引用编译阶段全过链接阶段报一堆类似undefined reference to QLineSeries::QLineSeries()很常见的场景是MinGW环境下找不到Qt6Charts.dll对应的导入库。MinGW的导入库是libQt6Charts.a如果你的Qt目录里只有Qt6Charts.dll而没有libQt6Charts.a链接器就找不到符号。这种情况多半是组件库文件拷贝不完整导致的重装或者重新拷贝就解决了。另一个可能你用的库文件是调试版还是发布版对不上。CMake默认会根据CMAKE_BUILD_TYPE选库Debug模式找Qt6Chartsd.dll和libQt6Chartsd.aRelease模式找不带d的版本。如果只拷贝了其中一套切换构建模式时就会出现找不到库的问题。检查一下CMAKE_BUILD_TYPE并确保两种库都存在。7.3 运行时崩溃或窗口白屏编译链接全过了运行起来却崩溃或者窗口一片白。先确认代码里有没有写QT_CHARTS_USE_NAMESPACE。在Qt6里如果漏了编译阶段通常会报QLineSeries未定义之类的错误但也有例外某些老代码用了全局命名空间污染的方式能编译过但运行时对象没正确构造表现就是白屏。运行时崩溃还有一个常见元凶在QChartView还没显示之前就对chart调用了某些需要布局生效的函数比如在构造函数里直接访问chart-axes()。如果返回的列表为空你紧接着调用.first()就会崩溃。这种情况要检查执行顺序确保addSeries和createDefaultAxes都执行完之后再取坐标轴。7.4 中文乱码图表标题、坐标轴标题、图例文字如果是中文在Windows上偶尔会遇到乱码。Qt6默认源码用UTF-8编码问题多数出在源文件本身的保存编码上。VS里新建的源文件默认可能是GBK或带BOM的UTF-8而MinGW的编译器可能按UTF-8无BOM解释就会乱。解决路径很简单源文件统一用UTF-8无BOM保存这是Qt6官方推荐的源码编码。编辑器里调整一下另存为编码即可基本不需要在代码里额外处理。Linux和macOS上一般没有这个问题。7.5 部署发布时的DLL缺失最后一个坑不是开发阶段的是部署阶段的。把程序复制到没装Qt的机器上运行时提示缺少Qt6Charts.dll。用windeployqt工具部署时要注意老版本windeployqt可能不自动拷贝Qt Charts的运行时库需要手动把Qt6Charts.dll复制到exe所在目录。Qt6的windeployqt已经智能很多但如果用了自定义插件或做了裁剪还是建议部署后检查一下DLL依赖用Dependencies或Process Explorer看一眼确实带着Qt6Charts.dll再发出去。8. 样式美化与交互增强让图表更像产品最后聊一点画蛇添足但实际很有用的东西。做完基础图表之后你会发现默认样式很朴素。想让图表真正能拿得出手这几个增强项值得花时间研究。主题方面Qt Charts内置了几套主题一行代码切换chart-setTheme(QChart::ChartThemeDark);ChartThemeDark比较适合做成深色背景的监控大屏ChartThemeHighContrast适合投影场景ChartThemeLight是默认的。设置主题后背景色、调色板、网格线颜色、文字颜色都会联动变化比自己一个个改省事很多。动画效果可以锦上添花chart-setAnimationOptions(QChart::SeriesAnimations);SeriesAnimations会让序列在数据变化时平滑过渡到新位置在演示场景里很有效果。但注意如果数据刷新很快每秒多次更新动画反而会拖慢界面这时候建议设成NoAnimation。图例位置和样式也可以微调chart-legend()-setMarkerShape(QLegend::MarkerShapeFromSeries); chart-legend()-setFont(QFont(Microsoft YaHei, 9));最后一个有实用价值的增强是点击事件。结合QChartView的信号或者自己对QGraphicsScene做事件过滤可以实现在鼠标悬停或点击数据点时提示具体数值。这块涉及QXYSeries::hovered信号写法也比较固定connect(series, QXYSeries::hovered, this, [](const QPointF point, bool state) { if (state) { statusBar()-showMessage( QString(X%1, Y%2).arg(point.x()).arg(point.y())); } });注意QXYSeries::hovered信号在Qt6里的参数是const QPointF , bool老代码里可能是(const QPointF , bool)已经变了以当前版本的头文件为准。connect时如果编译报参数不匹配检查一下信号的定义再调整槽函数。整套下来从组件安装到画图再到动态刷新和样式调整Qt Charts的基本链路就完整了。实际使用中你会发现真正的业务难点往往不在画图本身而是数据怎么组织、坐标轴怎么定标、性能怎么保障这些经验只能靠一个个需求喂出来希望上面这些内容能帮你跳过最开始那段最煎熬的踩坑期。
返回列表