【Qwt 7.0 系列】交互功能详解 —— 平移、缩放、坐标轴交互与数据拾取

发布时间:2026/7/5 2:56:17
【Qwt 7.0 系列】交互功能详解 —— 平移、缩放、坐标轴交互与数据拾取 【Qwt 7.0 系列】交互功能详解 —— 平移、缩放、坐标轴交互与数据拾取本文是 Qwt 7.0 系列介绍和教程如果你正在寻找一个高性能、协议友好、同时支持 2D 和 3D 绘图的 Qt 数据可视化库那么这篇文章就是为你准备的。系列总述文章Qwt 7.0 —— 基于 Qt 的高性能 2D/3D 绘图库概述 | 高性能曲线绘制 | 常用图表类型 | 高级科学图表 | 多坐标轴与布局 | 交互功能 | 3D 数据可视化 | 坐标轴与刻度 | 控件与辅助元素 | 总体架构解析 | matplotlib 风格绘图项目地址GitHub | Gitee | 在线文档引言交互是数据可视化的关键环节。一张静态的图表只能展示数据的一个切面而通过平移、缩放、拾取等交互操作用户才能在数据海洋中自由探索发现隐藏的规律。原版 Qwt 6.2 已经提供了平移和缩放功能但随着使用场景的复杂化它暴露出一些明显的不足平移过程基于位图缓存导致体验不够流畅缩放器绑定坐标轴数量有限坐标轴无法直接操作数据拾取需要开发者自己实现等。Qwt 7.0 对交互功能进行了全面重构和增强带来了一系列令人瞩目的新特性实时平移QwtPlotPanner基于QwtPicker状态机重构平移过程中绘图实时刷新整体画布缩放QwtPlotCanvasZoomer无需指定坐标轴自动处理全部四轴坐标轴内置交互鼠标直接拖动、滚轮缩放坐标轴7.0.5 新增数据拾取QwtPlotSeriesDataPicker提供 Y 值拾取和最近点拾取7.0.6 新增本文将逐一详解这些功能。一、平移工具1.1 缓存平移的痛点原版 Qwt 6 的QwtPlotPanner采用基于缓存的平移机制平移过程中显示的是一张缓存的位图而非实时更新的绘图内容。这带来两个问题平移时看到的不是真实数据视觉上有拖动残影的感觉无法在平移过程中看到数据的实时变化虽然缓存机制减少了重绘开销但对视觉体验很不友好。1.2 实时平移QwtPlotPanner7.0 新增Qwt 7.0 对QwtPlotPanner进行了完全重构新的实现基于QwtPicker状态机机制实现了实时平移——平移过程中绘图内容实时更新新的QwtPlotPanner提供了完整的坐标轴支持线性坐标轴Linear Scale对数坐标轴Logarithmic Scale日期时间坐标轴DateTime Scale多坐标轴基本用法非常简单#includeqwt_plot_panner.h#includeqwt_plot.h#includeqwt_plot_curve.h// 创建绘图和曲线QwtPlot*plotnewQwtPlot;QwtPlotCurve*curvenewQwtPlotCurve(Sine Wave);curve-attach(plot);// 创建实时平移器QwtPlotPanner*pannernewQwtPlotPanner(plot-canvas());// 配置鼠标按钮默认中键平移panner-setMouseButton(Qt::MiddleButton);// 也可以用左键平移// panner-setMouseButton(Qt::LeftButton);// 启用平移器panner-setEnabled(true);你还可以限制平移方向只允许水平或垂直方向移动// 默认水平 垂直panner-setOrientations(Qt::Horizontal|Qt::Vertical);// 仅水平// panner-setOrientations(Qt::Horizontal);// 仅垂直// panner-setOrientations(Qt::Vertical);注意QwtPlotPanner不需要绑定坐标轴因为它针对整个画布操作移动画布时所有坐标轴都会同步移动。1.3 缓存平移QwtPlotCachePanner原 QwtPlotPanner如果你仍然想使用原来的缓存式平移Qwt 7.0 保留了它只是换了名字。以下是重要的类名变更原名称Qwt 6新名称Qwt 7.0说明QwtPannerQwtCachePanner基类更名QwtPlotPanner旧QwtPlotCachePanner绘图缓存平移器更名QwtPolarPannerQwtPolarCachePanner极坐标缓存平移器更名QwtPlotPanner新—全新重构基于QwtPicker简单来说Qwt 6 的QwtPlotPanner在 Qwt 7.0 中变成了QwtPlotCachePanner而QwtPlotPanner这个名字被新的实时平移器夺走了。接口保持不变原有代码基本无需修改即可使用QwtPlotCachePanner。二、缩放工具2.1 缩放器的两个选择在 Qwt 6 中QwtPlotZoomer需要指定两个坐标轴来进行缩放。如果你的绘图有四个坐标轴上下左右都显示数据就需要创建两个缩放器// Qwt 6 传统用法 - 需要两个缩放器来覆盖四个坐标轴QwtPlotZoomer*zoomer1newQwtPlotZoomer(QwtAxis::XBottom,QwtAxis::YLeft,canvas);QwtPlotZoomer*zoomer2newQwtPlotZoomer(QwtAxis::XTop,QwtAxis::YRight,canvas);这既繁琐又容易遗漏。Qwt 7.0 提供了两个缩放器分工明确特性QwtPlotAxisZoomerQwtPlotCanvasZoomer坐标轴绑定需要指定 X 和 Y 轴自动处理所有四个坐标轴使用场景特定坐标轴缩放整体画布缩放寄生绘图支持需手动绑定自动支持所有寄生绘图2.2 QwtPlotAxisZoomer原 QwtPlotZoomerQwtPlotAxisZoomer就是原来的QwtPlotZoomer重命名以区分新的画布缩放器。它绑定两个特定的坐标轴#includeqwt_plot_axis_zoomer.h// 创建针对特定坐标轴的缩放器QwtPlotAxisZoomer*axisZoomernewQwtPlotAxisZoomer(QwtAxis::XBottom,// X 轴QwtAxis::YLeft,// Y 轴plot-canvas(),// 画布true// 自动重绘);// 或者使用默认坐标轴XBottom YLeftQwtPlotAxisZoomer*defaultZoomernewQwtPlotAxisZoomer(plot-canvas());2.3 QwtPlotCanvasZoomer7.0 新增QwtPlotCanvasZoomer是 Qwt 7.0 新增的缩放器无需指定坐标轴自动对整个画布进行缩放。这对多坐标轴和寄生绘图场景特别友好#includeqwt_plot_canvas_zoomer.h// 创建整体画布缩放器自动处理所有坐标轴QwtPlotCanvasZoomer*canvasZoomernewQwtPlotCanvasZoomer(plot-canvas());一行代码搞定简洁明了。2.4 默认按键绑定两个缩放器的按键绑定完全一致鼠标操作操作默认按钮功能描述缩放选择左键拖拽选择矩形区域进行缩放回到基准右键点击缩放到基准视图完全缩小后退一级中键点击后退一级缩放前进一级中键Shift 点击前进一级缩放键盘操作操作默认按键功能描述前进一级前进一级缩放后退一级-后退一级缩放回到基准Escape回到基准视图2.5 自定义按键如果默认按键不满足需求可以自定义// 配置键盘快捷键zoomer-setKeyPattern(QwtEventPattern::KeyRedo,Qt::Key_Plus,Qt::ShiftModifier);// 前进zoomer-setKeyPattern(QwtEventPattern::KeyUndo,Qt::Key_Minus,Qt::ShiftModifier);// 后退zoomer-setKeyPattern(QwtEventPattern::KeyHome,Qt::Key_Escape);// 重置// 配置鼠标模式中键重置右键回退zoomer-setMousePattern(QwtEventPattern::MouseSelect2,Qt::MiddleButton);zoomer-setMousePattern(QwtEventPattern::MouseSelect3,Qt::RightButton);三、坐标轴内置交互动作7.0.5 新增3.1 直接操作坐标轴这是一个非常实用的新特性。在 Qwt 6 及更早版本中用户无法直接操作坐标轴——所有的平移和缩放都发生在画布上。从Qwt 7.0.5开始你可以用鼠标直接拖动坐标轴来平移用滚轮在坐标轴上缩放。这个功能的设计灵感来源于 QCustomPlot 的交互机制。启用后用户可以在任意坐标轴上左键单击选中坐标轴左键拖动平移坐标轴范围绘图区域实时更新滚轮缩放缩放中心位于鼠标当前位置右键单击取消选中状态坐标轴拖动效果坐标轴滚轮缩放效果3.2 启用与配置坐标轴交互功能通过QwtPlotScaleEventDispatcher类实现事件分发但用户通常无需直接接触这个类只需调用QwtPlot::setEnableScaleBuildinActions即可// Qwt 7 默认启用坐标轴交互// 如需手动控制plot-setEnableScaleBuildinActions(true);// 启用// plot-setEnableScaleBuildinActions(false); // 禁用3.3 单轴自定义你可以针对特定坐标轴配置不同的交互行为。QwtScaleWidget使用BuiltinActions枚举来定义内置动作// 仅让 XBottom 轴支持拖动不支持滚轮缩放plot-axisWidget(QwtAxis::XBottom)-setBuildinActions(QwtScaleWidget::ActionClickPan);// 查询当前配置autoflagsplot-axisWidget(QwtAxis::XBottom)-buildinActions();// 检查某个动作是否激活boolcanPanplot-axisWidget(QwtAxis::XBottom)-testBuildinActions(QwtScaleWidget::ActionClickPan);3.4 选中效果自定义坐标轴被选中时默认文字和轴线颜色变为蓝色。你可以自定义这个选中颜色以及选中后轴线粗细的变化// 设置选中颜色默认蓝色plot-axisWidget(QwtAxis::XBottom)-setSelectionColor(QColor(255,100,0));// 设置选中后画笔宽度偏移默认 1即选中后线条加粗 1 像素plot-axisWidget(QwtAxis::XBottom)-setSelectedPenWidthOffset(2);效果如下图所示选中坐标轴后轴线变粗、颜色变化四、数据拾取QwtPlotSeriesDataPicker7.0.6 新增4.1 为什么需要数据拾取数据拾取是绘图控件的常见需求用户移动鼠标时实时显示当前位置对应的数据值。在 Qwt 6 中这个功能需要开发者自己实现涉及坐标转换、数据查找、文本绘制等繁琐工作。从Qwt 7.0.6开始Qwt 新增了QwtPlotSeriesDataPicker类开箱即用支持两种拾取模式、线性插值、多曲线、高性能优化等特性核心特性一览双模式拾取Y 值拾取 最近点拾取智能插值线性插值计算数据点之间也能精确取值高性能优化二分查找 窗口搜索算法支持大数据集多曲线支持同时拾取多条曲线的数据自定义显示文本样式、特征点绘制、背景均可定制寄生绘图支持支持宿主绘图和寄生绘图的数据获取4.2 基本用法#includeqwt_plot_series_data_picker.h// 创建绘图对象QwtPlot*plotnewQwtPlot(this);// 创建数据拾取器传入 canvas 即可QwtPlotSeriesDataPicker*pickernewQwtPlotSeriesDataPicker(plot-canvas());// 设置拾取模式picker-setPickMode(QwtPlotSeriesDataPicker::PickYValue);// 启用线性插值picker-setInterpolationMode(QwtPlotSeriesDataPicker::LinearInterpolation);// 设置文本显示位置自动选择picker-setTextArea(QwtPlotSeriesDataPicker::TextPlaceAuto);4.3 Y 值拾取模式Y 值拾取模式显示当前 X 位置对应所有曲线的 Y 值。当鼠标在画布上移动时拾取器会画一条垂直辅助线并在每条曲线上标记对应 Y 值的位置// Y 值拾取模式默认picker-setPickMode(QwtPlotSeriesDataPicker::PickYValue);插值是该模式的亮点。当数据点比较稀疏时拾取器会通过线性插值计算出当前 X 轴位置对应的 Y 值而非简单取最近点。插值默认开启也可以关闭// 关闭插值使用最近的数据点picker-setInterpolationMode(QwtPlotSeriesDataPicker::NoInterpolation);4.4 最近点拾取模式最近点拾取模式计算距离鼠标最接近的数据点并显示。这个模式特别适合频谱图等需要拾取峰值的场景// 最近点拾取模式picker-setPickMode(QwtPlotSeriesDataPicker::PickNearestPoint);4.5 性能优化窗口搜索算法最近点拾取需要计算曲线每个点到鼠标的距离全曲线遍历会非常耗时。为此Qwt 7.0 提供了窗口搜索算法// 设置搜索窗口大小voidsetNearestSearchWindowSize(intwindowSize);intnearestSearchWindowSize()const;窗口大小支持三种设置方式设置值含义0不使用窗口搜索整个曲线正数固定窗口大小数据点数量负数自适应窗口取绝对值百分比如 -5 表示曲线点数的 5%默认值为-5即曲线点数的 5%。需要注意的是窗口优化算法默认在曲线点数超过 1000 时才生效点数较少时直接全量搜索。此外窗口优化算法要求曲线数据按 X 坐标升序排列。使用自定义数据源时请确保数据已排序。4.6 自定义显示文本通过继承QwtPlotSeriesDataPicker并重写valueString方法可以自定义数据点的显示格式classCustomDataPicker:publicQwtPlotSeriesDataPicker{public:explicitCustomDataPicker(QWidget*canvas):QwtPlotSeriesDataPicker(canvas){}protected:QStringvalueString(constQPointFvalue,QwtPlotItem*item,size_t seriesIndex,intorder)constoverride{Q_UNUSED(seriesIndex);if(pickMode()PickYValue){QString text;if(order!0){text\n;// 非第一个点需要换行}textQString(%1: %2 (X%3)).arg(item-title().text()).arg(value.y(),0,f,3).arg(value.x(),0,f,3);returntext;}returnQString(坐标: (%1, %2)).arg(value.x()).arg(value.y());}};valueString的order参数表示当前是第几个特征点多条曲线时递增可根据它是否为 0 决定是否换行。4.7 特征点与文本样式拾取到的数据点称为特征点会在曲线上标记出来// 启用特征点标记picker-setEnableDrawFeaturePoint(true);// 设置特征点大小像素picker-setDrawFeaturePointSize(6);// 自定义文本背景picker-setTextBackgroundBrush(QBrush(QColor(255,255,255,180)));// 设置文本对齐方式picker-setTextAlignment(Qt::AlignLeft|Qt::AlignTop);4.8 点击信号与数据获取QwtPlotSeriesDataPicker提供了点击信号方便响应用户操作// 单击信号voidclicked(QwtPlotSeriesDataPicker*picker,constQPointpos);// 双击信号voiddoubleClicked(QwtPlotSeriesDataPicker*picker,constQPointpos);点击信号仅响应鼠标左键。双击时会先触发clicked()再触发doubleClicked()这是 Qt 的标准行为。通过featurePoints()方法可以获取当前拾取到的数据点信息structFeaturePoint{QwtPlotItem*item{nullptr};// 对应的曲线项QPointF feature{0,0};// 特征点坐标size_t index{0};// 在曲线数据中的索引};// 获取当前拾取到的特征点列表QListFeaturePointfeaturePoints()const;使用示例connect(picker,QwtPlotSeriesDataPicker::clicked,[](QwtPlotSeriesDataPicker*p,constQPointpos){Q_UNUSED(pos);QListQwtPlotSeriesDataPicker::FeaturePointfpsp-featurePoints();for(constautofp:fps){if(fp.item){qDebug()曲线:fp.item-title().text()X:fp.feature.x()Y:fp.feature.y()索引:fp.index;}}});4.9 多子图联动QwtPlotSeriesDataPickerGroup在多子图布局中QwtPlotSeriesDataPickerGroup可以实现跨图数据联动。当用户点击某个子图时组内其他 picker 会自动同步到相同的 X 轴比例位置#includeqwt_plot_series_data_picker_group.h// 创建 Group 并添加多个 pickerQwtPlotSeriesDataPickerGroup*pickerGroupnewQwtPlotSeriesDataPickerGroup(this);pickerGroup-addPicker(picker1);pickerGroup-addPicker(picker2);pickerGroup-addPicker(picker3);// 连接 Group 的点击信号connect(pickerGroup,QwtPlotSeriesDataPickerGroup::clicked,this,[this](QwtPlotSeriesDataPicker*picker,constQPointpos){Q_UNUSED(pos);// 此时所有 picker 已同步可获取所有子图的数据for(auto*p:{picker1,picker2,picker3}){autofpsp-featurePoints();if(!fps.isEmpty()){qDebug()Y value:fps.first().feature.y();}}});Group 在发出点击信号之前会先同步所有 picker 的位置这意味着信号触发时所有 picker 的featurePoints()都已更新非常适合多子图联动检视。五、Picker 状态机简介前面多次提到QwtPlotPanner基于QwtPicker状态机实现这里简要介绍一下状态机的概念帮助理解 Qwt 7.0 交互功能的底层机制。5.1 为什么需要状态机图形交互中用户的操作往往是一个有序的事件序列而非单一动作。比如拖拽操作 按下鼠标 → 移动鼠标 → 释放鼠标。如果没有状态机管理我们需要在事件处理函数中写大量if-else分支和状态变量代码会变得难以维护。Qwt 的QwtPicker使用状态机来管理交互过程将事件序列的识别与具体业务操作分离状态机负责识别完整的事件序列按下→移动→释放QwtPicker负责执行具体的绘制、选择等操作5.2 工作原理QwtPicker的事件处理流程如下事件流: QEvent → QwtPicker → QwtPickerMachine → Commands → QwtPicker方法状态机将具体的绘图动作抽象为五种命令命令描述Begin()开始选择Append()添加点Move()移动点Remove()删除点End()结束选择状态机分析事件后返回命令序列QwtPicker依次执行这些命令。以拖拽点选择为例核心逻辑如下QListQwtPickerMachine::CommandQwtPickerDragPointMachine::transition(constQwtEventPatterneventPattern,constQEvent*event){QListQwtPickerMachine::CommandcmdList;switch(event-type()){caseQEvent::MouseButtonPress:if(state()0){// 初始状态cmdListBegin;// 开始选择cmdListAppend;// 添加第一个点setState(1);// 进入拖拽状态}break;caseQEvent::MouseMove:if(state()!0)// 在拖拽状态中cmdListMove;// 持续移动break;caseQEvent::MouseButtonRelease:if(state()!0){cmdListEnd;// 结束选择setState(0);// 回到初始状态}break;}returncmdList;}状态转换非常清晰状态 0初始→ 按下 → 状态 1拖拽→ 释放 → 状态 0。5.3 常用内置状态机Qwt 提供了多种预定义状态机满足不同的交互需求状态机用途典型场景QwtPickerTrackerMachine实时追踪鼠标位置坐标显示、悬停提示QwtPickerClickPointMachine单次点击选择一个点点选数据点、快速定位QwtPickerDragPointMachine拖拽选择点精确位置选择、实时平移QwtPickerClickRectMachine两次点击确定矩形精确矩形选择QwtPickerDragRectMachine拖拽选择矩形快速缩放、区域选择QwtPickerDragLineMachine拖拽选择线段距离测量、角度测量QwtPickerPolygonMachine多次点击创建多边形复杂区域选择、多边形绘制理解状态机的工作原理不仅有助于正确使用 Qwt 的交互组件还能在需要自定义交互模式时通过继承QwtPickerMachine快速实现。六、与旧版本的区别总结下表汇总了 Qwt 7.0 交互功能与 Qwt 6 的核心区别功能Qwt 6Qwt 7.0平移QwtPlotPanner缓存平移QwtPlotPanner实时平移重构QwtPlotCachePanner缓存平移缩放QwtPlotZoomer绑定 2 轴QwtPlotAxisZoomer绑定 2 轴QwtPlotCanvasZoomer整体画布缩放坐标轴交互不支持7.0.5 支持鼠标拖动和滚轮缩放数据拾取需自行实现7.0.6 内置QwtPlotSeriesDataPicker多子图联动无QwtPlotSeriesDataPickerGroup支持同步类名变更速查重要旧名Qwt 6新名Qwt 7.0说明QwtPannerQwtCachePanner缓存平移基类更名QwtPlotPannerQwtPlotCachePanner缓存平移器更名QwtPolarPannerQwtPolarCachePanner极坐标缓存平移器更名QwtPlotZoomerQwtPlotAxisZoomer轴缩放器更名迁移提示如果你从 Qwt 6 迁移到 7.0只需将旧的QwtPlotPanner替换为QwtPlotCachePanner将QwtPlotZoomer替换为QwtPlotAxisZoomer接口保持不变。或者直接使用新的实时平移QwtPlotPanner和整体画布缩放QwtPlotCanvasZoomer体验更佳。总结Qwt 7.0 的交互功能相比 Qwt 6 有了质的提升实时平移让数据探索更流畅告别缓存残影整体画布缩放简化了多坐标轴场景的配置坐标轴内置交互让用户可以直接操作坐标轴交互更直观数据拾取器开箱即用支持双模式、插值、多子图联动大幅减少开发工作量底层的Picker 状态机机制让自定义交互也变得清晰可控类名变更是迁移时最需要注意的地方但接口保持不变迁移成本很低。如果你是新项目直接使用 7.0 的新组件即可获得最佳体验。系列文章系列总述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