# qtcustomplot-view **Repository Path**: 1667/qtcustomplot-view ## Basic Information - **Project Name**: qtcustomplot-view - **Description**: No description available - **Primary Language**: C++ - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-24 - **Last Updated**: 2026-06-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # QtView 项目说明 基于 Qt 5.9.3 + QCustomPlot 2.1.1 的坐标轨迹绘制示例。 提供一个可复用的 `TrajectoryScene` 控件:通过 `addPoint()` 追加业务坐标点,自动连线形成轨迹,点击散点弹出气泡悬浮窗查看该点的属性,方便连续点击浏览。 支持 **MinGW** 与 **MSVC141(VS 2017 工具集)** 两种编译器,可通过 `build.ps1` / `run.ps1` 的 `-Compiler` 参数切换。 --- ## 1. 环境 | 项 | MinGW | MSVC141 | | --- | --- | --- | | Qt 安装路径 | `D:\works\Qt5.9.3` | `D:\works\Qt5.9.3` | | Qt 套件 | `5.9.3\mingw53_32`(32 位) | `5.9.3\msvc2017_64`(64 位) | | 工具链 | `qmake` + `mingw32-make` / `jom` | `qmake` + `vcvarsall.bat` + `nmake` / `jom` | | 编译器 | `D:\works\Qt5.9.3\Tools\mingw530_32\bin` | VS 18 自带 v141 工具集(`14.16.27023`) | | 平台位数 | x86 | x64 | | Shell | PowerShell 5 | PowerShell 5 | MSVC 工具链关键点: - Visual Studio 安装:`C:\Program Files\Microsoft Visual Studio\18\Enterprise`(VS 18 自带 v141 工具集,**无需**安装独立 VS 2017)。 - vcvars 调用:`vcvarsall.bat x64 -vcvars_ver=14.16`,强制使用 v141 = VS 2017 工具集。 - Windows SDK:由 `vcvarsall.bat` 自动注入 `INCLUDE` / `LIB` / `PATH`。 > 若本机 Qt 套件 / VS 安装路径不同,修改 `build.ps1` / `run.ps1` 顶部的 `$MinGW_QtDir` / `$MSVC_QtDir` / `$VsInstall` 即可。 --- ## 2. 目录结构 ``` d:\works\qtview\ ├── qtview.pro # qmake 工程文件 ├── main.cpp # 主程序源码(QMainWindow + 演示定时器) ├── README.md # 本文件 ├── build.ps1 # 编译脚本(Debug/Release + 并行) ├── run.ps1 # 启动脚本(自动加 Qt bin 到 PATH) ├── src/ │ └── sceneview/ # 场景视图模块(轨迹绘制 + QCustomPlot) │ ├── trajectoryscene.h / .cpp # 核心:QCustomPlot 派生的轨迹绘制类 │ ├── trajectorypoint.h # 轨迹点数据结构(x, y, attrs, index) │ ├── pointattrdialog.h / .cpp # 点属性气泡悬浮窗(无边框、圆角、阴影) │ ├── qcustomplot.h # QCustomPlot 2.1.1 头文件 │ └── qcustomplot.cpp # QCustomPlot 2.1.1 实现 ├── debug/ # Debug 编译产物 │ ├── main.o │ ├── trajectoryscene.o │ ├── pointattrdialog.o │ ├── qcustomplot.o │ ├── moc_*.o │ └── qtview.exe ├── release/ # Release 编译产物(运行后生成) ├── Makefile* # qmake 生成的中间文件 └── .qmake.stash # qmake 缓存 ``` --- ## 3. 工程文件 [qtview.pro](file:///d:/works/qtview/qtview.pro) ```pro QT += core gui printsupport # printsupport 供 QCustomPlot 打印支持 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 shared # 显式声明动态链接 Qt、启用 C++11 TARGET = qtview TEMPLATE = app DEFINES += QT_DEPRECATED_WARNINGS # 32/64 位差异由所选 Qt 套件(mingw53_32 / msvc2017_64)决定,pro 文件无需额外处理。 # 仅在 MSVC 下打开 /W3 /permissive- /utf-8 等较严格的开关 # /utf-8 让 cl 把源文件按 UTF-8 读,避免 main.cpp / *.h 里的中文注释触发 C4819/C2001 msvc { QMAKE_CXXFLAGS += /W3 /permissive- /Zc:__cplusplus /utf-8 CONFIG(debug, debug|release) { QMAKE_CXXFLAGS += /Od /RTC1 } else { QMAKE_CXXFLAGS += /O2 } } SOURCES += main.cpp \ src/sceneview/trajectoryscene.cpp \ src/sceneview/pointattrdialog.cpp \ src/sceneview/qcustomplot.cpp HEADERS += src/sceneview/trajectoryscene.h \ src/sceneview/trajectorypoint.h \ src/sceneview/pointattrdialog.h \ src/sceneview/qcustomplot.h # 让源码可以用短路径引用头文件 INCLUDEPATH += $$PWD/src/sceneview ``` 要点: - `QT += printsupport` —— QCustomPlot 2.x 的导出/打印功能依赖 `QtPrintSupport`。 - `CONFIG += shared` —— 强制使用 Qt 的动态库(`Qt5*.dll`)。 - `CONFIG += c++11` —— 启用 C++11。 - `QT += widgets` —— Qt5 引入的模块拆分,必须显式加上。 - `src/sceneview/qcustomplot.*` —— QCustomPlot 官方单文件分发版,直接编译进工程,与业务控件同目录管理。 - `msvc { ... }` 作用域块 —— MinGW 编译时该块完全被忽略,MSVC 编译时启用 `/W3` `/permissive-` `/utf-8` 等 MSVC 专属开关;`/utf-8` 是必须的,因为源码里含中文注释。 --- ## 4. 主程序 [main.cpp](file:///d:/works/qtview/main.cpp) - `QMainWindow` 作为主窗口。 - 菜单栏:`文件 / 轨迹切换(弓字形 / 回字形)/ 清空轨迹 / 退出`、`帮助 / 关于`。 - 状态栏:默认 `就绪:演示正在追加点…`;加载/清空轨迹时显示提示;拖动 / 缩放时显示新范围。 - 中央部件:`TrajectoryScene`(QCustomPlot 派生),菜单"加载弓字形/回字形轨迹"通过 `setPoints()` 一次性灌入预生成路径;启动时每 50 ms 追加一个点画正弦曲线 `y = 2·sin(x)`,共 60 个点。点击散点弹出属性气泡(由 `TrajectoryScene` 内部管理,`main.cpp` 无需处理点的点击事件)。 - 内置轨迹生成器(`namespace` 内的纯函数): - `generateBowTrajectory()` — 蛇形来回扫描(弓字形),区域 10×8,5 行 10 列,共 55 个点。 - `generateHuiTrajectory()` — 由外向内逐层回字形(从 (0,0) 起算,每层一矩形螺旋),最大半径 4.5,步长 1。 ### 4.1 核心控件 `TrajectoryScene`([trajectoryscene.h](file:///d:/works/qtview/src/sceneview/trajectoryscene.h) / [.cpp](file:///d:/works/qtview/src/sceneview/trajectoryscene.cpp)) | 项 | 说明 | | --- | --- | | 基类 | `QCustomPlot` | | 业务坐标 | x 向右、y 向上(与 QCustomPlot 默认一致,无需翻转) | | 渲染 | 1 条 `QCPCurve`:`lsLine` 折线 + `ssDisc` 散点 | | 交互 | `iRangeDrag` 拖动 + `iRangeZoom` 滚轮缩放 + `iSelectPlottables` 点选 | | 选中 | `setSelectable(QCP::stSingleData)` + `plottableClick` → 内部弹出属性气泡 + 发出 `pointClicked(idx, pt)` 信号 | | 属性气泡 | 内部持有 `PointAttrDialog`,点击点时自动在鼠标位置显示气泡悬浮窗,外部无需手动管理 | | 自适应 | 每次 `addPoint` / `setPoints` 后 `rescaleAxes(true)`,并强制把原点 (0,0) 包含进视野 | > **为什么用 `QCPCurve` 而不是 `QCPGraph`?** QCP 2.1 的 `QCPGraph` 内部用 `QMap` 按 key(即 X)自动排序,破坏路径的插入顺序。`setPoints()` 灌入的"蛇形/回字形"路径会被重排成"按 X 列优先"的点集,连线就变成纵向 zigzag 了。`QCPCurve` 用独立的 `t` 参数作为排序键(这里传点的索引 i),保留插入顺序,路径就对了。 公开 API: ```cpp int addPoint(qreal x, qreal y, const QVariantMap &attrs = {}); int addPoint(const QPointF &pos, const QVariantMap &attrs = {}); void setPoints(const QVector &points); // 一次性灌入整条路径 void clear(); int pointCount() const; const TrajectoryPoint &pointAt(int index) const; QRectF dataRange() const; // 当前 X/Y 轴范围(业务坐标) signals: void pointAdded (int index, const TrajectoryPoint &pt); void pointClicked(int index, const TrajectoryPoint &pt); void rangeChanged(const QRectF &range); ``` ### 4.2 数据结构 `TrajectoryPoint`([trajectorypoint.h](file:///d:/works/qtview/src/sceneview/trajectorypoint.h)) ```cpp struct TrajectoryPoint { qreal x = 0.0; // 业务 x qreal y = 0.0; // 业务 y QVariantMap attrs; // 任意业务属性(键值对) int index = -1; // 在所属轨迹中的序号 }; ``` ### 4.3 点属性气泡 `PointAttrDialog`([pointattrdialog.h](file:///d:/works/qtview/src/sceneview/pointattrdialog.h) / [.cpp](file:///d:/works/qtview/src/sceneview/pointattrdialog.cpp)) 点击轨迹点时弹出的气泡悬浮窗,替代状态栏显示,方便连续点击浏览。由 `TrajectoryScene` 内部创建和管理,外部无需直接使用。 | 特性 | 实现 | | --- | --- | | 窗口样式 | 无边框 + 圆角 + 投影阴影 | | 显示位置 | 跟随鼠标点击位置,自动边界检测(不超出屏幕) | | 内容区域 | 基本信息(序号 / X / Y)+ 业务属性(动态键值对,可滚动) | | 交互 | 不抢焦点(`WA_ShowWithoutActivating`),点击气泡外区域自动关闭 | | 文字可选 | 坐标值和属性值支持鼠标选中复制 | 公开 API: ```cpp void setPoint(int index, const TrajectoryPoint &pt); void showAt(const QPoint &globalPos); ``` --- ## 5. 编译脚本 [build.ps1](file:///d:/works/qtview/build.ps1) | 参数 | 默认 | 说明 | | --- | --- | --- | | `-Compiler` | `MinGW` | 取值 `MinGW` / `MSVC`(脚本会校验) | | `-Config` | `Debug` | 取值 `Debug` / `Release`(脚本会校验) | | `-Jobs N` | 自动检测 CPU 核心数 | 并行任务数;0 = 自动 | | `-Clean` | 关闭 | 先清理再编译(全量重编) | | `-CleanOnly` | 关闭 | 只清理不编译 | ### 5.1 MinGW 流程 1. 临时把 `qmake.exe` / `mingw32-make.exe` 加到当前会话的 `PATH` 前面。 2. 若指定 `-Clean` 或 `-CleanOnly`,清理上次编译产物(`release/`、`debug/`、`Makefile*`、`moc_*.cpp`、`ui_*.h`、`*.o` 等)。 3. 调用 `qmake qtview.pro "CONFIG+=$($Config.ToLower())"` —— 注意是小写,qmake 只识别 `debug`/`release`。 4. 优先用 `jom -jN`(Qt 自带,对 Windows 并行更稳),回退到 `mingw32-make -jN`。 5. 打印最终生成的 exe 路径。 > **默认增量编译**:不加 `-Clean` 时为增量编译,`make` / `jom` 会根据文件修改时间只重编改动过的目标文件,编译速度更快。 ### 5.2 MSVC141 流程 1. 解析 `qmake` 路径(`D:\works\Qt5.9.3\5.9.3\msvc2017_64\bin\qmake.exe`)。 2. 若指定 `-Clean` 或 `-CleanOnly`,清理上次编译产物(`release/`、`debug/`、`Makefile*`、`moc_*.cpp`、`ui_*.h`、`*.obj` 等)。 3. 调用 `vcvarsall.bat x64 -vcvars_ver=14.16`,把 `cl.exe` / `link.exe` / `INCLUDE` / `LIB` / `PATH` 全部注入**当前 PowerShell 进程**(通过 `cmd /c "call vcvars... && set"` 回读环境变量实现)。 4. 校验 `cl.exe` 已就绪,确认走的是 v141 工具集(`14.16.27023`)。 5. 临时把 `msvc2017_64\bin` 加到 `PATH` 前面,让 qmake 找得到自己需要的 DLL。 6. 调用 `qmake` 生成 NMake 用的 Makefile。 7. 优先 `jom -jN`,回退到 `nmake`(单线程)。 ### 用法示例 ```powershell cd d:\works\qtview # 默认 MinGW Debug + 自动并行(增量编译) powershell -ExecutionPolicy Bypass -File .\build.ps1 # MinGW Release(增量编译) .\build.ps1 -Compiler MinGW -Config Release # MSVC141 Debug(增量编译) .\build.ps1 -Compiler MSVC -Config Debug # MSVC141 Release + 8 并发(增量编译) .\build.ps1 -Compiler MSVC -Config Release -Jobs 8 # 全量重编(先清理再编译) .\build.ps1 -Clean # 只清理不编译 .\build.ps1 -CleanOnly ``` --- ## 6. 启动脚本 [run.ps1](file:///d:/works/qtview/run.ps1) | 参数 | 默认 | 说明 | | --- | --- | --- | | `-Compiler` | `MinGW` | 取值 `MinGW` / `MSVC`,决定加载哪个 Qt 套件的 `bin` 目录 | | `-Config` | `Debug` | 取值 `Debug` / `Release` | | `-Build` | 关闭 | 启动前先编译一次(使用同一 `-Compiler` / `-Config`) | 行为: 1. 若指定 `-Build`,先调用 `build.ps1`,失败则终止。 2. **MSVC 模式**:先调用 `vcvarsall.bat x64 -vcvars_ver=14.16`,把 MSVC 运行时与 Windows SDK 的 DLL 路径(`PATH`)抽出来,**仅用于本次启动**。 3. 把所选 Qt 套件的 `bin` 目录(MinGW: `mingw53_32\bin`;MSVC: `msvc2017_64\bin`)临时加到 `PATH` 前面(用于解析 `Qt5Core.dll` 等)。 4. 启动 `$Config\qtview.exe`,把脚本收到的额外参数(`--` 之后)原样透传给 exe。 5. exe 退出后恢复原 `PATH`,**不会污染当前 PowerShell 会话的环境**。 6. 把 exe 的退出码作为脚本的退出码返回,便于和其它脚本串联。 ### 用法示例 ```powershell # 直接启动 MinGW Debug 产物 .\run.ps1 # 启动 MinGW Release 产物 .\run.ps1 -Compiler MinGW -Config Release # 启动 MSVC141 Debug 产物(需先编译) .\run.ps1 -Compiler MSVC -Config Debug # 编译并启动(MSVC141 Release) .\run.ps1 -Compiler MSVC -Build -Config Release # 编译 Release 并启动,同时给程序传 Qt 风格参数 .\run.ps1 -Build -Config Release -- -style fusion ``` --- ## 7. 进度记录 | 步骤 | 状态 | 说明 | | --- | --- | --- | | 搭建最小 Qt 窗口工程 | ✅ 完成 | `qtview.pro` + `main.cpp`,主界面含菜单/状态栏/中央部件 | | 命令行编译(cmd 批处理) | ✅ 完成 → 弃用 | 初次实现 `build.bat`,在 PowerShell 下因中文编码被截断,弃用 | | 命令行编译(PowerShell) | ✅ 完成 | `build.ps1`,临时改 PATH、清理、qmake、并行编译 | | 选择 Debug / Release | ✅ 完成 | `-Config` 参数,默认 Debug,传给 qmake 时小写化 | | 并行编译 | ✅ 完成 | 默认按 CPU 核心数;优先 `jom`,回退到 `mingw32-make -jN`;`-Jobs` 可覆盖 | | 动态链接 Qt | ✅ 完成 | `.pro` 显式 `CONFIG += shared`;顺带修复 Debug 大写问题 | | 启动脚本 | ✅ 完成 | `run.ps1`,自动把 Qt bin 加到 PATH 后启动 exe | | 文档(README) | ✅ 完成 | 本文件 | | V1:QGraphicsScene 轨迹示例 | ✅ 完成 → 替换 | 用 `QGraphicsScene` + 自绘 `PointItem` / `AxisItem` 实现首版轨迹,已被 V2 取代 | | 引入 QCustomPlot 2.1.1 | ✅ 完成 → 迁移 | 原置于 `thirdparty/qcustomplot/`,后迁移至 `src/sceneview/` 统一管理 | | V2:QCustomPlot 轨迹控件 | ✅ 完成 | `TrajectoryScene` 继承 `QCustomPlot`,`QCPGraph` 折线 + 散点;拖动 / 缩放 / 点击点均由 QCustomPlot 内建支持 | | 清理过时源码 | ✅ 完成 | 删除 `pointitem.{h,cpp}` / `axisitem.{h,cpp}`,相关 .pro 条目已移除 | | MSVC141 编译器支持 | ✅ 完成 | 复用 VS 18 自带 v141 工具集(`14.16.27023`),`build.ps1` / `run.ps1` 新增 `-Compiler` 参数;`qtview.pro` 增加 `msvc {}` 作用域(`/W3` `/permissive-` `/utf-8`) | | V3:点属性弹窗(对话框) | ✅ 完成 → 替换 | 点击点弹出 `PointAttrDialog`(QDialog),后重构为气泡悬浮窗 | | V4:点属性气泡悬浮窗 | ✅ 完成 | 无边框 + 圆角 + 投影阴影,跟随鼠标位置,点击外部自动关闭,不抢焦点方便连续点击 | | QCustomPlot 源码迁移 | ✅ 完成 | 从 `thirdparty/qcustomplot/` 移至 `src/sceneview/`,与业务控件同目录管理,Git 重命名保留历史 | | 属性气泡收敛至 TrajectoryScene | ✅ 完成 | `PointAttrDialog` 改由 `TrajectoryScene` 内部持有并管理,`main.cpp` 不再处理点的点击事件,职责更内聚 | | 增量编译支持 | ✅ 完成 | 默认改为增量编译,新增 `-Clean`(清理后全量重编)和 `-CleanOnly`(只清理不编译)参数 | | V5:内置轨迹路径生成器 | ✅ 完成 | `main.cpp` 的匿名 namespace 内新增 `generateBowTrajectory()`(蛇形来回扫描,10×8 / 5 行 10 列)和 `generateHuiTrajectory()`(由外向内回字形,最大半径 4.5 / 步长 1) | | V5:菜单"轨迹切换"子菜单 | ✅ 完成 | `文件` 菜单下新增 `轨迹切换(&T)`,含 `弓字形(&B)` / `回字形(&H)` 两个可勾选 `QAction`(互斥 `QActionGroup`);清空轨迹时自动取消勾选 | | V5:`TrajectoryScene::setPoints()` | ✅ 完成 | 新增 `void setPoints(const QVector &points)` 批量灌入接口;循环里用点索引 i 作为 `t` 参数以保留插入顺序 | | V5:`QCPGraph` → `QCPCurve` | ✅ 完成 | QCP 2.1 的 `QCPGraph` 按 X 自动排序破坏路径顺序,改用 `QCPCurve`(独立 `t` 排序键,2.1 无 `addCurve()` 需 `new QCPCurve(xAxis, yAxis)`,QCPAbstractPlottable 构造时自动注册到 plot);新增私有成员 `QCPCurve *m_curve` | --- ## 8. 待办 / 后续可扩展点 - [ ] 加入 `windeployqt` 自动拷贝运行所需 DLL 和 platform 插件(用于分发 exe 到无 Qt 环境的机器)。 - [ ] 拆分 `MainWindow` 类到独立的头/源文件,方便后续扩展。 - [ ] 增加 CMake 构建方式(`CMakeLists.txt`)作为备选。 - [ ] 引入 Qt 资源文件(`.qrc`)+ 图标。 - [ ] 加一段简单的单元测试或冒烟启动检测。 - [ ] `TrajectoryScene` 支持多条轨迹(多 `QCPGraph`),每条独立颜色 / 名称 / 选中。 - [ ] `TrajectoryScene` 增加 hover 高亮 + tooltip 显示点的属性。 - [ ] `addPoint` 增加可选的"自动重算范围"开关,避免与用户的拖动缩放冲突。 - [ ] 气泡悬浮窗增加小箭头指向被点击的点。 - [ ] 支持键盘方向键在点之间切换(上一个 / 下一个)。