工具类 - 便捷操作 QwtPlot
Qwt 7 提供了一组位于 Qwt 命名空间下的工具类,用于简化常见的绘图操作。这些工具类按职责拆分为独立的类,用户可按需引入,不引入多余依赖。
工具类概览
classDiagram
class QwtPlotItemInfo {
+isSeriesItem()
+isXYSeriesItem()
+filterByRtti()
+dataSize()
+rttiName()
}
class QwtPlotDataAccess {
+xySamples()
+intervalSamples()
+ohlcSamples()
+setXYSamples()
+removeSamplesInRange()
}
class QwtPlotFactory {
+createCurve()
+createBarChart()
+createGrid()
+createMarker()
+createHLine()
}
class QwtPlotTransform {
+transformPoint()
+toPlotPoint()
+onePixelOffset()
+visibleRange()
}
class QwtPlotStyling {
+color()
+setColor()
+pen()
+brush()
+setSymbol()
}
QwtPlotItemInfo ..> QwtPlotItem : queries
QwtPlotDataAccess ..> QwtPlotItem : reads/writes
QwtPlotFactory ..> QwtPlot : creates items
QwtPlotTransform ..> QwtPlot : transforms
QwtPlotStyling ..> QwtPlotItem : styles
| 类名 |
职责 |
头文件 |
QwtPlotItemInfo |
类型查询与过滤 |
<QwtPlotItemInfo> |
QwtPlotDataAccess |
数据提取与设置 |
<QwtPlotDataAccess> |
QwtPlotFactory |
工厂方法创建绘图项 |
<QwtPlotFactory> |
QwtPlotTransform |
坐标变换 |
<QwtPlotTransform> |
QwtPlotStyling |
样式读写 |
<QwtPlotStyling> |
QwtPlotFactory — 工厂方法
QwtPlotFactory 提供一行代码创建并附加绘图项到 QwtPlot 的便捷方法,覆盖了所有常见的绘图项类型。
创建曲线
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | #include <QwtPlotFactory>
#include <QwtPlot>
QwtPlot* plot = new QwtPlot();
// 从 QPointF 数据创建曲线
QVector<QPointF> data = {{0, 1}, {1, 3}, {2, 2}, {3, 5}};
QwtPlotCurve* curve = QwtPlotFactory::createCurve(plot, "温度", data);
// 从分离的 x, y 向量创建曲线
QVector<double> x = {0, 1, 2, 3};
QVector<double> y = {1, 3, 2, 5};
QwtPlotCurve* curve2 = QwtPlotFactory::createCurve(plot, "压力", x, y);
// 仅 y 值(x 自动为 0, 1, 2, ...)
QVector<double> yOnly = {10, 20, 15, 25, 30};
QwtPlotCurve* curve3 = QwtPlotFactory::createCurve(plot, "索引曲线", yOnly);
|
工厂方法会自动完成创建 → 设置数据 → 绑定坐标轴 → attach 的全部流程,返回的指针可进一步自定义样式。
创建柱状图与直方图
| // 柱状图
QVector<double> values = {10, 25, 30, 15, 40};
QwtPlotBarChart* bar = QwtPlotFactory::createBarChart(plot, "销售额", values);
// 直方图
QVector<QwtIntervalSample> histData;
histData << QwtIntervalSample(5, 0, 10)
<< QwtIntervalSample(15, 10, 20)
<< QwtIntervalSample(25, 20, 30);
QwtPlotHistogram* hist = QwtPlotFactory::createHistogram(plot, "分布", histData);
|
创建 K 线图(交易曲线)
| QVector<QwtOHLCSample> ohlc;
ohlc << QwtOHLCSample(1, 100, 110, 95, 105); // time, open, high, low, close
ohlc << QwtOHLCSample(2, 105, 115, 100, 108);
ohlc << QwtOHLCSample(3, 108, 120, 102, 118);
QwtPlotTradingCurve* kline = QwtPlotFactory::createTradingCurve(plot, "股票", ohlc);
|
创建箱线图与向量场
| // 箱线图
QVector<QwtBoxSample> boxData;
boxData << QwtBoxSample(1, 10, 25, 50, 75, 90); // pos, min, q1, median, q3, max
QwtPlotBoxChart* box = QwtPlotFactory::createBoxChart(plot, "统计", boxData);
// 向量场
QVector<QwtVectorFieldSample> vf;
vf << QwtVectorFieldSample(0, 0, 1, 0)
<< QwtVectorFieldSample(1, 0, 0, 1);
QwtPlotVectorField* field = QwtPlotFactory::createVectorField(plot, "流场", vf);
|
创建装饰项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | // 添加网格
QwtPlotGrid* grid = QwtPlotFactory::createGrid(plot, true); // true = 启用次网格线
// 添加水平参考线
QwtPlotMarker* hline = QwtPlotFactory::createHLine(plot, 50.0);
// 添加垂直参考线
QwtPlotMarker* vline = QwtPlotFactory::createVLine(plot, 2.5);
// 添加标记点
QwtPlotMarker* marker = QwtPlotFactory::createMarker(plot, QPointF(1, 3), "峰值");
// 添加高亮区域
QwtPlotZoneItem* zone = QwtPlotFactory::createZone(
plot, QwtInterval(2, 4), Qt::Vertical, QBrush(QColor(255, 0, 0, 30)));
// 添加画布内图例
QwtPlotLegendItem* legend = QwtPlotFactory::createLegend(plot);
// 添加箭头标记
QwtPlotArrowMarker* arrow = QwtPlotFactory::createArrowMarker(
plot, QPointF(0, 0), QPointF(3, 5));
|
完整的工厂方法列表
QwtPlotFactory 覆盖全部 9 种数据绘图项和 7 种装饰项的创建。详见 API 文档。
QwtPlotItemInfo — 类型查询
QwtPlotItemInfo 用于查询绘图项的类型信息和过滤,不会修改任何绘图项状态。
类型判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | #include <QwtPlotItemInfo>
QwtPlotItem* item = ...;
// 是否是数据系列项
bool isSeries = QwtPlotItemInfo::isSeriesItem(item);
// 是否是 XY 数据项(曲线、柱状图)
bool isXY = QwtPlotItemInfo::isXYSeriesItem(item);
// 是否是区间数据项(区间曲线、直方图)
bool isInterval = QwtPlotItemInfo::isIntervalSeriesItem(item);
// 是否是装饰项(网格、标记等)
bool isDeco = QwtPlotItemInfo::isDecoratorItem(item);
|
按类型过滤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | // 获取 plot 中所有曲线
QwtPlotItemList curves = QwtPlotItemInfo::filterByRtti(
plot, QwtPlotItem::Rtti_PlotCurve);
// 按多个类型过滤
QSet<int> types = {
QwtPlotItem::Rtti_PlotCurve,
QwtPlotItem::Rtti_PlotBarChart
};
QwtPlotItemList dataItems = QwtPlotItemInfo::filterByRtti(plot, types);
// 获取所有数据系列项
QwtPlotItemList allSeries = QwtPlotItemInfo::seriesItems(plot);
// 获取所有 XY 数据项
QwtPlotItemList xyItems = QwtPlotItemInfo::xySeriesItems(plot);
// 获取所有可见项
QwtPlotItemList visible = QwtPlotItemInfo::visibleItems(plot);
|
获取基本信息
| // 获取数据点数
int count = QwtPlotItemInfo::dataSize(item); // -1 表示非数据项
// 获取数据范围
QRectF rect = QwtPlotItemInfo::dataRect(item);
// 获取 rtti 的名称
QString name = QwtPlotItemInfo::rttiName(item->rtti()); // "PlotCurve"
|
QwtPlotDataAccess — 数据读写
QwtPlotDataAccess 通过 rtti 自动分发,从任意 QwtPlotItem 中提取或设置样本数据,覆盖全部 7 种数据类型。
提取 XY 数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | #include <QwtPlotDataAccess>
QwtPlotItem* item = ...; // 可能是 QwtPlotCurve 或 QwtPlotBarChart
// 提取为 QPointF 向量
QVector<QPointF> pts = QwtPlotDataAccess::xySamples(item);
// 提取为分离的 x, y 向量
QVector<double> x, y;
QwtPlotDataAccess::xySamples(item, x, y);
if (!pts.isEmpty()) {
qDebug() << "第一个点:" << pts.first();
qDebug() << "总数据量:" << pts.size();
}
|
提取其他类型数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | // 区间数据(QwtPlotIntervalCurve, QwtPlotHistogram)
QVector<QwtIntervalSample> intervals = QwtPlotDataAccess::intervalSamples(item);
// OHLC 数据(QwtPlotTradingCurve)
QVector<QwtOHLCSample> ohlc = QwtPlotDataAccess::ohlcSamples(item);
// 3D 数据(QwtPlotSpectroCurve)
QVector<QwtPoint3D> xyz = QwtPlotDataAccess::xyzSamples(item);
// 多系列数据(QwtPlotMultiBarChart)
QVector<QwtSetSample> sets = QwtPlotDataAccess::setSamples(item);
// 向量场数据(QwtPlotVectorField)
QVector<QwtVectorFieldSample> vf = QwtPlotDataAccess::vectorFieldSamples(item);
// 箱线图数据(QwtPlotBoxChart)
QVector<QwtBoxSample> boxes = QwtPlotDataAccess::boxSamples(item);
|
设置数据
1
2
3
4
5
6
7
8
9
10
11
12
13 | // 设置 XY 数据
QVector<QPointF> newData = {{0, 5}, {1, 8}, {2, 3}};
bool ok = QwtPlotDataAccess::setXYSamples(item, newData);
// 设置分离的 x, y 数据
QVector<double> x = {0, 1, 2};
QVector<double> y = {5, 8, 3};
bool ok2 = QwtPlotDataAccess::setXYSamples(item, x, y);
// 设置 OHLC 数据
QVector<QwtOHLCSample> ohlcData;
ohlcData << QwtOHLCSample(1, 100, 110, 95, 105);
QwtPlotDataAccess::setOhlcSamples(item, ohlcData);
|
模板提取
对于已知具体类型的场景,可以直接使用模板方法:
| #include <QwtSeriesStore>
const auto* store = static_cast<const QwtSeriesStore<QPointF>*>(curve);
// 提取全部数据
QVector<QPointF> all = QwtPlotDataAccess::samples(store);
// 提取子范围
QVector<QPointF> subset = QwtPlotDataAccess::samples(store, 10, 50);
|
范围筛选与删除
1
2
3
4
5
6
7
8
9
10
11
12 | // 提取矩形范围内的数据点
QRectF range(1.0, 0.0, 3.0, 5.0);
QVector<QPointF> inRange = QwtPlotDataAccess::xySamplesInRange(item, range);
// 提取路径范围内的数据点
QPainterPath path;
path.addEllipse(QPointF(2, 3), 1, 1);
QVector<QPointF> inPath = QwtPlotDataAccess::xySamplesInRange(item, path);
// 删除矩形范围内的数据点
int removed = QwtPlotDataAccess::removeSamplesInRange(item, range);
qDebug() << "删除了" << removed << "个数据点";
|
QwtPlotTransform 提供坐标系统之间的转换,所有方法均为只读操作,不会修改 plot 状态。
坐标系互转
当 plot 使用寄生坐标轴(多轴)时,同一屏幕位置在不同轴系下有不同的数据坐标值:
| #include <QwtPlotTransform>
QwtPlot* plot = ...;
QPointF point(100, 50); // 在 XBottom/YLeft 轴系下的数据坐标
// 转换到 XTop/YRight 轴系(同一屏幕位置)
QPointF converted = QwtPlotTransform::transformPoint(
plot, point,
QwtAxis::XBottom, QwtAxis::YLeft, // 源轴系
QwtAxis::XTop, QwtAxis::YRight // 目标轴系
);
|
屏幕/数据坐标互转
| // 鼠标点击位置转数据坐标(常用于 pick 操作)
QPointF mousePos(150, 200);
QPointF dataPoint = QwtPlotTransform::toPlotPoint(plot, mousePos);
// 数据坐标转屏幕坐标
QPointF screenPos = QwtPlotTransform::toScreenPoint(plot, dataPoint);
|
像素偏移
| // 计算 1 像素对应的数据偏移量
QPointF offset = QwtPlotTransform::onePixelOffset(plot);
qDebug() << "1px =" << offset.x() << "(x)," << offset.y() << "(y)";
|
此方法常用于实现拖拽、微调等需要像素级精度控制的交互功能。
可视范围
| // 获取当前可视区域的数据范围
QRectF visible = QwtPlotTransform::visibleRange(plot);
qDebug() << "可见 X:" << visible.left() << "~" << visible.right();
qDebug() << "可见 Y:" << visible.top() << "~" << visible.bottom();
// 获取所有数据项的数据范围并集
QRectF totalRange = QwtPlotTransform::totalDataRange(plot);
// 仅统计可见项
QRectF visibleRange = QwtPlotTransform::totalDataRange(plot, true);
|
QwtPlotStyling — 样式操作
QwtPlotStyling 通过 rtti 分发,统一读写各种绘图项的颜色、画笔、画刷和符号。
统一颜色读写
1
2
3
4
5
6
7
8
9
10
11
12 | #include <QwtPlotStyling>
QwtPlotItem* item = ...;
// 获取颜色(自动识别类型)
QColor c = QwtPlotStyling::color(item);
if (c.isValid()) {
qDebug() << "当前颜色:" << c.name();
}
// 设置颜色(自动分发到 pen 或 brush)
QwtPlotStyling::setColor(item, Qt::red);
|
不同类型绘图项的颜色来源:
| 绘图项类型 |
颜色来源 |
| QwtPlotCurve |
pen().color() |
| QwtPlotBarChart |
brush().color() |
| QwtPlotHistogram |
brush().color() |
| QwtPlotGrid |
majorPen().color() |
| QwtPlotMarker |
linePen().color() |
| QwtPlotTradingCurve |
symbolPen().color() |
画笔与画刷
| // 获取画笔
QPen p = QwtPlotStyling::pen(item);
QBrush b = QwtPlotStyling::brush(item);
// 设置画笔
QwtPlotStyling::setPen(item, QPen(Qt::blue, 2, Qt::DashLine));
// 设置画刷
QwtPlotStyling::setBrush(item, QBrush(QColor(255, 0, 0, 50)));
|
曲线符号
| QwtPlotCurve* curve = ...;
// 设置符号(颜色跟随曲线颜色)
QwtPlotStyling::setSymbol(curve, QwtSymbol::Ellipse, QSize(8, 8));
// 设置符号(指定颜色)
QwtPlotStyling::setSymbol(curve, QwtSymbol::Diamond, Qt::green, QSize(10, 10));
// 设置线型
QwtPlotStyling::setLineStyle(curve, Qt::DashLine, 1.5);
|
推荐值与强制重绘
| // 根据数据量推荐线宽
qreal width = QwtPlotStyling::recommendPenWidth(curve->dataSize());
// 强制重绘(即使 autoReplot 关闭也会立即刷新)
QwtPlotStyling::forceReplot(plot);
|
综合示例
以下示例展示如何组合使用多个工具类完成一个常见的绘图流程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 | #include <QwtPlot>
#include <QwtPlotFactory>
#include <QwtPlotDataAccess>
#include <QwtPlotStyling>
#include <QwtPlotTransform>
void setupPlot(QwtPlot* plot)
{
// 1. 使用工厂方法快速创建绘图项
QVector<double> x = {1, 2, 3, 4, 5};
QVector<double> y = {10, 25, 15, 30, 20};
auto* curve = QwtPlotFactory::createCurve(plot, "温度", x, y);
QwtPlotFactory::createBarChart(plot, "产量", {100, 200, 150, 300, 250});
QwtPlotFactory::createGrid(plot);
QwtPlotFactory::createHLine(plot, 20.0, QPen(Qt::red, 1, Qt::DashLine));
// 2. 使用 Styling 统一设置样式
QwtPlotStyling::setColor(curve, Qt::blue);
QwtPlotStyling::setSymbol(curve, QwtSymbol::Ellipse, Qt::cyan, QSize(6, 6));
// 3. 使用 DataAccess 提取数据做分析
QVector<QPointF> data = QwtPlotDataAccess::xySamples(curve);
double maxVal = 0;
for (const auto& p : data)
maxVal = qMax(maxVal, p.y());
// 4. 在最大值处添加标记
auto maxIt = std::max_element(data.begin(), data.end(),
[](const QPointF& a, const QPointF& b) { return a.y() < b.y(); });
if (maxIt != data.end()) {
QwtPlotFactory::createMarker(plot, *maxIt,
QString("最大值: %1").arg(maxIt->y()));
}
// 5. 使用 Transform 获取可视范围
QRectF visible = QwtPlotTransform::visibleRange(plot);
qDebug() << "当前可视范围:" << visible;
plot->replot();
}
|
支持的绘图项类型
工具类覆盖 Qwt 7 中全部绘图项类型:
数据绘图项
| 绘图项 |
数据类型 |
工厂方法 |
数据读写 |
QwtPlotCurve |
QPointF |
createCurve() |
xySamples() / setXYSamples() |
QwtPlotBarChart |
QPointF |
createBarChart() |
xySamples() / setXYSamples() |
QwtPlotMultiBarChart |
QwtSetSample |
createMultiBarChart() |
setSamples() / setSetSamples() |
QwtPlotHistogram |
QwtIntervalSample |
createHistogram() |
intervalSamples() / setIntervalSamples() |
QwtPlotIntervalCurve |
QwtIntervalSample |
createIntervalCurve() |
intervalSamples() / setIntervalSamples() |
QwtPlotTradingCurve |
QwtOHLCSample |
createTradingCurve() |
ohlcSamples() / setOhlcSamples() |
QwtPlotSpectroCurve |
QwtPoint3D |
createSpectroCurve() |
xyzSamples() / setXyzSamples() |
QwtPlotVectorField |
QwtVectorFieldSample |
createVectorField() |
vectorFieldSamples() / setVectorFieldSamples() |
QwtPlotBoxChart |
QwtBoxSample |
createBoxChart() |
boxSamples() / setBoxSamples() |
装饰项
| 绘图项 |
工厂方法 |
QwtPlotGrid |
createGrid() |
QwtPlotMarker |
createMarker() / createHLine() / createVLine() |
QwtPlotZoneItem |
createZone() |
QwtPlotArrowMarker |
createArrowMarker() |
QwtPlotGraphicItem |
createGraphic() |
QwtPlotTextLabel |
createTextLabel() |
QwtPlotLegendItem |
createLegend() |
QwtPlotScaleItem |
createScaleItem() |
相关示例
工具类的使用方式可以结合现有的 examples/2D/ 下的示例进行理解。
高层封装:QwtPyPlot
如果你熟悉 matplotlib 的接口风格,可以使用 QwtPyPlot 获得更简洁的绘图体验。QwtPyPlot 在内部使用上述工具类,但提供了类似 plt.plot() 的一站式接口:
| #include <QwtPyPlot>
QwtPlot* plot = new QwtPlot;
QwtPyPlot plt(plot);
plt.plot(x, y, "r-o", "Temperature"); // 一行搞定
plt.setTitle("My Plot");
plt.grid(true);
plt.legend();
plot->show();
|