使用QCustomPlot创建带游标测量的Plot

       QCustomePlot是一个可跨平台应用的开源库,可以创建自定义的Plot;而且集成简单,只需要在项目中加入头文件qcustomplot.hqcustomplot.cpp文件,然后使一个widget提升为QCustomPlot类,即可使用。

本实例应用是使用此开源库创建带游标测量的Plot.

先上效果图

创建一个自定义plot类,继承自QCustomPlot.

#ifndef MULTICURVESPLOT_H
#define MULTICURVESPLOT_H

#include <QObject>
#include "qcustomplot.h"
#include <QMouseEvent>
#include <QMap>
#include "qDebug.h"
typedef struct _CurveData
{
    QVector<double> keyVec;//x
    QVector<double> valVec;//y
}CurveData;//单个曲线的数据


/*
注意:调用任何函数前,必须先已经通过init函数初始化过支持的曲线数目,送入数据源的指针
为什么不直接在构造中
*/
class MultiCurvesPlot : public QCustomPlot
{
    Q_OBJECT
public:
    /*传入的_allCurvesData必须是有size的*/
    MultiCurvesPlot(int _curvesCnt, QWidget *parent = 0);
    ~MultiCurvesPlot(){qDebug() << "delete MultiCurvesPlot";}

    void setCurvesName(QVector<QString> _nameVec);
    void addData(int curveIdx, double x, double y);
    void setAutoScroll(bool enable){autoScroll = enable;}
    void setDiffSolveEnable(bool enable);
    void setColors(QVector<QColor> _colors);
    void setMainPlot(QCustomPlot *_mainPlot);
    void setTracerEnable(bool enable);//是否使能游标
    void setScatterPointEnable(bool enable);//是否使能散点
    const QVector<CurveData> *getAllData(void) const;
    QString getCurveName(int idx);

public slots:
    void setCurveName(int idx, const QString newName);
    void showCurves(QList<uint16_t> _idxList);
    void storeAxisScope(bool x, bool y);//保存轴的范围
    void resumeAxisScope(bool x, bool y);//恢复轴的范围

signals:
    void noniusValueChanged(int _curveIdx, QString val);//游标值变化了



protected:
    void timerEvent(QTimerEvent *event) Q_DECL_OVERRIDE;

    virtual void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    virtual void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    virtual void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;

//    virtual void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
//    virtual void keyReleaseEvent(QKeyEvent *event) Q_DECL_OVERRIDE;

private slots:
    void clearAllData();
    void when_selectionChangedByUser();
    //void when_itemDoubleClick (QCPAbstractItem *item, QMouseEvent *event){qDebug() << item;}
    void when_legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event);


private:
    //QCPGraph* getGraph(int curveIdx);

    MultiCurvesPlot();
    QVector<CurveData> allCurvesData;//所有曲线的数据,allCurvesData[n]中又含有两个QVector,也即第n条曲线的x、y值
    int curvesCnt;//支持的曲线数目    
    QMap<int, QCPGraph*> curveIdx2graphPtr;//记录或查询:从曲线索引(key)->graph指针(val)。读之前必须查询key的存在性contains(key)?
    QMap<QCPGraph*, int> graphPtr2curveIdx;//记录或查询:从graph指针(key)->曲线索引(val)。读之前必须查询key的存在性contains(key)?
    QVector<QColor> getColor;//记录所有曲线的颜色
    QVector<QString> getName;  //记录所有曲线的名称(图例)
    QList<uint16_t> idxList;//当前正在显示的曲线的编号
    bool autoScroll;

    QCPItemText *textLabel;//单击时提示信息框
    QCPItemLine *arrow;//提示信息的箭头

    QAction *actClearDatas;//右键菜单:清空历史数据
    QAction *actshowAllGraph;//右键菜单:全显

    /*游标相关组件*/
    QCPItemTracer *tracer;//游标
    QCPGraph *traceGraph;//游标要吸附哪个graph
    QCPItemText *tracerXText;//用于实时显示游标X值
    QCPItemText *tracerYText;//用于实时显示游标Y值
    QCPItemLine *tracerArrow;
    bool tracerEnable;//是否使能游标

    /*两点求差组件*/
    bool diffSolveEnable;//是否使能两点求差
    QCPItemText *diffText;//单击时提示信息框
    QPointF *lastPoint;
    void solveDifference(QPointF newPoint);

    /*记忆轴的范围组件*/
    QCPRange Xscope;//记录X轴的范围
    QCPRange Yscope;//记录Y轴的范围


};

#endif // MULTICURVESPLOT_H

部分实现如下:

#include "multicurvesplot.h"
#include "qDebug.h"
#include <QColorDialog>


#pragma execution_character_set("utf-8")

MultiCurvesPlot::MultiCurvesPlot(int _curvesCnt, QWidget *parent):
    QCustomPlot(parent)
    ,allCurvesData(_curvesCnt)
    ,curvesCnt(allCurvesData.size())
    ,autoScroll(true)
    ,tracer(new QCPItemTracer(this))
    ,tracerEnable(false)
    ,lastPoint(new QPointF(0, 0))
{
    this->setInteractions(QCP::iRangeDrag //可平移
                          | QCP::iRangeZoom //可滚轮缩放
//                          | QCP::iSelectPlottables //可选中曲线
                          | QCP::iSelectLegend );//可选中图例
    this->setNoAntialiasingOnDrag(true);//禁用抗锯齿,以提高性能
    connect(this, SIGNAL(selectionChangedByUser()), this, SLOT(when_selectionChangedByUser()));
    connect(this, SIGNAL(legendDoubleClick(QCPLegend*, QCPAbstractLegendItem*, QMouseEvent*)), this, SLOT(when_legendDoubleClick(QCPLegend*, QCPAbstractLegendItem*, QMouseEvent*)));

    this->legend->setVisible(true);//使能图例
    this->axisRect()->insetLayout()->setInsetAlignment(0,Qt::AlignTop|Qt::AlignLeft);//设置图例的位置
    //this->setOpenGl(true);
    qDebug() << "QCustomplot opengl status = " << this->openGl();
    this->yAxis2->setVisible(true);
    connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));//左右y轴同步放缩

    QSharedPointer<QCPAxisTickerDateTime> dateTicker(new QCPAxisTickerDateTime);//日期做X轴
    dateTicker->setDateTimeFormat("hh:mm:ss.zzz\nyyyy-MM-dd");//日期格式(可参考QDateTime::fromString()函数)
    //QDateTime::fromString("");
    this->xAxis->setTicker(dateTicker);//设置X轴为时间轴
    this->xAxis->setTickLabels(true);//显示刻度标签


    /*显示数值的提示框*/
    textLabel = new QCPItemText(this);
    textLabel->setPositionAlignment(Qt::AlignTop|Qt::AlignHCenter);//文本框的原点位置
    textLabel->position->setType(QCPItemPosition::ptAxisRectRatio);//位置类型(当前轴范围的比例)
    textLabel->position->setCoords(0.5, 0); // place position at center/top of axis rect
    textLabel->setText("Text Item Demo");
    textLabel->setFont(QFont(font().family(), 16)); // make font a bit larger
    textLabel->setPen(QPen(Qt::black)); // show black border around text
    textLabel->setPadding(QMargins(2,2,2,2));//边界宽度
    textLabel->setBackgroundColor(Qt::green);

    //单击时指向数值的箭头:
    arrow = new QCPItemLine(this);
    arrow->start->setParentAnchor(textLabel->bottom);    
    //arrow->end->setCoords(4, 1.6); // point to (4, 1.6) in x-y-plot coordinates
    arrow->setHead(QCPLineEnding::esSpikeArrow);
    textLabel->setVisible(false);
    arrow->setVisible(false);

    //游标以及游标文本框设置
    this->setMouseTracking(true);
    tracer->setInterpolating(false);//禁用插值
    tracer->setPen(QPen(Qt::DashLine));//虚线游标
    tracer->setStyle(QCPItemTracer::tsCrosshair);//游标样式:十字星、圆圈、方框等
    //游标的X值文本框
    tracerXText = new QCPItemText(this);
    tracerXText->setPositionAlignment(Qt::AlignBottom|Qt::AlignRight);
    tracerXText->position->setType(QCPItemPosition::ptAxisRectRatio);//位置类型(当前轴范围的比例)
    tracerXText->position->setCoords(0, 0.99); // 在plot中的位置
    tracerXText->position->setParentAnchorX(tracer->position);//X位置锚定到游标的X位置
    tracerXText->setText("no curves..");
    tracerXText->setFont(QFont(font().family(), 12));
    tracerXText->setPen(QPen(Qt::black));
    tracerXText->setBackgroundColor(Qt::green);
    tracerXText->setPadding(QMargins(2,2,2,2));//边界宽度
    tracerXText->setVisible(false);
    //游标的Y值文本框
    tracerYText = new QCPItemText(this);
    tracerYText->setPositionAlignment(Qt::AlignTop|Qt::AlignLeft);
    tracerYText->position->setType(QCPItemPosition::ptAxisRectRatio);//位置类型(当前轴范围的比例)
    tracerYText->position->setCoords(0.01, 0); // 在plot中的位置
    tracerYText->setText("no curves..");
    tracerYText->position->setParentAnchorY(tracer->position);//Y位置锚定到游标的Y位置
    tracerYText->setFont(QFont(font().family(), 12));
    tracerYText->setPen(QPen(Qt::black));
    tracerYText->setBackgroundColor(Qt::green);
    tracerYText->setPadding(QMargins(2,2,2,2));//边界宽度
    tracerYText->setVisible(false);

    /*两点差值的显示框*/
    diffText = new QCPItemText(this);
    diffText->setPositionAlignment(Qt::AlignTop|Qt::AlignRight);
    diffText->setTextAlignment(Qt::AlignTop|Qt::AlignLeft);//文本框的原点位置
    diffText->position->setType(QCPItemPosition::ptAxisRectRatio);//位置类型(当前轴范围的比例)
    diffText->position->setCoords(0.98, 0); // place position at center/top of axis rect
    diffText->setText("dt=0\r\ndy=0");
    diffText->setFont(QFont(font().family(), 16)); // make font a bit larger
    diffText->setPen(QPen(Qt::black)); // show black border around text
    diffText->setPadding(QMargins(2,2,2,2));//边界宽度
    diffText->setBackgroundColor(Qt::green);
    diffText->setVisible(false);

    //右键菜单
    this->setContextMenuPolicy(Qt::ActionsContextMenu);
    //菜单-按钮1
    actshowAllGraph = new QAction("全显");
    connect(actshowAllGraph, SIGNAL(triggered(bool)), this, SLOT(showAllGraph()));
    this->addAction(actshowAllGraph);

    //菜单-按钮2
    actClearDatas = new QAction("清空历史数据");
    connect(actClearDatas, SIGNAL(triggered(bool)), this, SLOT(clearAllData()));
    this->addAction(actClearDatas);


    getColor.resize(curvesCnt);
    for(int i = 0; i < getColor.size();i++)
    {
        getColor[i] = QColor(qrand()%255, qrand()%255, qrand()%255);//生成随机颜色
    }
    getName.resize(curvesCnt);
    for(int i = 0; i < getName.size();i++)//设置默认曲线名称
    {
        getName[i] = QString("curve %1").arg(i);
    }

    for(int i = 0; i < allCurvesData.size();i++)//清空备份的数据源
    {
        allCurvesData[i].keyVec.clear();
        allCurvesData[i].valVec.clear();
    }

    startTimer(30, Qt::CoarseTimer);//定频刷新曲线图
}


void MultiCurvesPlot::showCurves(QList<uint16_t> _idxList)
{
//    qDebug() << "MultiCurvesPlot::showCurves";
//    qDebug() << "MultiCurvesPlot::showCurves:" << idxList;

    this->clearGraphs();//先移除所有的graph
    idxList = _idxList;//备份,可能别的函数需要它
    curveIdx2graphPtr.clear();//清除曲线编号->graph指针的映射
    graphPtr2curveIdx.clear();//清除graph指针->曲线编号的映射


    int graphIdx = 0;

    for(QList<uint16_t>::const_iterator it = idxList.begin(); it != idxList.end(); it++)
    {   //*it为每一个要显示的曲线编号
        uint16_t curveIdx = *it;
        if(curveIdx > allCurvesData.size())
        {
            qDebug() << QString("warning: MultiCurvesPlot::showCurves->超出数据源max index").arg(*it);
            continue;
        }

        this->addGraph(this->xAxis, this->yAxis);
        QCPGraph* pGraph = graph(graphIdx);
        curveIdx2graphPtr[curveIdx] = pGraph;//记录:曲线索引->graph指针的映射
        graphPtr2curveIdx[pGraph] = curveIdx;//记录:graph指针->曲线索引的映射
        pGraph->setData(allCurvesData[curveIdx].keyVec, allCurvesData[curveIdx].valVec, true);//数据源 (todo:*it的合法性)
        pGraph->setPen(QPen(getColor[curveIdx]));//线的颜色随机
        pGraph->setLineStyle(QCPGraph::lsStepLeft);//阶梯线样式        
        //this->graph(graphIdx)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 3));//显示散点
        this->graph(graphIdx)->setName(getName[curveIdx]);
        graphIdx++;        
    }
    if(graphCount() > 0)
        traceGraph = graph(0);
    else
        traceGraph = NULL;
//    showAllGraph();
    this->replot();
}

在窗口类中调用MultiCurvesPlot,

#include "mainwindow.h"
#include "ui_mainwindow.h"

#define CURVE_CNT 5 //最多显示几条曲线

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    plot = new MultiCurvesPlot(CURVE_CNT, this);
    plot->setGeometry(20, 170, 800, 350);//设置位置和大小(但不推荐这么做,应该放进布局中使之能自动调整大小)

    plot->yAxis->setRange(-1.5, 1.5);//设置Y轴的范围
    QList<uint16_t> list;
    list << 0 << 1 << 3;
    plot->showCurves(list);//显示0号、1号、3号曲线

    this->startTimer(20);//启动定时器,用于生成模拟点

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::timerEvent(QTimerEvent *event)
{
    Q_UNUSED(event);

    double t = (double)(QDateTime::currentMSecsSinceEpoch()) / 1000.0;//当前时间

    plot->addData(0, t, qSin(t));//曲线0,sin曲线
    plot->addData(1, t, qCos(t));//曲线1,cos曲线
    plot->addData(2, t, 0.5 * qSin(t));//曲线2,sin
    plot->addData(3, t, 0.5 * qCos(t));//曲线3,cos
    plot->addData(4, t, 1);//曲线4,水平直线

}



void MainWindow::on_checkBox_tracer_clicked(bool checked)
{
    plot->setTracerEnable(checked);
}

void MainWindow::on_checkBox_scroll_clicked(bool checked)
{
    plot->setAutoScroll(checked);
}

下载全部代码

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

previewer1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值