
【Qwt 7.0 系列】总体架构解析 —— 从单体到三库模块化的演进本文是 Qwt 7.0 系列介绍和教程如果你正在寻找一个高性能、协议友好、同时支持 2D 和 3D 绘图的 Qt 数据可视化库那么这篇文章就是为你准备的。系列总述文章Qwt 7.0 —— 基于 Qt 的高性能 2D/3D 绘图库概述 | 高性能曲线绘制 | 常用图表类型 | 高级科学图表 | 多坐标轴与布局 | 交互功能 | 3D 数据可视化 | 坐标轴与刻度 | 控件与辅助元素 | 总体架构解析 | matplotlib 风格绘图项目地址GitHub | Gitee | 在线文档一、引言如果你用过原版 Qwt 6.x一定对它的一大坨库印象深刻——所有 2D 绘图、控件、工具类全部打包成一个.so/.dll构建系统还是老派的 qmake。想要 3D 绘图得额外引入一个独立的 QwtPlot3D 库两套 API 风格、两套构建配置维护起来相当痛苦。Qwt 7.0 对底层架构进行了彻底重构将原来的单体库拆分为core plot plot3d三个独立的共享库全面迁移到 CMake 构建系统并内置了原 QwtPlot3D 的 3D 绘图能力。这一架构改进不仅带来了更好的模块隔离和按需编译能力也为后续的性能优化如 SIMD 加速和功能扩展如 22 种科学 colormap 预设、3D 主题系统打下了坚实基础。本文将从架构演进的角度深入解析 Qwt 7.0 的三库模块化设计。二、Qwt 6.x 的架构回顾要理解 Qwt 7.0 为什么这样重构先得回顾原版的架构痛点。2.1 单库结构原版 Qwt 6.2.0 将所有代码编译为一个共享库。无论你只用到一条简单的曲线还是用到完整的刻度引擎、栅格数据、SVG 导出链接的都是同一个庞大的库文件。这带来几个问题无法按需链接即使用户只需要基础曲线绘制也不得不把 SVG 导出、OpenGL 画布等无关代码全部链接进来编译耦合修改任何一个模块的代码都要重新编译整个库3D 绘图割裂原 QwtPlot3D 是一个完全独立的项目来自 SciDAVis/qwtplot3d有自己的一套构建系统和代码风格与 Qwt 主库毫无代码复用2.2 qmake 构建系统原版使用 qmake 的.pro文件管理构建。qmake 在 Qt 生态中虽然经典但存在明显局限不支持现代 CMake 的find_package机制第三方项目引入 Qwt 需要手动配置 include 路径和库路径无法生成标准的 CMake Config 文件与现代 C 项目集成困难条件编译选项管理不够灵活2.3 Qt 版本支持原版 Qwt 6.2.0 主要面向 Qt 5对 Qt 6 的支持有限。随着 Qt 6 的普及许多 API 发生了变化如QMouseEvent::pos()→QMouseEvent::position()需要大量条件编译代码来兼容。三、Qwt 7.0 的三库架构Qwt 7.0 的核心架构改进是将单体库拆分为三个独立的 shared library每个库有清晰的职责边界。3.1 三个共享库目标CMake target输出名DLL 导出宏公共基础qwt::coreqwtcore.dll/libqwtcore.soQWTCORE_EXPORT2D 绘图qwt::plotqwtplot.dll/libqwtplot.soQWT_EXPORT3D 绘图qwt::plot3dqwtplot3d.dll/libqwtplot3d.soQWT3D_EXPORT3.2 依赖关系三个库的依赖关系非常清晰┌──────────────────────────┐ │ qwt::core │ ← 基础工具库颜色、数学、数据类型、几何、变换、时间等 └──────────────────────────┘ ↗ ↖ ┌──────────────┐ ┌───────────────┐ │ qwt::plot │ │ qwt::plot3d │ │ (2D) │ │ (3D) │ └──────────────┘ └───────────────┘核心原则plot和plot3d都依赖core但彼此互不依赖。这意味着什么如果你只做 2D 绘图链接qwt::plot就够了完全不会引入任何 OpenGL / 3D 相关的代码和依赖。反之亦然。这种解耦设计让依赖关系最小化编译和部署都更加轻量。3.3 各模块的 Qt 依赖模块Qt 依赖publicQt 依赖private可选依赖coreCore,Gui——plotCore,Gui,WidgetsConcurrent,PrintSupportOpenGL,OpenGLWidgets(Qt6),Svgplot3dCore,Gui,Widgets,OpenGL,OpenGLWidgets(Qt6)—外部gl2ps找不到则内置注意 plot3d 还依赖OpenGL::GLU这是 3D 渲染的必需品。关键设计这些 Qt 依赖在find_package(qwt)引入时会自动传递。你只需要target_link_libraries(your_app PRIVATE qwt::plot)CMake 会自动把 Qt 的 Core、Gui、Widgets 等依赖全部加上无需手动指定。四、Core 模块详解qwt::core是整个项目的基础工具库被 plot 和 plot3d 共享。它包含 28 个模块文件涵盖了从颜色管理到坐标变换的全部基础能力。4.1 模块分类类别核心文件说明全局qwtcore_global.h模块导出宏QWTCORE_EXPORT颜色工具qwt_colormap.h,qwt_color_cycle.h,qwt_colormap_preset.hQwtColorMap及子类、颜色循环、22 种科学 colormap 预设数学工具qwt_math.h,qwt_simd_argminmax.h数学常量与工具函数、SSE2/AVX2/NEON 加速的 argmin/argmax数据类型qwt_interval.h,qwt_point_3d.h,qwt_point_polar.h,qwt_samples.h,qwt_box_statistics.h区间、三维点、极坐标点、样本数据结构、箱线图统计量几何算法qwt_bezier.h,qwt_clipper.h贝塞尔曲线de Casteljau 算法、多边形裁剪坐标变换qwt_transform.h,qwt_scale_map.h,qwt_scale_div.h,qwt_scale_engine.h变换函数线性/对数等、坐标映射、刻度划分、刻度引擎时间处理qwt_date.h,qwt_system_clock.h日期时间工具、高精度计时器通用算法qwt_algorithm.hpp,qwt_qt5qt6_compat.hpp通用算法模板、Qt5/Qt6 兼容层数据容器qwt_grid_data.hpp网格数据容器数据系列qwt_series_data.h,qwt_point_data.h,qwt_series_store.h数据系列基类模板、点数据实现、数据存储模板栅格数据qwt_raster_data.h,qwt_matrix_raster_data.h,qwt_grid_raster_data.h栅格数据基类、矩阵栅格数据、网格栅格数据v7 新增4.2 为什么要独立出 core将基础能力独立为 core 库的好处是显而易见的代码复用plot 和 plot3d 共享同一套颜色映射、坐标变换、数据类型避免重复代码按需编译如果有人只需要 Qwt 的数学工具或颜色映射比如用于自定义绘图可以只链接 core不需要引入整个 2D/3D 绘图栈关注点分离core 不依赖任何 Widgets可以用于无 GUI 的后端计算场景举个例子core 中的QwtColorMapPreset提供了 22 种科学 colormap 预设viridis、plasma、jet、hot 等这些预设既可用于 2D 的QwtPlotSpectrogram也可通过ColorMapColor适配器用于 3D 表面图。一个预设定义两处复用。4.3 SIMD 加速core 模块中的qwt_simd_argminmax.h是一个值得关注的性能优化点。它利用 SSE2 / AVX2x86和 NEONARM指令集来加速大规模数据的 argmin/argmax 运算。在处理百万级数据点的曲线时这一优化可以显著减少渲染延迟。五、Plot 模块详解qwt::plot是 2D 绘图的核心模块承载了原版 Qwt 的全部 2D 绘图能力。5.1 核心功能plot 模块包含但不限于QwtPlot主绘图容器支持多轴、寄生绘图Parasite Plot曲线系列QwtPlotCurve、QwtPlotSpectrogram、QwtPlotTradingCurve等交互工具缩放器、平移器、拾取器Picker刻度绘制QwtScaleDraw、QwtDateScaleDraw图形元素QwtGraphic、QwtColumnSymbol、QwtIntervalSymbol布局管理QwtDynGridLayout放大镜QwtMagnifier5.2 链接方式plot 模块通过 PUBLIC 链接依赖 coretarget_link_libraries(${QWTPLOT_LIB_NAME} PUBLIC ${QWT_LIB_NAME}::core Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets )注意这里用的是PUBLIC而非PRIVATE。这意味着当你的项目链接qwt::plot时core 的头文件路径和符号会自动传递过来。你不需要单独再链接qwt::core。5.3 可选功能plot 模块有两个重要的条件编译选项QWT_CONFIG_QWTOPENGL默认 ON启用 OpenGL 画布支持。启用后会额外依赖Qt::OpenGLQt6 还需Qt::OpenGLWidgets允许使用 GPU 加速渲染高频数据QWT_CONFIG_QWTSVG默认 ON启用 SVG 图像显示和导出额外依赖Qt::Svg如果你不需要这些功能可以在 CMake 配置时关闭以减少依赖cmake -S . -B build -DQWT_CONFIG_QWTOPENGLOFF -DQWT_CONFIG_QWTSVGOFF六、Plot3D 模块详解qwt::plot3d是 3D 绘图模块在 Qwt 7.1 之后正式整合进了主项目。6.1 整合自 QwtPlot3D原版 Qwt 6.x 时代3D 绘图需要单独引入 QwtPlot3D 库这个库来自 SciDAVis/qwtplot3d有完全独立的代码库和构建系统。Qwt 7.0 将 QwtPlot3D 的代码整合进了主项目统一了构建系统和代码风格通过QWT_CONFIG_QWTPLOT_3D选项控制是否编译。这解决了几个长期痛点不再需要维护两套项目3D 绘图可以直接复用 core 模块的颜色映射、数据类型用户只需一次find_package(qwt)就能同时获得 2D 和 3D 能力6.2 gl2ps 回退机制plot3d 模块支持将 3D 场景导出为矢量图PostScript / PDF这依赖 gl2ps 库。CMakeLists.txt 中实现了一个优雅的回退机制find_library(GL2PS_LIBRARY NAMES gl2ps) if(GL2PS_LIBRARY) # 系统已安装 gl2ps直接链接 find_path(GL2PS_INCLUDE_DIR NAMES gl2ps.h REQUIRED) target_link_libraries(${QWTPLOT3D_LIB_NAME} ${GL2PS_LIBRARY}) else() # 系统没有 gl2ps编译内置的 3rdparty/gl2ps 源码 target_sources(${QWTPLOT3D_LIB_NAME} PRIVATE 3rdparty/gl2ps/gl2ps.c 3rdparty/gl2ps/gl2ps.h) target_include_directories(${QWTPLOT3D_LIB_NAME} PRIVATE 3rdparty/gl2ps) endif()这段逻辑确保了无论目标系统是否预装了 gl2ps3D 矢量导出功能都能正常工作。系统有就用系统的没有就编译内置的。这是开箱即用理念的典型实践。6.3 Qwt3D 主题系统plot3d 模块引入了Qwt3D::Qwt3DTheme主题系统封装了 3D 绘图的全部视觉属性背景色、网格色/线宽数据 colormap坐标轴颜色、标题样式光照预设NoLighting、FlatLight、Studio、Outdoor、Soft着色模式、材质参数内置 10 种预设主题Default、Dark、Scientific、Warm、Cool、Matplotlib、EarthTones、Ocean、HighContrast、Presentation。// 使用预设主题plot-applyTheme(Qwt3D::Qwt3DTheme::Dark);// 手动定制Qwt3D::Qwt3DThemetheme(Qwt3D::Qwt3DTheme::Scientific);theme.setDataColorPreset(plasma);// 使用 core 模块的 colormap 预设theme.setShininess(20.0);theme.apply(plot);// plot 是 Qwt3D::Plot3D* 指针ColorMapColor适配器桥接了 core 模块的QwtColorMap到 3D 的Qwt3D::Color接口使得 22 种科学 colormap 预设可以直接用于 3D 表面图。这正是三库架构带来的复用优势——core 定义 colormapplot 和 plot3d 各自消费。七、CMake 构建系统Qwt 7.0 全面迁移到 CMake放弃了 qmake。这是现代化的关键一步。7.1 find_package 一键引入安装 Qwt 后在你的项目中只需# 指定 Qwt 的安装目录如果非默认路径 set(qwt_DIR C:/path/to/install/lib/cmake/qwt) # 一行加载 find_package(qwt REQUIRED) # 按需链接 target_link_libraries(your_app PRIVATE qwt::plot) # 只用 2D # 或者 target_link_libraries(your_app PRIVATE qwt::plot3d) # 只用 3D # 或者 target_link_libraries(your_app PRIVATE qwt::plot qwt::plot3d) # 2D 3D链接qwt::plot会自动传递qwt::core及其 Qt 依赖Core、Gui、Widgets链接qwt::plot3d会自动传递qwt::core、Qt OpenGL 系列依赖和OpenGL::GLU。你不需要手动管理任何传递依赖。7.2 三个独立的 CMake target每个库都创建了命名空间别名使用现代 CMake 的ALIAS机制# core 模块 add_library(${QWT_LIB_NAME}::${QWTCORE_LIB_NAME} ALIAS ${QWTCORE_LIB_NAME}) # plot 模块 add_library(${QWT_LIB_NAME}::${QWTPLOT_LIB_NAME} ALIAS ${QWTPLOT_LIB_NAME}) # plot3d 模块 add_library(${QWT_LIB_NAME}::${QWTPLOT3D_LIB_NAME} ALIAS ${QWTPLOT3D_LIB_NAME})用户始终通过qwt::core、qwt::plot、qwt::plot3d这三个带命名空间的 target 来引用符合现代 CMake 的最佳实践。7.3 条件编译选项CMake 选项默认控制内容QWT_CONFIG_QWTPLOTON核心绘图QwtPlot、曲线、网格、缩放等QWT_CONFIG_QWTPOLARON极坐标绘图子模块QWT_CONFIG_QWTWIDGETSON控件滑块、旋钮、刻度盘、温度计等QWT_CONFIG_QWTSVGONSVG 导出/渲染QWT_CONFIG_QWTOPENGLONOpenGL 画布QWT_CONFIG_QWTPLOT_3DON3D 绘图模块QWT_CONFIG_BUILD_EXAMPLEON构建示例程序QWT_CONFIG_BUILD_PLAYGROUNDON构建实验性代码QWT_CONFIG_BUILD_STATIC_EXAMPLEON构建静态链接示例QWT_CONFIG_BUILD_TESTSOFF测试构建例如只构建核心库不编译示例和实验代码cmake -S . -B build -G Visual Studio 16 2019 -A x64 ^ -DCMAKE_PREFIX_PATHD:/Qt/6.7.3/msvc2019_64 ^ -DQWT_CONFIG_BUILD_EXAMPLEOFF ^ -DQWT_CONFIG_BUILD_PLAYGROUNDOFF7.4 与 qmake 的对比对比项qmake (Qwt 6.x)CMake (Qwt 7.0)包发现手动配置 include/lib 路径find_package(qwt)标准 CMake 机制依赖传递无自动传递PUBLIC/PRIVATE 依赖自动传递条件编译.pro文件中define标准 CMake option跨平台需多套 .pro单一 CMakeLists.txtIDE 支持Qt Creator 为主CLion / VS / Qt Creator / VSCode 全支持八、PIMPL 模式Qwt 7.0 在 PIMPLPointer to Implementation模式上有一个值得注意的设计它没有使用 Qt 标准的Q_DECLARE_PRIVATE宏而是自定义了一套宏。8.1 自定义宏体系这套宏定义在qwt_global.h中// 头文件声明 PIMPLclassQWT_EXPORTQwtFoo:publicQWidget{QWT_DECLARE_PRIVATE(QwtFoo)// → class PrivateData; unique_ptrPrivateData m_data;};// 源文件实现 PrivateDataclassQwtFoo::PrivateData{QWT_DECLARE_PUBLIC(QwtFoo)// → QwtFoo* q_ptr;};// 构造函数初始化QwtFoo::QwtFoo():QWT_PIMPL_CONSTRUCT{}// → m_data(qwt_make_uniquePrivateData(this))// 访问私有时使用QWT_D(d);// PrivateData* d d_func()QWT_DC(d);// const PrivateData* d d_func()8.2 与 Qt 标准 PIMPL 的区别Qt 标准的Q_DECLARE_PRIVATE使用的是间接指针d_ptr指向QObjectPrivate子类而 Qwt 的自定义方案有一个关键区别m_data直接存储PrivateData通过unique_ptr不使用堆指针重定向非_const_ 方法通过QWT_D()访问私有数据const方法通过QWT_DC()访问私有数据这种设计简化了私有数据的访问路径同时在 const 正确性上更加严格——QWT_DC确保 const 方法只能 const 访问私有数据。九、Qt5/Qt6 兼容层Qwt 7.0 同时支持 Qt 5.12 和 Qt 6.x。为了优雅地处理两个版本之间的 API 差异core 模块提供了qwt::compat命名空间下的兼容层定义在qwt_qt5qt6_compat.hpp中。9.1 核心兼容函数// 鼠标/触摸事件位置// Qt5: event-pos() → QPoint// Qt6: event-position().toPoint() → QPointqwt::compat::eventPos(event);// 滚轮事件增量// Qt5: event-delta() → int// Qt6: event-angleDelta().y() → intqwt::compat::wheelEventDelta(event);// 字体度量文本宽度// Qt5.12-: fm.width(str) → int// Qt5.12: fm.horizontalAdvance(str) → intqwt::compat::horizontalAdvance(fm,str);9.2 设计理念这套兼容层的理念是一处封装处处使用。所有与 Qt 版本相关的 API 差异都集中在qwt::compat命名空间中处理业务代码只调用兼容函数不需要写任何#if QT_VERSION条件编译。对比一下没有兼容层时的写法// 没有兼容层 —— 到处都是条件编译QPoint pos;#ifQT_VERSIONQT_VERSION_CHECK(6,0,0)posevent-position().toPoint();#elseposevent-pos();#endif// 有兼容层 —— 一行搞定QPoint posqwt::compat::eventPos(event);这使得代码可读性大幅提升也降低了维护成本。当未来需要支持新的 Qt 版本时只需修改兼容层即可。十、与 Qwt 6.x 架构对比总结对比维度Qwt 6.xQwt 7.0库结构单体库一个 .so/.dll三库core plot plot3d构建系统qmake (.pro)CMake (CMakeLists.txt)3D 绘图需独立引入 QwtPlot3D 库内置 plot3d 模块统一管理包引入手动配置路径find_package(qwt)标准机制Qt 版本主要 Qt5Qt 5.12 / Qt 6.x 双支持兼容方案#if QT_VERSION散落各处qwt::compat命名空间统一封装PIMPL 模式标准 QtQ_DECLARE_PRIVATE自定义宏QWT_DECLARE_PRIVATE/QWT_D/QWT_DC颜色映射基础 colormap 类core 模块 22 种科学预设 3D 适配器性能优化无SIMD 加速 argmin/argmaxSSE2/AVX2/NEON矢量导出(3D)依赖外部 gl2ps内置 gl2ps 回退开箱即用条件编译.pro 中的 define标准 CMake option粒度更细十一、总结Qwt 7.0 的三库模块化架构是一次深思熟虑的重构。它不是简单地把代码拆成三份而是重新梳理了模块间的依赖关系core作为基础工具库提供颜色、数学、数据类型、坐标变换等通用能力被 plot 和 plot3d 共享plot专注于 2D 绘图按需引入 OpenGL 和 SVGplot3d整合了原 QwtPlot3D内置 gl2ps 回退通过ColorMapColor适配器复用 core 的 colormap 预设配合 CMake 构建系统、qwt::compat兼容层和自定义 PIMPL 宏Qwt 7.0 在保持 API 兼容性的同时实现了现代化的工程架构。无论你是只需要简单的 2D 曲线还是需要完整的 2D 3D 科学可视化方案都可以通过find_package(qwt)按需引入不多不少。这一架构为后续的性能优化和功能扩展打下了坚实基础。在后续系列文章中我们将深入各个模块的具体实现细节。系列文章系列总述Qwt 7.0 —— 基于 Qt 的高性能 2D/3D 绘图库第 1 篇快速入门与核心新特性概览第 2 篇曲线绘图详解 —— 从基础到百万级数据性能优化第 3 篇常用图表类型实战 —— 柱状图、散点图、箱线图与直方图第 4 篇高级科学图表 —— 光谱图、向量场、K线图与极坐标绘图第 5 篇多坐标轴与多绘图布局 —— 寄生绘图与 QwtFigure 容器第 6 篇交互功能详解 —— 平移、缩放、坐标轴交互与数据拾取第 7 篇3D 数据可视化 —— OpenGL 高性能三维绘图第 8 篇坐标轴与刻度系统 —— 刻度引擎、网格、图例与刻度朝内第 9 篇控件与辅助元素 —— 滑块旋钮、标记与装饰第 10 篇总体架构解析 —— 从单体到三库模块化的演进第 11 篇matplotlib 风格绘图 —— QwtPyPlot 接口详解相关链接项目地址https://github.com/czyt1988/QWTGitee 镜像https://gitee.com/czyt1988/QWT在线文档https://czyt1988.github.io/QWT/zh/系列总述https://blog.csdn.net/czyt1988/article/details/160193393