【Qwt 7.0 系列】坐标轴与刻度系统 ------ 刻度引擎、网格、图例与刻度朝内
本文是 Qwt 7.0 系列介绍和教程,如果你正在寻找一个高性能、协议友好、同时支持 2D 和 3D 绘图的 Qt 数据可视化库,那么这篇文章就是为你准备的。
系列总述文章:Qwt 7.0 ------ 基于 Qt 的高性能 2D/3D 绘图库
概述 | 高性能曲线绘制 | 常用图表类型 | 高级科学图表 | 多坐标轴与布局 | 交互功能 | 3D 数据可视化 | 坐标轴与刻度 | 控件与辅助元素 | 总体架构解析 | matplotlib 风格绘图
引言
坐标轴是数据可视化的骨架。无论你的曲线画得多漂亮,如果坐标轴的刻度不合理、标签看不清、网格太杂乱,读者就无法准确理解数据。Qwt 7.0 作为基于原版 Qwt 6.2.0 的现代化维护分支,提供了一套强大的刻度引擎和灵活的坐标轴配置系统,能够应对从简单的线性图表到复杂的时间序列、对数坐标等各种场景。
本篇是 Qwt 7.0 系列博客的第 8 篇,将全面介绍 Qwt 7.0 的坐标轴与刻度系统,包括:
- 刻度引擎:线性、对数、日期时间三种引擎的原理与用法
- 坐标轴控件:刻度线配置、标签格式化、轴标题与外观
- 网格线:主/次网格的独立控制与样式配置
- 图例系统:外部图例与内嵌图例的使用
- 刻度朝内显示:Qwt 7.2.1+ 新增的紧凑布局功能
一、刻度引擎 QwtScaleEngine
刻度引擎是坐标轴系统的"大脑"。它负责根据数据范围自动计算合理的刻度位置------比如数据范围是 0~100,引擎会自动选择 0、20、40、60、80、100 作为主刻度,而不是 0、17、34 这样的奇怪数字。
1.1 三种内置刻度引擎
Qwt 7.0 提供三种内置刻度引擎:
| 引擎类 | 适用场景 | 主刻度示例 |
|---|---|---|
QwtLinearScaleEngine |
线性数据(默认) | 0, 20, 40, 60, 80, 100 |
QwtLogScaleEngine |
大范围/指数关系数据 | 1, 10, 100, 1000 |
QwtDateScaleEngine |
时间序列数据 | 按时间级别自动选择 |
1.2 线性刻度引擎(默认)
线性刻度引擎是默认引擎,适用于大多数均匀分布的数据。你甚至不需要显式创建它------QwtPlot 默认就使用线性引擎。
cpp
#include <QwtPlot>
QwtPlot* plot = new QwtPlot();
// 默认使用线性刻度引擎,启用自动刻度计算
plot->setAxisAutoScale(QwtAxis::XBottom);
// 设置主刻度数量上限(最多10个主刻度)
plot->setAxisMaxMajor(QwtAxis::XBottom, 10);
// 设置次刻度数量上限(每个主刻度区间最多5个次刻度)
plot->setAxisMaxMinor(QwtAxis::XBottom, 5);
刻度引擎会自动在 1、2、5、10 等美观间隔中选择最合适的刻度间距,你只需要设定大致的刻度数量上限即可。
下图是刻度引擎的演示效果,展示了不同引擎和参数下的刻度表现:

1.3 对数刻度引擎
当数据跨越多个数量级时,线性刻度会让小数值"挤"在一起看不清。对数刻度引擎将坐标轴按对数分布,适合频率响应图、地震图等场景。
cpp
#include <QwtLogScaleEngine>
// 为Y轴设置对数刻度引擎
plot->setAxisScaleEngine(QwtAxis::YLeft, new QwtLogScaleEngine());
// 设置对数刻度范围(跨越6个数量级)
plot->setAxisScale(QwtAxis::YLeft, 0.001, 1000);
对数刻度的特点:
- 主刻度位于 10^n 位置(0.001, 0.01, 0.1, 1, 10, 100, 1000...)
- 次刻度位于中间位置(2, 3, 4, 5, 6, 7, 8, 9 对应倍数)
1.4 日期时间刻度引擎
处理时间序列数据时,日期刻度引擎能根据时间跨度自动选择合适的刻度级别------跨度大的用年份,跨度小的用小时甚至分钟。
cpp
#include <QwtDateScaleEngine>
#include <QwtDateScaleDraw>
#include <QwtDate>
// 设置日期刻度引擎
plot->setAxisScaleEngine(QwtAxis::XBottom, new QwtDateScaleEngine());
// 设置日期刻度绘制器,自定义不同级别的日期格式
QwtDateScaleDraw* dateDraw = new QwtDateScaleDraw();
dateDraw->setDateFormat(QwtDateScaleDraw::Month, QString("yyyy-MM")); // 月份级别显示年月
dateDraw->setDateFormat(QwtDateScaleDraw::Day, QString("MM-dd")); // 日期级别显示月日
plot->setAxisScaleDraw(QwtAxis::XBottom, dateDraw);
// 设置时间范围(QwtDate::toDouble 将 QDateTime 转为轴坐标值)
QDateTime start = QDateTime::fromString("2024-01-01", "yyyy-MM-dd");
QDateTime end = QDateTime::fromString("2024-12-31", "yyyy-MM-dd");
plot->setAxisScale(QwtAxis::XBottom,
QwtDate::toDouble(start),
QwtDate::toDouble(end));
QwtDateScaleDraw 支持的时间级别格式如下:
| 级别 | 说明 | 默认格式 |
|---|---|---|
Millisecond |
毫秒级 | hh:mm:ss.zzz |
Second |
秒级 | hh:mm:ss |
Minute |
分钟级 | hh:mm |
Hour |
小时级 | hh:mm |
Day |
日级 | MM-dd |
Week |
周级 | MM-dd |
Month |
月级 | yyyy-MM |
Year |
年级 | yyyy |
引擎会根据数据范围自动选择最合适的级别。比如数据跨度是半年,它会选 Day 或 Week 级别;如果是十年,就选 Year 级别。

1.5 自定义刻度分割
如果自动计算无法满足需求,你可以完全手动控制刻度位置:
cpp
#include <QwtScaleDiv>
// 手动创建刻度划分
QwtScaleDiv scaleDiv;
scaleDiv.setInterval(0, 100); // 设置范围
// 设置主刻度
QList<double> majorTicks;
majorTicks << 0 << 20 << 40 << 60 << 80 << 100;
scaleDiv.setTicks(QwtScaleDiv::MajorTick, majorTicks);
// 设置次刻度
QList<double> minorTicks;
for (int i = 0; i <= 100; i += 4) {
if (!majorTicks.contains(i))
minorTicks << i;
}
scaleDiv.setTicks(QwtScaleDiv::MinorTick, minorTicks);
// 应用自定义刻度划分
plot->setAxisScaleDiv(QwtAxis::XBottom, scaleDiv);
1.6 刻度引擎属性
刻度引擎还提供了一些属性来微调刻度计算行为:
| 属性 | 说明 |
|---|---|
IncludeReference |
强制包含参考值作为刻度 |
Symmetric |
强制范围对称(如 -100 ~ 100) |
Floating |
端点不强制落在刻度上 |
Inverted |
反转刻度方向 |
cpp
// 获取刻度引擎并设置属性
QwtScaleEngine* engine = plot->axisScaleEngine(QwtAxis::XBottom);
engine->setAttribute(QwtScaleEngine::IncludeReference, true);
engine->setAttribute(QwtScaleEngine::Symmetric, false);
engine->setAttribute(QwtScaleEngine::Floating, false);
// 设置边距(数据范围两端各留5%空白)
engine->setMargins(0.05);
小贴士:选择刻度引擎的简单原则------线性数据用线性引擎(默认),跨数量级数据用对数引擎,时间数据用日期引擎。特殊需求手动设置刻度划分。
二、坐标轴控件 QwtScaleWidget
QwtScaleWidget 是坐标轴的显示控件,负责绘制刻度线、刻度标签和轴标题。在 QwtPlot 中,每个坐标轴都对应一个 QwtScaleWidget 实例。
2.1 通过 QwtPlot 配置坐标轴
日常使用中,你通常通过 QwtPlot 的便捷方法来配置坐标轴,而不需要直接操作 QwtScaleWidget:
cpp
// 设置坐标轴标题
plot->setAxisTitle(QwtAxis::XBottom, "时间 (s)");
plot->setAxisTitle(QwtAxis::YLeft, "电压 (V)");
// 设置坐标轴范围
plot->setAxisScale(QwtAxis::XBottom, 0, 100);
plot->setAxisScale(QwtAxis::YLeft, -10, 10);
// 控制坐标轴可见性(隐藏顶部X轴,显示右侧Y轴)
plot->setAxisVisible(QwtAxis::XTop, false);
plot->setAxisVisible(QwtAxis::YRight, true);
plot->replot();
Qwt 7.0 使用 QwtAxis::Position 枚举标识四个基本坐标轴位置:
| 枚举值 | 说明 |
|---|---|
QwtAxis::XBottom |
底部水平轴 |
QwtAxis::XTop |
顶部水平轴 |
QwtAxis::YLeft |
左侧垂直轴 |
QwtAxis::YRight |
右侧垂直轴 |
2.2 获取并自定义坐标轴控件
需要更精细的控制时,可以通过 axisWidget() 获取底层的 QwtScaleWidget:
cpp
// 获取特定位置的坐标轴控件
QwtScaleWidget* scaleWidget = plot->axisWidget(QwtAxis::XBottom);
// 设置刻度标签字体
scaleWidget->setFont(QFont("Arial", 8));
// 自定义轴标题样式
QwtText title("时间 (s)");
title.setFont(QFont("Arial", 10, QFont::Bold));
title.setColor(Qt::darkBlue);
scaleWidget->setTitle(title);
// 设置刻度颜色
scaleWidget->setPalette(QPalette(Qt::black));
2.3 刻度标签格式化
默认情况下,刻度标签直接显示数值。如果你需要自定义格式(比如加单位、控制小数位数),可以继承 QwtScaleDraw 并重写 label() 方法:
cpp
#include <QwtScaleDraw>
// 自定义刻度绘制器
class MyScaleDraw : public QwtScaleDraw
{
public:
virtual QwtText label(double value) const override
{
// 保留1位小数
return QwtText(QString::number(value, 'f', 1));
}
};
// 应用自定义刻度绘制器
scaleWidget->setScaleDraw(new MyScaleDraw());
这个技巧非常实用。比如你想在温度轴上显示 "36.5°C",在百分比轴上显示 "85%",都可以通过重写 label() 方法实现。
2.4 颜色条(光谱图专用)
QwtScaleWidget 还支持显示颜色条,常用于光谱图(Spectrogram)等需要颜色映射的场景:
cpp
// 启用颜色条
scaleWidget->setColorBarEnabled(true);
scaleWidget->setColorBarWidth(20);
// 设置颜色映射(蓝色到红色的渐变)
QwtLinearColorMap* colorMap = new QwtLinearColorMap(Qt::blue, Qt::red);
scaleWidget->setColorMap(QwtInterval(0, 100), colorMap);
2.5 内置交互功能(Qwt 7.0 新增)
Qwt 7.0 为坐标轴控件新增了内置交互功能,支持直接在坐标轴上拖动平移和滚轮缩放:
cpp
// 启用内置平移功能(左键拖动)
scaleWidget->setBuiltInAction(QwtScaleWidget::Pan, true);
// 启用内置缩放功能
scaleWidget->setBuiltInAction(QwtScaleWidget::Zoom, true);
// 设置交互的鼠标按钮
scaleWidget->setActionButton(Qt::LeftButton); // 左键拖动平移
这是 Qwt 7.0 相比原版 Qwt 6.2.0 的一项实用改进------用户不需要编写额外的事件过滤器,就能直接在坐标轴上拖动来浏览数据。
三、网格 QwtPlotGrid
网格线帮助读者更准确地判断数据点的坐标值,是科学图表中不可或缺的辅助元素。QwtPlotGrid 是 Qwt 提供的网格绘图项,它会自动跟随坐标轴的刻度划分来绘制网格线。
3.1 基本使用
cpp
#include <QwtPlotGrid>
QwtPlotGrid* grid = new QwtPlotGrid();
// 启用主网格(默认已启用)
grid->enableX(true); // X轴主网格(垂直线)
grid->enableY(true); // Y轴主网格(水平线)
// 启用次网格(默认关闭)
grid->enableXMin(false);
grid->enableYMin(false);
// 附加到绘图
grid->attach(plot);
网格线会自动跟随坐标轴刻度的变化------当你缩放或平移绘图时,网格线会自动调整位置,无需手动维护。
3.2 网格线样式
主网格和次网格可以设置不同的样式,通常主网格更醒目,次网格更淡:
cpp
// 主网格:灰色实线
grid->setMajorPen(QPen(QColor(150, 150, 150), 1.0, Qt::SolidLine));
// 次网格:浅灰色点线
grid->setMinorPen(QPen(QColor(200, 200, 200), 0.5, Qt::DotLine));
// 也可以用快捷方法统一设置
// grid->setPen(Qt::gray, 0.5, Qt::DotLine);
小技巧 :在 Qt 中,线宽设为
0.0表示使用 1 像素的快速绘制线(cosmetic pen),对于网格线来说这能获得最细的线条效果。
3.3 完整网格配置示例
下面是一个包含网格、曲线的完整示例:
cpp
#include <QwtPlot>
#include <QwtPlotGrid>
#include <QwtPlotCurve>
#include <QwtLegend>
QwtPlot* plot = new QwtPlot();
plot->setTitle("网格配置示例");
plot->setCanvasBackground(Qt::white);
// 创建并配置网格
QwtPlotGrid* grid = new QwtPlotGrid();
grid->enableX(true);
grid->enableY(true);
grid->enableXMin(true); // 启用次网格
grid->enableYMin(true);
// 主网格用灰色实线,次网格用浅灰色点线
grid->setMajorPen(QPen(QColor(150, 150, 150), 1.0, Qt::SolidLine));
grid->setMinorPen(QPen(QColor(220, 220, 220), 0.0, Qt::DotLine));
grid->attach(plot);
// 添加一条正弦曲线
QwtPlotCurve* curve = new QwtPlotCurve("信号");
QPolygonF points;
for (int i = 0; i <= 100; i++) {
double x = i;
double y = 50 + 30 * std::sin(i * 0.1);
points << QPointF(x, y);
}
curve->setSamples(points);
curve->setPen(QPen(Qt::blue, 2.0));
curve->attach(plot);
plot->setAxisScale(QwtAxis::XBottom, 0, 100);
plot->setAxisScale(QwtAxis::YLeft, 0, 100);
plot->replot();
3.4 网格核心方法速查
| 方法 | 说明 |
|---|---|
enableX(bool) |
启用/禁用 X 轴主网格 |
enableY(bool) |
启用/禁用 Y 轴主网格 |
enableXMin(bool) |
启用/禁用 X 轴次网格 |
enableYMin(bool) |
启用/禁用 Y 轴次网格 |
setMajorPen() |
设置主网格画笔 |
setMinorPen() |
设置次网格画笔 |
setXDiv() |
手动设置 X 轴刻度划分 |
setYDiv() |
手动设置 Y 轴刻度划分 |
四、图例 QwtLegend
图例用于标识不同曲线或数据系列的含义,是多人阅读图表时的必备元素。Qwt 7.0 提供了两种图例方式:外部图例(QwtLegend)和内嵌图例(QwtPlotLegendItem)。
4.1 外部图例
外部图例独立于画布,占用绘图区域的边缘空间:
cpp
#include <QwtLegend>
// 创建并插入图例(放在右侧)
QwtLegend* legend = new QwtLegend();
plot->insertLegend(legend, QwtPlot::RightLegend);
// 其他位置选项:
// QwtPlot::LeftLegend - 左侧
// QwtPlot::BottomLegend - 底部
// QwtPlot::TopLegend - 顶部
// 第三个参数控制图例占比(0.0~1.0)
// plot->insertLegend(legend, QwtPlot::RightLegend, 0.2); // 占20%宽度
下图是图例演示效果,展示了多条曲线的图例显示:

4.2 图例交互
默认情况下,点击图例项可以切换对应曲线的显示/隐藏状态,这是非常实用的交互功能:
cpp
// 可点击模式(默认)------点击切换曲线显示
legend->setItemMode(QwtLegend::ClickableItem);
// 只读模式------仅展示,不可点击
// legend->setItemMode(QwtLegend::ReadOnlyItem);
4.3 绘图项的图例属性
每条曲线可以控制自己在图例中的显示方式:
cpp
QwtPlotCurve* curve = new QwtPlotCurve("曲线A");
// 控制图例图标显示内容
curve->setLegendAttribute(QwtPlotCurve::LegendShowLine, true); // 显示线条
curve->setLegendAttribute(QwtPlotCurve::LegendShowSymbol, true); // 显示符号
curve->setLegendAttribute(QwtPlotCurve::LegendShowBrush, true); // 显示填充
// 设置图例图标大小
curve->setLegendIconSize(QSize(20, 15));
4.4 内嵌图例
如果不想让图例占用绘图边缘的空间,可以使用 QwtPlotLegendItem 将图例直接绘制在画布上:
cpp
#include <QwtPlotLegendItem>
// 创建内嵌图例(作为绘图项附加到画布)
QwtPlotLegendItem* legendItem = new QwtPlotLegendItem();
legendItem->attach(plot);
// 设置位置
legendItem->setPosition(QwtPlotLegendItem::TopRight); // 右上角
// 设置背景和边框
legendItem->setBackgroundBrush(QBrush(Qt::white));
legendItem->setBorderPen(QPen(Qt::gray, 1));
// 设置最大列数
legendItem->setMaxColumns(1);
内嵌图例的好处是它"浮"在画布上方,不占用布局空间,适合空间紧凑的场景。
4.5 图例使用建议
| 曲线数量 | 推荐方案 |
|---|---|
| 少量(<5条) | 右侧外部图例 |
| 中等(5-10条) | 底部外部图例 |
| 大量(>10条) | 内嵌图例或分组显示 |
五、刻度朝内显示(7.2.1+ 新增)
这是 Qwt 7.0 系列中一个虽小但实用的功能。从 7.2.1 版本开始,Qwt 支持将坐标轴刻度线显示在绘图区域内部,而非传统的朝外显示。
5.1 什么是刻度朝内
传统绘图中,刻度线从轴线向外延伸。而刻度朝内功能将刻度线从画布边缘向内延伸,刻度标签仍然保持在外部:
刻度朝外(默认): 刻度朝内:
────┼────┼────┼────→ ────┼────┼────┼────→
│ │ │ │ │ │ ← 向内延伸
0 2 4 0 2 4
┌────────────────┐
│ ← 刻度伸入画布 │
└────────────────┘
5.2 使用方法
API 非常简洁,一个方法搞定:
cpp
#include <QwtPlot>
// 设置 YLeft 轴刻度朝内
plot->setAxisTickDirection(QwtAxis::YLeft, QwtPlot::TickInside);
// 设置 XBottom 轴刻度朝内
plot->setAxisTickDirection(QwtAxis::XBottom, QwtPlot::TickInside);
// 刷新显示
plot->replot();
枚举值说明:
| 枚举值 | 说明 |
|---|---|
QwtPlot::TickOutside |
刻度朝外(默认行为) |
QwtPlot::TickInside |
刻度朝内,从画布边缘向内延伸 |
下图展示了刻度朝内的实际效果:

5.3 独立控制与批量设置
每个坐标轴可以独立设置刻度方向,也可以批量设置:
cpp
// 所有轴刻度朝内
plot->setAxisTickDirection(QwtAxis::YLeft, QwtPlot::TickInside);
plot->setAxisTickDirection(QwtAxis::YRight, QwtPlot::TickInside);
plot->setAxisTickDirection(QwtAxis::XBottom, QwtPlot::TickInside);
plot->setAxisTickDirection(QwtAxis::XTop, QwtPlot::TickInside);
plot->replot();
// 查询当前设置
QwtPlot::TickDirection dir = plot->axisTickDirection(QwtAxis::YLeft);
if (dir == QwtPlot::TickInside) {
qDebug() << "当前刻度朝内";
}
5.4 适用场景
刻度朝内功能特别适合以下场景:
- 紧凑布局:在有限的窗口空间中最大化绘图区域
- 出版物图表:很多学术期刊偏好刻度朝内的风格
- 嵌入式界面:屏幕空间宝贵的嵌入式设备界面
注意:朝内刻度自动继承朝外刻度的样式设置(长度、线宽、颜色),无需重复配置。该功能还可与寄生轴(Parasite Plot)配合使用,每个寄生绘图可独立设置刻度方向。
六、与旧版本的区别
如果你从原版 Qwt 6.2.0 迁移到 Qwt 7.0,坐标轴系统有几个重要变化需要了解:
6.1 QwtAxisId 多轴架构(7.0 新增)
原版 Qwt 6.2.0 的坐标轴系统只支持固定四个轴(yLeft、yRight、xBottom、xTop),每个位置只能有一个轴。Qwt 7.0 引入了 QwtAxisId 架构,每个轴由 Position + 序号 组成,支持在同一位置放置任意多个坐标轴。
通过 QwtPlot::createParasitePlot() 可以创建寄生绘图,共享宿主画布区域但拥有独立的坐标轴。这意味着你可以同时在左侧放两个、三个甚至更多 Y 轴,彻底突破了原版"四轴限制"。
6.2 刻度朝内显示(7.2.1+ 新增)
原版 Qwt 6.2.0 不支持刻度朝内显示,刻度线只能朝外。Qwt 7.2.1+ 通过 setAxisTickDirection() 方法新增了这一功能。
6.3 坐标轴内置交互(7.0 新增)
原版 Qwt 6.2.0 的坐标轴控件不支持直接交互。Qwt 7.0 为 QwtScaleWidget 新增了内置平移和缩放功能,用户可以直接在坐标轴上拖动浏览数据。
6.4 兼容性
Qwt 7.0 保留了旧版的 Axis 枚举兼容(通过 QWT_AXIS_COMPAT 宏),原来的 QwtPlot::yLeft 等写法仍然可用,迁移成本很低。
| 特性 | 原版 Qwt 6.2.0 | Qwt 7.0+ |
|---|---|---|
| 坐标轴数量 | 固定4个 | 任意多轴(QwtAxisId) |
| 刻度朝内 | 不支持 | 7.2.1+ 支持 |
| 坐标轴交互 | 无 | 内置平移/缩放 |
| 旧枚举兼容 | - | 通过 QWT_AXIS_COMPAT 宏 |
七、总结
本篇全面介绍了 Qwt 7.0 的坐标轴与刻度系统:
-
刻度引擎 是坐标轴的核心------线性引擎适合常规数据,对数引擎适合大范围数据,日期引擎适合时间序列。特殊需求可以手动创建
QwtScaleDiv完全控制刻度位置。 -
坐标轴控件提供了从标题、字体到刻度标签格式的全面自定义能力。Qwt 7.0 还新增了坐标轴内置交互功能。
-
网格线自动跟随刻度变化,主/次网格可独立控制样式,是提升图表可读性的利器。
-
图例系统支持外部图例和内嵌图例两种方式,点击图例项可切换曲线显示状态。
-
刻度朝内显示 是 Qwt 7.2.1+ 的新功能,通过简单的
setAxisTickDirection()调用即可实现紧凑布局效果。 -
QwtAxisId 多轴架构是 Qwt 7.0 的核心改进之一,突破了原版四轴限制,支持任意多轴。
系列文章
- 第 1 篇:快速入门与核心新特性概览
- 第 2 篇:曲线绘图详解 ------ 从基础到百万级数据性能优化
- 第 3 篇:常用图表类型实战 ------ 柱状图、散点图、箱线图与直方图
- 第 4 篇:高级科学图表 ------ 光谱图、向量场、K线图与极坐标绘图
- 第 5 篇:多坐标轴与多绘图布局 ------ 寄生绘图与 QwtFigure 容器
- 第 6 篇:交互功能详解 ------ 平移、缩放、坐标轴交互与数据拾取
- 第 7 篇:3D 数据可视化 ------ OpenGL 高性能三维绘图
- 第 8 篇:坐标轴与刻度系统 ------ 刻度引擎、网格、图例与刻度朝内
- 第 9 篇:控件与辅助元素 ------ 滑块旋钮、标记与装饰
- 第 10 篇:总体架构解析 ------ 从单体到三库模块化的演进
- 第 11 篇:matplotlib 风格绘图 ------ QwtPyPlot 接口详解
相关链接