QIm
Qt-native wrapper for ImPlot/ImPlot3D. Enables high-performance scientific visualization (2D/3D plots, heatmaps) in Qt apps. GPU-accelerated, million-point rendering with Qt signals/slots API. Ideal for SCADA, instrumentation & real-time analytics.
Install / Use
/learn @czyt1988/QImREADME
QIm介绍
QIm 是一个将 Dear ImGui、ImPlot、ImPlot3D 等ImGui生态集成到Qt框架中的开源库。它通过保留模式(Retained Mode)的封装,让Qt开发者能够使用熟悉的Qt编程范式(信号槽、属性系统、对象树)来构建高性能的实时数据可视化应用。
基于QIm,你无需学习 ImGui,即可直接使用熟悉的 Qt 信号槽、属性系统与对象树管理机制,构建实时数据监控、科学绘图2D/3D界面、工程仿真可视化等高性能应用场景。
QIm计划把ImGui生态的成熟库进行封装,目前正在做ImPlot和ImPlot3D的封装,旨在为Qt开发环境提供一款开箱即用的数据可视化库。
效果
通过QIm的Plot模块,可以实现如下数据可视化效果:
2D绘图
| | | |
|:---:|:---:|:---:|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3D绘图
| | | |
|:---:|:---:|:---:|
|
|
|
|
|
|
|
|
核心设计
ImGui 原生采用即时模式(Immediate Mode):每次渲染都需重新构建 UI 结构,与 Qt 编程习惯差异较大:
// 传统ImGui代码即时模式代码,这段带码每次刷新帧都要执行一遍
if (ImGui::Begin("Window")) {
if (ImPlot::BeginPlot("Plot")) {
ImPlot::PlotLine(...);
ImPlot::EndPlot();
}
ImGui::End();
}
QIm 将其转换为 Qt 开发者熟悉的保留模式(Retained Mode),通过面向对象 + 对象树的方式管理 UI 组件,属性与交互完全贴合 Qt 生态:
// QIm方式 - 面向对象,更符合Qt习惯
auto window = new QImWindowNode(root);
window->setTitle("Window");
auto plot = new QImPlotNode(window); // 自动成为子节点
plot->setTitle("Plot");
auto line = new QImPlotLineNode(plot);
line->setData(...);
QIm的核心是将 ImGui 生态的组件映射为 Qt 节点对象,将 ImGui 属性映射为 Qt 属性系统,同时保留信号槽机制
快速开始
编译和安装
项目使用cmake构建,建议执行安装(install)后使用
# 创建构建目录
mkdir build && cd build
# 配置(指定 Qt 安装路径,或确保 Qt 已加入环境变量)
cmake .. -G "Visual Studio 17 2022" -A x64 ^
-DCMAKE_PREFIX_PATH="C:/Qt/6.5.0/msvc2019_64" ^
-DCMAKE_BUILD_TYPE=Release
# 构建
cmake --build . --config Release
# 安装(可选,默认安装到 build/install 目录)
cmake --install .
项目集成
在你的 Qt 项目 CMakeLists.txt 中引入 QIm:
# 基础 Qt 组件依赖
set(MIN_QT_VERSION 5.14)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} ${MIN_QT_VERSION} COMPONENTS
Core
Gui
Widgets
OpenGL
REQUIRED
)
# Qt6 需额外引入 OpenGLWidgets
if(${QT_VERSION_MAJOR} EQUAL 6)
find_package(Qt${QT_VERSION_MAJOR} ${MIN_QT_VERSION} COMPONENTS
OpenGLWidgets
REQUIRED
)
target_link_libraries(<your_target> PRIVATE
Qt${QT_VERSION_MAJOR}::OpenGLWidgets
)
endif()
# 引入 QIm
find_package(QIm REQUIRED)
# 链接依赖
target_link_libraries(<your_target> PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::OpenGL
QIm::Core
QIm::Widgets
)
简单示例
QIm目前把ImPlot进行了封装,同步封装了对应的Widget窗口,在一个Qt桌面应用程序中,像建立窗口一样即可实现ImGui的界面,同时还支持信号槽,实现Qt环境的数据交互
#include <QImFigureWidget.h>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget* parent = nullptr) : QMainWindow(parent) {
// 创建绘图窗口
QIM::QImFigureWidget* figure = new QIM::QImFigureWidget(this);
setCentralWidget(figure);
// 配置2行1列的子图
figure->setSubplotGrid(2, 1);
// 创建第一个子图
QIM::QImPlotNode* plot1 = figure->createPlotNode();
plot1->x1Axis()->setLabel("时间 (s)");
plot1->y1Axis()->setLabel("幅度");
// 添加数据
QVector<double> x = {0, 1, 2, 3, 4};
QVector<double> y = {0, 1, 4, 9, 16};
plot1->addLine(x, y, "二次曲线");
// 第二个子图
QIM::QImPlotNode* plot2 = figure->createPlotNode();
plot2->setLegendEnabled(true);
// 添加多条曲线
std::vector<double> x2 = {0, 1, 2, 3, 4};
std::vector<double> sin_y, cos_y;
for (double val : x2) {
sin_y.push_back(std::sin(val));
cos_y.push_back(std::cos(val));
}
plot2->addLine(x2, sin_y, "sin(x)");
plot2->addLine(x2, cos_y, "cos(x)");
}
};
大规模数据处理
针对超大数据量(>50 万点)的渲染压力,QIm 内置基于 LTTB 算法的降采样模块(QImLTTBDownsampler),默认开启自适应采样,兼顾渲染性能与数据可视化精度。
渲染模式控制
QIm提供三种渲染策略适应不同场景,你可以通过QImWidget::setRenderMode根据你面对的场景选择合适的渲染模式:
QImWidget* widget = new QImWidget();
widget->setRenderMode(QImWidget::RenderAdaptive); // 默认:智能自适应
// 三种模式:
// 1. RenderAdaptive: 交互时高帧率(18FPS),静止时低帧率(1FPS)
// 2. RenderContinuous: 持续渲染(18FPS),适合动画
// 3. RenderOnDemand: 仅在事件触发时渲染,最节能
自定义节点开发
继承QImAbstractNode创建自定义组件:
class CustomPlotNode : public QImAbstractNode {
Q_OBJECT
public:
CustomPlotNode(QObject* parent = nullptr) : QImAbstractNode(parent) {}
protected:
bool beginDraw() override {
// 对应 ImGui::Begin("MyCustomWindow")
return ImGui::Begin("MyCustomWindow", nullptr, m_flags);
}
void endDraw() override {
ImGui::End();
}
private:
ImGuiWindowFlags m_flags = 0;
};
已知限制
当前QIm的Plot模块存在如下缺陷,选用请自行评估
- 不能任意添加字体,你需要先加载字体文件才能加载对应字体
- 不支持线形,你无法指定虚线、点划线等操作
绘图性能对比测试
Qt 生态里能画图的库不多,主流的为QCustomPlot、Qwt、Qt Charts和KDChart,后两者性能低,和前两者不是一个数量级,这里不做讨论,在性能对比测试中,主要对比QCustomPlot和Qwt
QCustomPlot和Qwt都自带了降采样算法,在点数非常多的时候可与高效绘图,Qwt默认不开启降采样算法,QCustomplot默认是开启降采样算法的,Qwt可以通过QwtPlotCurve::setPaintAttribute(QwtPlotCurve::FilterPointsAggressive);开启降采样算法,性能可以极大提高,下面对比区分开启降采样算法和不开启降采样算法以及开启OpenGL和不开启OpenGL的情况
下面是QIm和QCustomPlot、Qwt在「100万点绘图」场景下进行性能对比(渲染100次取平均),测试覆盖不同降采样/OpenGL 配置:
测试1:降采样关闭 + OpenGL 关闭
| 绘图库 | 单次绘图耗时(MS) | FPS | |--------|------------------|-----| | QCustomPlot | 249.3 | 4.01123 | | Qwt | 144.1 | 6.93963 | | QIm | 92.45 | 10.8167 |
测试2:降采样开启 + OpenGL 关闭
| 绘图库 | 单次绘图耗时(MS) | FPS | |--------|------------------|-----| | QCustomPlot | 39.58 | 25.2653 | | Qwt | 41.51 | 24.0906 | | QIm | 36.61 | 27.3149 |
测试3:降采样关闭 + OpenGL 开启
| 绘图库 | 单次绘图耗时(MS) | FPS | |--------|------------------|-----| | QCustomPlot | 152.93 | 6.53894 | | Qwt | 121.78 | 8.21153 | | QIm | 80.72 | 12.3885 |
测试4:降采样开启 + OpenGL 开启
| 绘图库 | 单次绘图耗时(MS) | FPS | |--------|------------------|-----| | QCustomPlot | 44.56 | 22.4417 | | Qwt | 43.58 | 22.9463 | | QIm | 38.73 | 25.8198 |
结论
| 场景 | 性能排序 | 关键特征 | |------|----------|----------| | 无降采样 + 无OpenGL | QIm ≫ Qwt > QCustomPlot | QIm 凭借 OpenGL 渲染管线优势,在百万级数据下性能领先 1.5~2 倍 | | 开启降采样(LTTB) | 三者性能趋同 | 降采样成为性能瓶颈,库间差异缩小至 10% 以内 | | 内存占用 | Qwt ≈ QCustomPlot ≪ QIm | QIm 内存开销约为其他库的 5~15 倍(架构特性决定) |
1. 降采样是大数据量渲染的决定性因素
- 10万点以下:原生渲染性能差异显著(QIm 优势明显)
- 50万点以上:开启降采样后,三者渲染耗时差距缩小至 5% 以内
- 结论:>10万点场景必须启用降采样,此时选择库应优先考虑功能与易用性,而非纯渲染性能
2. OpenGL 加速的实际收益有限
对于Qwt,QCustomPlot等库
- 在已开启降采样的前提下,启用 OpenGL 对 FPS 提升 <5%
- 仅在无降采样 + 超大数据量场景下,OpenGL 能带来 30%+ 性能增益(但此时帧率仍低于 10 FPS,实用性低)
- 建议:优先启用降采样,OpenGL 作为辅助优化手段
3. 内存占用呈现明显差异
| 库 | 100万点内存 | 500万点内存 | 特点 | |----|-------------|-------------|------| | QIm | ~460 MB | ~1.4 GB | ImGui 架构需维护双缓冲+GPU资源,内存开销大 | | Qwt | ~21 MB | ~134 MB | 内存效率高 | | QCustomPlot | ~21 MB | ~82 MB | 内存控制优秀,500万点时反超Qwt |
**注:**测试中的内存检测是检测整个测试过程的峰值内存差值,可能会存在偏差
具体测试代码见benchmark/performance
详细测试结果见:docs/zh/performance.md
