License: MIT
QtRhiPlot 是一个基于 Qt Quick 和 Qt RHI 的实时曲线绘图组件。组件在 QML 中以 RhiPlot 形式使用,支持多曲线、实时滚动、坐标轴、网格、鼠标缩放/拖拽、暂停后悬浮取值和 CSV 导出。
- 基于
QQuickItem的 QML 绘图组件 - 使用 Qt RHI 和
.qsbshader 渲染曲线、坐标轴和网格 - 支持多条曲线,每条曲线可设置颜色、宽度、线型和可见性
- 支持数值 X 轴和时间 X 轴
- 支持手动坐标范围和自动坐标范围
- 支持实时滚动 X 轴
- 支持鼠标滚轮缩放、左键拖拽平移
- 暂停跟随后支持悬浮窗显示各曲线插值
- 支持导出历史数据到 CSV
- 通过
refreshRate限制界面刷新频率,数据写入可高于刷新频率
- Qt 6.8 或更新版本
- CMake 3.16 或更新版本
- 支持 Qt 6 的 C++ 编译器
- Qt 模块:
CoreGuiGuiPrivateQmlQuickShaderTools
注意:项目使用了 Qt 私有 RHI 头文件,例如 <rhi/qrhi.h>,因此需要 GuiPrivate。如果使用系统包管理器安装 Qt,需要安装对应版本的 Qt private development 包;如果使用 Qt 官方安装器,需要确保安装的 Qt 版本包含 private headers。
cmake -S . -B build -DCMAKE_PREFIX_PATH=/path/to/Qt/6.x.x/gcc_64
cmake --build build --target QtRhiPlot运行:
./build/QtRhiPlotQt Creator 用户可以直接打开 CMakeLists.txt,选择 Qt 6.8+ Kit 后构建。
import QtQuick
import QtRhiPlotQML 1.0
Window {
width: 800
height: 480
visible: true
RhiPlot {
id: plot
anchors.fill: parent
autoRange: false
xMin: 0
xMax: 8
yMin: -1.5
yMax: 1.5
backgroundColor: "white"
gridVisible: true
axisVisible: true
xAxisTitle: "Time (s)"
yAxisTitle: "Value"
xAxisLabelMode: RhiPlot.NumericXAxis
Component.onCompleted: {
addCurve("sensor")
setCurveColor("sensor", "#0077b6")
setCurveLineStyle("sensor", RhiPlot.SolidLine)
addDataPoint("sensor", 0.0, 0.2)
addDataPoint("sensor", 1.0, 0.8)
addDataPoint("sensor", 2.0, 0.1)
}
}
}推荐做法是:C++ 负责采集或生成数据,通过信号发送到 QML;QML 收到信号后调用 RhiPlot 的 addDataPoint()。
class SampleFeeder : public QObject
{
Q_OBJECT
Q_PROPERTY(double startTime READ startTime NOTIFY startTimeChanged)
public:
explicit SampleFeeder(QObject *parent = nullptr)
: QObject(parent)
{
m_timer.setInterval(1);
m_timer.setTimerType(Qt::PreciseTimer);
connect(&m_timer, &QTimer::timeout, this, &SampleFeeder::generateSample);
reset();
}
double startTime() const { return m_startTime; }
Q_INVOKABLE void start() { m_timer.start(); }
Q_INVOKABLE void stop() { m_timer.stop(); }
Q_INVOKABLE void reset()
{
m_startTime = QDateTime::currentMSecsSinceEpoch() / 1000.0;
m_currentTime = m_startTime;
emit startTimeChanged();
}
signals:
void startTimeChanged();
void sampleGenerated(double x, double y0, double y1);
private:
void generateSample()
{
m_currentTime += m_timer.interval() / 1000.0;
const double t = m_currentTime - m_startTime;
emit sampleGenerated(m_currentTime, qSin(t * 2.6), qCos(t * 1.7));
}
QTimer m_timer;
double m_startTime = 0.0;
double m_currentTime = 0.0;
};在 main.cpp 中暴露给 QML:
QQmlApplicationEngine engine;
SampleFeeder sampleFeeder;
engine.rootContext()->setContextProperty("sampleFeeder", &sampleFeeder);
engine.loadFromModule("QtRhiPlotQML", "Main");QML 接收信号:
RhiPlot {
id: plot
property real startTime: 0
property real visibleSeconds: 8
autoRange: false
xAxisLabelMode: RhiPlot.TimeXAxis
Component.onCompleted: {
addCurve("sin")
addCurve("cos")
sampleFeeder.reset()
startTime = sampleFeeder.startTime
xMin = startTime
xMax = startTime + 0.001
sampleFeeder.start()
}
function appendSample(x, y0, y1) {
addDataPoint(0, x, y0)
addDataPoint(1, x, y1)
if (followLatestX) {
xMin = x - visibleSeconds
xMax = x
}
}
}
Connections {
target: sampleFeeder
function onSampleGenerated(x, y0, y1) {
plot.appendSample(x, y0, y1)
}
}1ms QTimer 不等于严格实时 1kHz。真实高频采集建议在 C++ 中缓存数据,并按较低 UI 频率批量发送给 QML。
| 属性 | 类型 | 说明 |
|---|---|---|
curveManager |
PlotCurveManager |
曲线管理器,通常不需要在 QML 中直接设置。 |
autoRange |
bool |
数据变化时自动调整坐标范围。 |
xMin, xMax |
real |
当前 X 轴可视范围。 |
yMin, yMax |
real |
当前 Y 轴可视范围。 |
backgroundColor |
color |
绘图背景色。 |
gridVisible |
bool |
是否显示网格。 |
gridColor |
color |
网格颜色。 |
axisVisible |
bool |
是否显示坐标轴、刻度、标签、标题和悬浮窗。 |
axisColor |
color |
坐标轴和刻度线颜色。 |
axisTextColor |
color |
坐标轴文字颜色。 |
plotLeftMargin |
real |
左侧绘图区边距。 |
plotRightMargin |
real |
右侧绘图区边距。 |
plotTopMargin |
real |
顶部绘图区边距。 |
plotBottomMargin |
real |
底部绘图区边距。 |
xAxisTitle |
string |
X 轴标题。 |
yAxisTitle |
string |
Y 轴标题。 |
zoomEnabled |
bool |
是否启用鼠标滚轮缩放。 |
followLatestX |
bool |
是否让 X 轴跟随最新数据。暂停后可查看历史并显示悬浮窗。 |
refreshRate |
real |
最大刷新率,单位 fps。设置为 0 表示立即刷新。 |
xAxisLabelMode |
enum |
RhiPlot.NumericXAxis 或 RhiPlot.TimeXAxis。 |
hoverPopupEnabled |
bool |
是否启用内置悬浮窗。 |
hoverInfoValid |
bool |
悬浮信息是否有效,只读。 |
hoverX |
real |
当前悬浮 X 值,只读。 |
hoverPosition |
point |
当前悬浮位置,只读。 |
hoverValues |
list |
当前悬浮位置的各曲线值,只读。 |
线型:
RhiPlot.SolidLine
RhiPlot.DashLine
RhiPlot.DotLine
RhiPlot.DashDotLine
RhiPlot.DashDotDotLine
RhiPlot.NoLineX 轴标签模式:
RhiPlot.NumericXAxis
RhiPlot.TimeXAxisTimeXAxis 把 X 值当作 Unix epoch 秒,并显示为本地时间,例如 HH:mm:ss.zzz。
曲线管理:
addCurve(name)
removeCurve(index)
removeCurve(name)
clearCurves()
curveCount()
getCurve(index)
getCurve(name)数据操作:
addDataPoint(curveIndex, x, y)
addDataPoint(curveName, x, y)
setData(curveIndex, xData, yData)
setData(curveName, xData, yData)
clearHistoryData()外观设置:
setCurveColor(curveIndex, color)
setCurveColor(curveName, color)
setCurveWidth(curveIndex, width)
setCurveWidth(curveName, width)
setCurveLineStyle(curveIndex, style)
setCurveLineStyle(curveName, style)
setCurveVisible(curveIndex, visible)
setCurveVisible(curveName, visible)坐标和刷新:
autoAdjustRange()
updatePlot()导出 CSV:
exportData(directoryPath, fileName)
exportDataFile(fileUrl)- 绘图区滚轮:缩放 X/Y 轴。
- X 轴区域滚轮:暂停跟随时缩放 X 轴。
- Y 轴区域滚轮:缩放 Y 轴。
- 左键拖拽:平移视图。暂停跟随后可以平移 X 轴。
- 暂停跟随后,鼠标移动到绘图区会显示悬浮窗,展示当前 X 位置各曲线的插值。
- X 数据建议保持单调递增。可视范围优化和悬浮插值依赖 X 轴二分查找。
refreshRate只限制界面刷新,不限制数据写入。- 高频数据建议在 C++ 侧批量缓存,避免 QML 每个点都处理一次。
- 当前项目使用 Qt 私有 API,升级 Qt 版本时需要同步验证 RHI 接口兼容性。
本项目使用 MIT License。详见 LICENSE。
QtRhiPlot is a real-time Qt Quick plotting component rendered with Qt RHI. It is available in QML as RhiPlot from the QtRhiPlotQML module. It supports multiple curves, rolling time axes, grid and axis labels, mouse zoom and pan, paused hover inspection, and CSV export.
- QML plotting component based on
QQuickItem - Rendering through Qt RHI and
.qsbshaders - Multiple curves with independent color, width, line style, and visibility
- Numeric X axis and wall-clock time X axis
- Manual or automatic axis ranges
- Rolling X axis for real-time data
- Mouse wheel zoom and left-button panning
- Hover popup with interpolated values when following is paused
- CSV export for historical data
- Render throttling with
refreshRate
- Qt 6.8 or newer
- CMake 3.16 or newer
- A C++ compiler supported by Qt 6
- Qt modules:
CoreGuiGuiPrivateQmlQuickShaderTools
GuiPrivate is required because this project uses Qt private RHI headers such as <rhi/qrhi.h>.
cmake -S . -B build -DCMAKE_PREFIX_PATH=/path/to/Qt/6.x.x/gcc_64
cmake --build build --target QtRhiPlotRun:
./build/QtRhiPlotimport QtQuick
import QtRhiPlotQML 1.0
RhiPlot {
id: plot
anchors.fill: parent
autoRange: false
xMin: 0
xMax: 8
yMin: -1.5
yMax: 1.5
backgroundColor: "white"
gridVisible: true
axisVisible: true
xAxisTitle: "Time (s)"
yAxisTitle: "Value"
Component.onCompleted: {
addCurve("sensor")
setCurveColor("sensor", "#0077b6")
setCurveLineStyle("sensor", RhiPlot.SolidLine)
addDataPoint("sensor", 0.0, 0.2)
addDataPoint("sensor", 1.0, 0.8)
}
}Create a QObject data source in C++, expose it to QML, and append samples in QML when the signal arrives.
QQmlApplicationEngine engine;
SampleFeeder sampleFeeder;
engine.rootContext()->setContextProperty("sampleFeeder", &sampleFeeder);
engine.loadFromModule("QtRhiPlotQML", "Main");Connections {
target: sampleFeeder
function onSampleGenerated(x, y0, y1) {
plot.addDataPoint(0, x, y0)
plot.addDataPoint(1, x, y1)
}
}For high-frequency data sources, batch samples in C++ and send them to QML at a lower UI rate. A 1 ms QTimer is not guaranteed to run at exact 1 kHz on desktop systems.
| Property | Type | Description |
|---|---|---|
autoRange |
bool |
Automatically fit axes to curve data. |
xMin, xMax |
real |
Visible X axis range. |
yMin, yMax |
real |
Visible Y axis range. |
backgroundColor |
color |
Plot background color. |
gridVisible |
bool |
Show or hide grid lines. |
gridColor |
color |
Grid line color. |
axisVisible |
bool |
Show or hide axes, labels, titles, and hover popup. |
axisColor |
color |
Axis and tick line color. |
axisTextColor |
color |
Axis text color. |
xAxisTitle |
string |
X axis title. |
yAxisTitle |
string |
Y axis title. |
zoomEnabled |
bool |
Enable mouse wheel zoom. |
followLatestX |
bool |
Keep X range following the latest data. Disable it to inspect history. |
refreshRate |
real |
Maximum render rate in fps. Use 0 for immediate updates. |
xAxisLabelMode |
enum |
RhiPlot.NumericXAxis or RhiPlot.TimeXAxis. |
hoverPopupEnabled |
bool |
Enable the built-in hover popup. |
Curve management:
addCurve(name)
removeCurve(index)
removeCurve(name)
clearCurves()
curveCount()
getCurve(index)
getCurve(name)Data:
addDataPoint(curveIndex, x, y)
addDataPoint(curveName, x, y)
setData(curveIndex, xData, yData)
setData(curveName, xData, yData)
clearHistoryData()Appearance:
setCurveColor(curveIndex, color)
setCurveColor(curveName, color)
setCurveWidth(curveIndex, width)
setCurveWidth(curveName, width)
setCurveLineStyle(curveIndex, style)
setCurveLineStyle(curveName, style)
setCurveVisible(curveIndex, visible)
setCurveVisible(curveName, visible)Export:
exportData(directoryPath, fileName)
exportDataFile(fileUrl)RhiPlot.SolidLine
RhiPlot.DashLine
RhiPlot.DotLine
RhiPlot.DashDotLine
RhiPlot.DashDotDotLine
RhiPlot.NoLine
RhiPlot.NumericXAxis
RhiPlot.TimeXAxisTimeXAxis treats X values as Unix epoch seconds and formats labels as local time.
- Wheel over plot area: zoom X and Y.
- Wheel over X axis area: zoom X when
followLatestXis false. - Wheel over Y axis area: zoom Y.
- Left-button drag: pan the plot.
- Hover popup is available inside the plot area when
followLatestXis false.
- Keep X values monotonically increasing for range optimization and hover interpolation.
refreshRatethrottles rendering, not data ingestion.- This project uses Qt private APIs. Re-test compatibility when upgrading Qt.
This project is licensed under the MIT License. See LICENSE.
.
├── CMakeLists.txt
├── LICENSE
├── README.md
├── main.cpp
├── Main.qml
└── PlotSrc
├── plotdata.cpp
├── plotdata.h
├── plot_rhi.frag
├── plot_rhi.vert
├── rhiplot.cpp
└── rhiplot.h