【Qwt 7.0 系列】总体架构解析 —— 从单体到三库模块化的演进
2026/7/5 2:47:19 网站建设 项目流程

【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_EXPORT
2D 绘图qwt::plotqwtplot.dll/libqwtplot.soQWT_EXPORT
3D 绘图qwt::plot3dqwtplot3d.dll/libqwtplot3d.soQWT3D_EXPORT

3.2 依赖关系

三个库的依赖关系非常清晰:

┌──────────────────────────┐ │ qwt::core │ ← 基础工具库(颜色、数学、数据类型、几何、变换、时间等) └──────────────────────────┘ ↗ ↖ ┌──────────────┐ ┌───────────────┐ │ qwt::plot │ │ qwt::plot3d │ │ (2D) │ │ (3D) │ └──────────────┘ └───────────────┘

核心原则:plotplot3d都依赖core,但彼此互不依赖

这意味着什么?如果你只做 2D 绘图,链接qwt::plot就够了,完全不会引入任何 OpenGL / 3D 相关的代码和依赖。反之亦然。这种解耦设计让依赖关系最小化,编译和部署都更加轻量。

3.3 各模块的 Qt 依赖

模块Qt 依赖(public)Qt 依赖(private)可选依赖
coreCore,Gui
plotCore,Gui,WidgetsConcurrent,PrintSupportOpenGL,OpenGLWidgets(Qt6),Svg
plot3dCore,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 库的好处是显而易见的:

  1. 代码复用:plot 和 plot3d 共享同一套颜色映射、坐标变换、数据类型,避免重复代码
  2. 按需编译:如果有人只需要 Qwt 的数学工具或颜色映射(比如用于自定义绘图),可以只链接 core,不需要引入整个 2D/3D 绘图栈
  3. 关注点分离:core 不依赖任何 Widgets,可以用于无 GUI 的后端计算场景

举个例子,core 中的QwtColorMapPreset提供了 22 种科学 colormap 预设(viridis、plasma、jet、hot 等),这些预设既可用于 2D 的QwtPlotSpectrogram,也可通过ColorMapColor适配器用于 3D 表面图。一个预设定义,两处复用。

4.3 SIMD 加速

core 模块中的qwt_simd_argminmax.h是一个值得关注的性能优化点。它利用 SSE2 / AVX2(x86)和 NEON(ARM)指令集来加速大规模数据的 argmin/argmax 运算。在处理百万级数据点的曲线时,这一优化可以显著减少渲染延迟。

五、Plot 模块详解

qwt::plot是 2D 绘图的核心模块,承载了原版 Qwt 的全部 2D 绘图能力。

5.1 核心功能

plot 模块包含但不限于:

  • QwtPlot:主绘图容器,支持多轴、寄生绘图(Parasite Plot)
  • 曲线系列:QwtPlotCurveQwtPlotSpectrogramQwtPlotTradingCurve
  • 交互工具:缩放器、平移器、拾取器(Picker)
  • 刻度绘制:QwtScaleDrawQwtDateScaleDraw
  • 图形元素:QwtGraphicQwtColumnSymbolQwtIntervalSymbol
  • 布局管理:QwtDynGridLayout
  • 放大镜:QwtMagnifier

5.2 链接方式

plot 模块通过 PUBLIC 链接依赖 core:

target_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::OpenGL(Qt6 还需Qt::OpenGLWidgets),允许使用 GPU 加速渲染高频数据
  • QWT_CONFIG_QWTSVG(默认 ON):启用 SVG 图像显示和导出,额外依赖Qt::Svg

如果你不需要这些功能,可以在 CMake 配置时关闭以减少依赖:

cmake -S . -B build -DQWT_CONFIG_QWTOPENGL=OFF -DQWT_CONFIG_QWTSVG=OFF

六、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()

这段逻辑确保了:无论目标系统是否预装了 gl2ps,3D 矢量导出功能都能正常工作。系统有就用系统的,没有就编译内置的。这是"开箱即用"理念的典型实践。

6.3 Qwt3D 主题系统

plot3d 模块引入了Qwt3D::Qwt3DTheme主题系统,封装了 3D 绘图的全部视觉属性:

  • 背景色、网格色/线宽
  • 数据 colormap
  • 坐标轴颜色、标题样式
  • 光照预设:NoLightingFlatLightStudioOutdoorSoft
  • 着色模式、材质参数

内置 10 种预设主题:DefaultDarkScientificWarmCoolMatplotlibEarthTonesOceanHighContrastPresentation

// 使用预设主题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 定义 colormap,plot 和 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::coreqwt::plotqwt::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_PATH="D:/Qt/6.7.3/msvc2019_64" ^ -DQWT_CONFIG_BUILD_EXAMPLE=OFF ^ -DQWT_CONFIG_BUILD_PLAYGROUND=OFF

7.4 与 qmake 的对比

对比项qmake (Qwt 6.x)CMake (Qwt 7.0)
包发现手动配置 include/lib 路径find_package(qwt)标准 CMake 机制
依赖传递无自动传递PUBLIC/PRIVATE 依赖自动传递
条件编译.pro文件中define标准 CMake option
跨平台需多套 .pro单一 CMakeLists.txt
IDE 支持Qt Creator 为主CLion / VS / Qt Creator / VSCode 全支持

八、PIMPL 模式

Qwt 7.0 在 PIMPL(Pointer to Implementation)模式上有一个值得注意的设计:它没有使用 Qt 标准的Q_DECLARE_PRIVATE,而是自定义了一套宏。

8.1 自定义宏体系

这套宏定义在qwt_global.h中:

// 头文件:声明 PIMPLclassQWT_EXPORTQwtFoo:publicQWidget{QWT_DECLARE_PRIVATE(QwtFoo)// → class PrivateData; unique_ptr<PrivateData> m_data;};// 源文件:实现 PrivateDataclassQwtFoo::PrivateData{QWT_DECLARE_PUBLIC(QwtFoo)// → QwtFoo* q_ptr;};// 构造函数初始化QwtFoo::QwtFoo():QWT_PIMPL_CONSTRUCT{}// → m_data(qwt_make_unique<PrivateData>(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_VERSION>=QT_VERSION_CHECK(6,0,0)pos=event->position().toPoint();#elsepos=event->pos();#endif// 有兼容层 —— 一行搞定QPoint pos=qwt::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/argmax(SSE2/AVX2/NEON)
矢量导出(3D)依赖外部 gl2ps内置 gl2ps 回退,开箱即用
条件编译.pro 中的 define标准 CMake option,粒度更细

十一、总结

Qwt 7.0 的三库模块化架构是一次深思熟虑的重构。它不是简单地把代码拆成三份,而是重新梳理了模块间的依赖关系:

  • core作为基础工具库,提供颜色、数学、数据类型、坐标变换等通用能力,被 plot 和 plot3d 共享
  • plot专注于 2D 绘图,按需引入 OpenGL 和 SVG
  • plot3d整合了原 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/QWT
  • Gitee 镜像:https://gitee.com/czyt1988/QWT
  • 在线文档:https://czyt1988.github.io/QWT/zh/
  • 系列总述:https://blog.csdn.net/czyt1988/article/details/160193393

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询