qCustomPlotDemo

使用QCustomPlot实现多窗口绘制数据(支持GPU加速)

作者
WildPointer
更新
2025-12-04
类型
自有项目
仓库
0 stars / 0 forks(抓取快照)

qCustomPlotDemo

使用QCustomPlot实现多窗口绘制数据(支持GPU加速)

语言组成

C++
97.3%
C
2.7%

文件结构

查看完整文件清单(128 个文件)
README.md
qCustomPlotDemo.sln
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/3dview_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/3dview_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/3dviewd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/3dviewd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/CallbackMaker_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/CallbackMaker_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/CallbackMakerd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/CallbackMakerd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Fractals_random_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Fractals_random_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Fractals_randomd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Fractals_randomd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Fractals_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Fractals_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Fractalsd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Fractalsd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Lorenz_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Lorenz_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Lorenzd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Lorenzd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/One_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/One_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Oned.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/Oned.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/freeglutd.dll
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/freeglutd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/indexed_color_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/indexed_color_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/indexed_colord.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/indexed_colord.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/joystick_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/joystick_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/joystickd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/joystickd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/keyboard_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/keyboard_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/keyboardd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/keyboardd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/multi-touch_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/multi-touch_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/multi-touchd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/multi-touchd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/resizer_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/resizer_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/resizerd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/resizerd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/shapes_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/shapes_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/shapesd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/shapesd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/smooth_opengl3_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/smooth_opengl3_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/smooth_opengl3d.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/smooth_opengl3d.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/spaceball_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/spaceball_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/spaceballd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/spaceballd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/subwin_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/subwin_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/subwind.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/subwind.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/timer_callback_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/timer_callback_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/timer_callbackd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/timer_callbackd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/timer_staticd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/timer_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/timerd.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Debug/timerd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/3dview.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/3dview_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/CallbackMaker.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/CallbackMaker_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/Fractals.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/Fractals_random.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/Fractals_random_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/Fractals_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/Lorenz.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/Lorenz_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/One.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/One_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/freeglut.dll
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/indexed_color.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/indexed_color_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/joystick.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/joystick_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/keyboard.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/keyboard_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/multi-touch.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/multi-touch_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/resizer.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/resizer_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/shapes.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/shapes_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/smooth_opengl3.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/smooth_opengl3_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/spaceball.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/spaceball_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/subwin.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/subwin_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/timer.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/timer_callback.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/timer_callback_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/bin/Release/timer_static.exe
qCustomPlotDemo/Freeglut_Windows_3.6.0/include/GL/freeglut.h
qCustomPlotDemo/Freeglut_Windows_3.6.0/include/GL/freeglut_ext.h
qCustomPlotDemo/Freeglut_Windows_3.6.0/include/GL/freeglut_std.h
qCustomPlotDemo/Freeglut_Windows_3.6.0/include/GL/freeglut_ucall.h
qCustomPlotDemo/Freeglut_Windows_3.6.0/include/GL/glut.h
qCustomPlotDemo/Freeglut_Windows_3.6.0/lib/Debug/freeglut_staticd.lib
qCustomPlotDemo/Freeglut_Windows_3.6.0/lib/Debug/freeglut_staticd.pdb
qCustomPlotDemo/Freeglut_Windows_3.6.0/lib/Debug/freeglutd.exp
qCustomPlotDemo/Freeglut_Windows_3.6.0/lib/Debug/freeglutd.lib
qCustomPlotDemo/Freeglut_Windows_3.6.0/lib/Release/freeglut.exp
qCustomPlotDemo/Freeglut_Windows_3.6.0/lib/Release/freeglut.lib
qCustomPlotDemo/Freeglut_Windows_3.6.0/lib/Release/freeglut_static.lib
qCustomPlotDemo/main.cpp
qCustomPlotDemo/qCustomPlotDemo.cpp
qCustomPlotDemo/qCustomPlotDemo.h
qCustomPlotDemo/qCustomPlotDemo.qrc
qCustomPlotDemo/qCustomPlotDemo.ui
qCustomPlotDemo/qCustomPlotDemo.vcxproj
qCustomPlotDemo/qCustomPlotDemo.vcxproj.filters
qCustomPlotDemo/qcustomplot.cpp
qCustomPlotDemo/qcustomplot.h

关键源码

qCustomPlotDemo/qCustomPlotDemo.h
qCustomPlotDemo/qCustomPlotDemo.h
// 本项目主窗口头文件:定义界面与数据刷新逻辑#pragma once// Qt 基础组件#include <QtWidgets/QWidget>#include <QtCore/QTimer>#include <QtWidgets/QPushButton>#include <QtWidgets/QVBoxLayout>#include <QtWidgets/QHBoxLayout>#include <QtWidgets/QGridLayout>#include <QtGui/QResizeEvent>#include <QtCore/QThread>#include <cmath>#include <QtCore/QRandomGenerator>#include "ui_qCustomPlotDemo.h"#include "qcustomplot.h"QT_BEGIN_NAMESPACEnamespace Ui { class qCustomPlotDemoClass; };QT_END_NAMESPACE/*------------    数据生成类---------------*/class DataWorker : public QObject{    Q_OBJECTpublic:    explicit DataWorker(int graphCount, QObject* parent = nullptr);    ~DataWorker();private:    int m_lastKey = 0;      // 样本索引    int m_graphCount = 0;   // 视图数量    QTimer* m_timer = nullptr;  // 数据生成定时器////---------------------------------------------------------------------------------------------------------------------------------------////-----------------------------------------------------------------处理(handle)模块------------------------------------------------------////---------------------------------------------------------------------------------------------------------------------------------------public slots:    // 暂停生成    void handle_Stop();    // 开始生成    void handle_Start();private slots:    // 生成数据    void handle_doGenerate();signals:    // 数据就绪    void handle_sendDataReady(int idx, const QVector<double>& keys, const QVector<double>& values, int lastKey);};/*----------    主窗口类-------------*/class qCustomPlotDemo : public QWidget{    Q_OBJECTpublic:    ~qCustomPlotDemo();    qCustomPlotDemo(QWidget *parent = nullptr);private:    int m_key = 0;  // 样本索引    bool m_running = false;     // 运行状态    int m_capPoints = 500000;   // 数据上限(超过后替换最旧数据)    QThread* m_workerThread = nullptr; // 子线程    Ui::qCustomPlotDemoClass* ui;    QVector<QCPGraph*> m_graphs;    // 折线图数组    QVector<QCustomPlot*> m_plots;  // 绘图控件数组    DataWorker* m_worker = nullptr; // 数据生成器    QPushButton* m_startBtn = nullptr;  // 开始按钮    QPushButton* m_pauseBtn = nullptr;  // 暂停按钮////---------------------------------------------------------------------------------------------------------------------------------------////-----------------------------------------------------------------初始化(init)模块------------------------------------------------------////---------------------------------------------------------------------------------------------------------------------------------------private:    // 初始化信号槽    void init_Connect();    // 初始化主界面    void init_mainWidget();    // 初始化数据生成模块    void init_workerModule();////---------------------------------------------------------------------------------------------------------------------------------------////-----------------------------------------------------------------处理(handle)模块------------------------------------------------------////---------------------------------------------------------------------------------------------------------------------------------------private slots:    // 开始生成数据    void handle_startGeneration();    // 暂停生成数据    void handle_pauseGeneration();    // 显示数据    void handle_showData(int idx, const QVector<double>& keys, const QVector<double>& values, int lastKey);};
qCustomPlotDemo/qCustomPlotDemo.cpp
qCustomPlotDemo/qCustomPlotDemo.cpp
#include "qCustomPlotDemo.h"/*------------    数据生成类---------------*/DataWorker::DataWorker(int graphCount, QObject* parent)    : QObject(parent)    , m_graphCount(graphCount){}DataWorker::~DataWorker() {}/*  暂停生成*/void DataWorker::handle_Stop(){    if (m_timer && m_timer->isActive()) {        m_timer->stop();    }}/*  开始生成*/void DataWorker::handle_Start(){    if (!m_timer) {        m_timer = new QTimer(this);        m_timer->setInterval(1000);        //信号:数据生成定时器触发    槽:生成数据        connect(m_timer, &QTimer::timeout, this, &DataWorker::handle_doGenerate);    }    if (!m_timer->isActive()) {        m_timer->start();    }}/*  生成数据*/void DataWorker::handle_doGenerate(){    const int count = 10000;    QVector<double> keys;    keys.reserve(count);    for (int i = 0; i < count; ++i) {        keys.push_back(++m_lastKey);    }    const double pi = 3.14159265358979323846;    for (int idx = 0; idx < m_graphCount; ++idx) {        QVector<double> values;        values.reserve(count);        const double freq = 0.0015 * (1 + idx * 0.3);        const double phase = idx * pi / 6.0;        for (int i = 0; i < count; ++i) {            double x = keys[i];            double v = std::sin(freq * x + phase);            v += (QRandomGenerator::global()->bounded(200) - 100) / 100000.0;            values.push_back(v);        }        emit handle_sendDataReady(idx, keys, values, m_lastKey);    // 发送数据就绪信号    }}/*----------    主窗口类-------------*/qCustomPlotDemo::qCustomPlotDemo(QWidget *parent)    : QWidget(parent)    , ui(new Ui::qCustomPlotDemoClass()){    ui->setupUi(this);    init_mainWidget();    init_workerModule();    init_Connect();}qCustomPlotDemo::~qCustomPlotDemo(){    if (m_workerThread) {        QMetaObject::invokeMethod(m_worker, "stop", Qt::QueuedConnection);        m_workerThread->quit();        m_workerThread->wait();        m_worker->deleteLater();        m_workerThread->deleteLater();    }    delete ui;}////---------------------------------------------------------------------------------------------------------------------------------------////-----------------------------------------------------------------初始化(init)模块------------------------------------------------------////---------------------------------------------------------------------------------------------------------------------------------------/*  初始化信号槽*/void qCustomPlotDemo::init_Connect() {    //信号:开始按钮被点击    槽:开始生成数据    connect(m_startBtn, &QPushButton::clicked, this, &qCustomPlotDemo::handle_startGeneration);    //信号:暂停按钮被点击    槽:暂停生成数据    connect(m_pauseBtn, &QPushButton::clicked, this, &qCustomPlotDemo::handle_pauseGeneration);    //信号:数据就绪        connect(m_worker, &DataWorker::handle_sendDataReady, this, &qCustomPlotDemo::handle_showData, Qt::QueuedConnection);    // 事件循环起始时再次启用OpenGL与设备像素比,确保上下文有效    QTimer::singleShot(0, this, [this]() {        for (auto* p : m_plots) {            p->setOpenGl(true, 8);            p->replot(QCustomPlot::rpImmediateRefresh);        }    });}/*  初始化主界面*/void qCustomPlotDemo::init_mainWidget() {    // 初始化布局和控件    QHBoxLayout* buttonLayout = new QHBoxLayout();    QVBoxLayout* rootLayout = new QVBoxLayout(this);    m_startBtn = new QPushButton(QStringLiteral("开始"), this);    m_pauseBtn = new QPushButton(QStringLiteral("暂停"), this);    buttonLayout->addWidget(m_startBtn);    buttonLayout->addWidget(m_pauseBtn);    rootLayout->addLayout(buttonLayout);    QGridLayout* grid = new QGridLayout();    rootLayout->addLayout(grid, 1);    // 中文名 + 颜色    const QList<QPair<QString, QColor>> series = {        {QStringLiteral("红"), QColor(Qt::red)},        {QStringLiteral("蓝"), QColor(Qt::blue)},        {QStringLiteral("紫"), QColor(128, 0, 128)},        {QStringLiteral("绿"), QColor(Qt::green)},        {QStringLiteral("黄"), QColor(Qt::yellow)},        {QStringLiteral("粉"), QColor(255, 182, 193)}    };    // 初始化绘图控件    for (int i = 0; i < series.size(); ++i){        QCustomPlot* p = new QCustomPlot(this);   // 单个绘图控件        p->setOpenGl(true, 8);        p->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);        p->legend->setVisible(true);              // 显示图例        p->xAxis->setLabel("样本索引");           // X 轴标签        p->setPlottingHint(QCP::phCacheLabels, true);        p->xAxis->setRange(0, 1);                  // 初次绘制范围        p->replot(QCustomPlot::rpImmediateRefresh);// 立即重绘以避免空白        p->yAxis->setRange(-2.0, 2.0);        QCPGraph* g = p->addGraph();              // 添加折线图        QPen pen(series[i].second);        pen.setWidth(2);        g->setPen(pen);        g->setName(series[i].first);        g->setAdaptiveSampling(true);        m_plots.push_back(p);        m_graphs.push_back(g);        int row = i / 2;        int col = i % 2;        grid->addWidget(p, row, col);    }}/*  初始化数据生成模块*/void qCustomPlotDemo::init_workerModule(){    m_workerThread = new QThread(this);    m_worker = new DataWorker(m_graphs.size());    m_worker->moveToThread(m_workerThread);    qRegisterMetaType<QVector<double>>("QVector<double>");    m_workerThread->start();}/*  开始生成数据*/void qCustomPlotDemo::handle_startGeneration(){    if (!m_running){        m_running = true;        QMetaObject::invokeMethod(m_worker, "handle_Start", Qt::QueuedConnection);    }}/*  暂停生成数据*/void qCustomPlotDemo::handle_pauseGeneration(){    m_running = false;    QMetaObject::invokeMethod(m_worker, "handle_Stop", Qt::QueuedConnection);}/*  显示数据*   idx:X轴索引    keys:    values:    lastKey:*/void qCustomPlotDemo::handle_showData(int idx, const QVector<double>& keys, const QVector<double>& values, int lastKey){    m_key = lastKey;    if (m_key <= m_capPoints) {        m_graphs[idx]->addData(keys, values);    } else {        m_graphs[idx]->data()->removeBefore(m_key - m_capPoints + 1);        m_graphs[idx]->addData(keys, values);    }    double startKey = m_key > m_capPoints ? (m_key - m_capPoints + 1) : 0;    m_plots[idx]->xAxis->setRange(startKey, m_key);    m_plots[idx]->replot(QCustomPlot::rpQueuedReplot);}
qCustomPlotDemo/main.cpp
qCustomPlotDemo/main.cpp
#include "qCustomPlotDemo.h"#include <QtWidgets/QApplication>int main(int argc, char *argv[]){    QApplication::setAttribute(Qt::AA_UseDesktopOpenGL);   // 使用桌面端OpenGL    QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);// 共享OpenGL上下文    // 多重采样提升文本与线条质量    QSurfaceFormat fmt;    fmt.setSamples(8);                                QSurfaceFormat::setDefaultFormat(fmt);    QApplication a(argc, argv);    qCustomPlotDemo w;    w.show();    return a.exec();}
WildPointer

专注系统编程、工程实践与底层技术,记录 C++、Qt、OpenCV 与 VTK 的学习和实践。