Qt自定义Plot实现曲线绘制的详细过程


Posted in Python onNovember 02, 2021

简介

实现了qt绘制曲线功能,包含arm触摸屏多点触控缩放(只支持两点),实时曲线绘制,数据点根据绘制宽度优化,跟踪点数据获取,双坐标等功能

演示

Qt自定义Plot实现曲线绘制的详细过程

Qt自定义Plot实现曲线绘制的详细过程

代码

头文件 plot.h

/*
 * 作者:老人与海
 * 博客:https://blog.csdn.net/qq_41340733
 * 代码不保证稳定性,请勿用于商业用途
 */

#ifndef PLOT_H
#define PLOT_H

#include <QWidget>
#include <QTimer>
#include <QList>
#include <QWheelEvent>
#include <QMouseEvent>
#include "plotdata.h"
typedef struct _RangeVal
{
    double Beg;
    double End;
    double offSet;
    bool IsOffSet;
    _RangeVal()
    {
        Beg=0;
        End=0;
        offSet=0;
        IsOffSet=false;
    }
}_RangeVal;

//鼠标跟中点
class Tracking
{
public:
    Tracking()
    {
        m_IsDraw=false;
        m_IsMove=false;
        m_IsInit=false;
        m_First=false;
        m_radius=25;
        m_PointCenter=QPoint(0,0);
    }
    ~Tracking(){}
    bool getIsRange(QPoint point)
    {
        if(point.x()>(m_PointCenter.x()-m_radius)&&
                point.x()<(m_PointCenter.x()+m_radius)&&
                point.y()>(m_PointCenter.y()-m_radius)&&
                point.y()<(m_PointCenter.y()+m_radius))
        {
            return true;
        }else{
            return false;
        }
    }
public:
    bool m_IsDraw;
    bool m_IsMove;//鼠标焦点
    bool m_IsInit;
    bool m_First;
    int m_radius;
    QPoint m_PointCenter;//绘制圆心
};

/**
 * @brief The CoorAxis class
 * 类名:CoorAxis
 * 功能:坐标轴部件,用来绘制XY坐标轴,使用时必须用Plot的ticker类进行初始化,否则无法正常绘制坐标
 */
class CoorAxis : public QWidget
{
    Q_OBJECT
public:
    explicit CoorAxis(QWidget *parent = nullptr,Ticker *ticker= nullptr);
    virtual ~CoorAxis();

    Ticker *ticker(){ return m_Ticker; }
    void mouseMove(QMouseEvent *event);
signals:
    void sig_UpdateUI();
public slots:
    void slots_sendCoorWheel(QWheelEvent *event);
    void slots_sendCoorMouse(QMouseEvent *event,QPoint point,bool IsFirst=false);
protected:
   void paintEvent(QPaintEvent *event);
   void drawTickerX(QPainter *painter);//绘制X轴
   void drawTickerY_L(QPainter *painter);//绘制左边Y轴
   void drawTickerY_R(QPainter *painter);//绘制右边Y轴
   void wheelEvent(QWheelEvent *event);//滚轮事件,缩放曲线图像
   void mousePressEvent(QMouseEvent *event);//按下事件,记录坐标和按下标志
   void mouseReleaseEvent(QMouseEvent *event);//抬起事件,取消按下标志
   void mouseMoveEvent(QMouseEvent *event);//鼠标移动事件,移动曲线

protected:
   Ticker *m_Ticker;
   int m_LastLength;
   int m_margin;

   bool IsPress=false;
   QPoint m_FirstPoint;
};


class Plot : public QWidget
{
    Q_OBJECT
public:
    explicit Plot(QWidget *parent=nullptr);
    virtual ~Plot();

    void ValueInit();
    Ticker *tickerX();
    Ticker *tickerL();
    Ticker *tickerR();
    void addGraph();
    void GraphClear(){ m_XyGraph.clear(); }
    int getGraphCount(){ return m_XyGraph.count(); }
    _XYList *Graph(int index);
    QList<_XYList> &GraphGroup(){ return m_XyGraph; }
    void setTrackVisibel(bool isVisbel){ m_Tracking.m_IsDraw=isVisbel; }
    bool getTrackVisibel(){ return m_Tracking.m_IsDraw; }
    void setIsDrawPath(bool value){ m_IsDrawPath=value; }
    bool getIsDrawPath(){ return m_IsDrawPath; }

    void coordToPixel(_COORDINATEVALUE *Value,_TICKERTYPE Type);
    void coordToPixel(double &key,double &value,_TICKERTYPE Type);
    void PixelToCoorKey(double &key,_TICKERTYPE Type);
    void PixelToCoorValue(double &value,_TICKERTYPE Type);
    void PixelToCoord(QPointF &pointf,_TICKERTYPE Type);
    void getOptimizedLineData(QVector<QPointF> *lineData,int index);

    void calculateTrackValue(QPoint Point);
    void setBackgroundColor(QColor color){ m_BackgroundColor=color; }
    QColor getBackgroundColor(){ return m_BackgroundColor; }
signals:
    void sig_sendCoorWheel(QWheelEvent *event);
    void sig_sendCoorMouse(QMouseEvent *event,QPoint point,bool IsFirst=false);
    void sig_UpdateUI();
    void sig_UpdateTrack();//刷新跟踪点数据
public slots:

protected:
   void paintEvent(QPaintEvent *event);
   void drawGrid(QPainter *painter);//绘制网格
   void drawPath(QPainter *painter);//绘制曲线
   void drawCursor(QPainter *painter);//绘制跟踪点
   void drawTickerX(QPainter *painter);//绘制X轴
   void drawTickerY_L(QPainter *painter);//绘制左边Y轴
   void drawTickerY_R(QPainter *painter);//绘制右边Y轴

   void wheelEvent(QWheelEvent *event);//滚轮事件,缩放曲线图像
   void mousePressEvent(QMouseEvent *event);//按下事件,记录坐标和按下标志
   void mouseReleaseEvent(QMouseEvent *event);//抬起事件,取消按下标志
   void mouseMoveEvent(QMouseEvent *event);//鼠标移动事件,移动曲线
   void showEvent(QShowEvent *event);//显示事件
   bool event(QEvent *event);//事件,实现多点触控功能,目前只对两个触控点做了处理

public:
   QPixmap m_PixMapCursor;//跟踪点绘制图像缓冲
   Tracking m_Tracking;
protected:
   Ticker *m_TickerX;
   Ticker *m_TickerL;
   Ticker *m_TickerR;

   bool IsPress=false;
   bool m_IsTouch=false;

   QPoint m_ClickedPoint;
   qreal m_scaleFactorX=0.0;
   qreal m_scaleFactorY=0.0;
   QList<_XYList> m_XyGraph;

   QColor m_BackgroundColor;
   bool m_IsDrawPath;//是否绘制曲线,在多线程添加数据的时候调用,添加数据时禁止绘制曲线
private:
   friend class Ticker;
};

class CurveWid : public QWidget
{
    Q_OBJECT
public:
    explicit CurveWid(QWidget *parent = nullptr);

    void DataInit();
    void reUpdate();
    void reUpdateAll();
signals:

public slots:
    void slots_UpdateUI();
    void slots_TimeOut();
protected:
    void paintEvent(QPaintEvent *event);
public:
    CoorAxis *m_AxisX;
    CoorAxis *m_AxisL;
    CoorAxis *m_AxisR;

    Plot *m_Plot;
    QTimer *m_Timer;
};


#endif // PLOT_H

头文件plotdata.h

/*
 * 作者:老人与海 
 * 博客:https://blog.csdn.net/qq_41340733
 * 代码不保证稳定性,请勿用于商业用途
 */


#ifndef PLOTDATA_H
#define PLOTDATA_H

#include <QObject>
#include <QVector>
#include <QColor>
class CoorAxis;

//_COORDINATEVALUE单个数据点结构体
typedef struct _COORDINATEVALUE
{
    double Key;
    double Value;
    _COORDINATEVALUE()
    {
        Key=0;
        Value=0;
    }
}_COORDINATEVALUE;
//坐标轴绘制的文本类型,包括时间毫秒,时间秒,数值
typedef enum _TEXT_TYPE
{
    _TimeMs,
    _TimeS,
    CountVal
}_TEXT_TYPE;
//坐标轴类型,XAxis表示X轴,YAxis_L表示左边的Y轴,YAxis_R表示右边的Y轴
typedef enum _TICKERTYPE
{
    XAxis=0,
    YAxis_L=1,
    YAxis_R=2
}_TICKERTYPE;
//坐标缩放前的坐标值,_begVal坐标起始值,_endVal坐标结束值,_siteVal坐标中点,
//多点触控的时候先记录坐标的原始值,然后根据手势距离对原始进行缩放
typedef struct _Scale
{
    double _begVal=0.0;
    double _endVal=0.0;
    double _siteVal=0.0;
}_Scale;

/**
 * @brief The _XYList class
 * 功能:单条曲线的数据结构类型,包括坐标轴类型,曲线颜色,
 * 最多可绘制的曲线点数,等功能等;
 */
class _XYList
{
public:
    explicit _XYList();
    virtual ~_XYList(){}

    int Count();//实际有效的数据点
    int CountAll();//所有的数据点,包括被移除的记录
    int getRemoveCount(){ return  m_removeCount; }
    _COORDINATEVALUE &getPoint(int index);
    void addData(const double Key,const double Value);
    void removefirst();
    void cleanBuff();
    int size() const { return m_CoodValue.size()-m_removeCount; }
    QVector<_COORDINATEVALUE> *data();

    void setPointCountMax(int Count);
    int getPointMax(){ return m_MaxPointCount; }
    void setColor(QColor color){ m_Color=color; }
    QColor getColor(){ return m_Color; }
    void setAxisType(_TICKERTYPE type){ m_Type=type; }
    _TICKERTYPE &getAixsType(){ return m_Type; }

    int getBegindex(const double &TmpKey);
    int getEndindex(const double &TmpKey);
    int getCoorToIndex(const double &TmpKey);

    _COORDINATEVALUE getTrackValue(){ return TrackValue; }
    void setTrackValue(const _COORDINATEVALUE value){ TrackValue=value; }
    void setTrackValue(const double key,const double value){
        TrackValue.Key=key;
        TrackValue.Value=value;
    }
    bool TracckIsValid(){ return IsValid; }
    void setTrackIsvalid(bool val){ IsValid=val; }

    void setVisible(bool val){ m_Visible=val; }
    bool getVisible(){ return m_Visible; }

    _COORDINATEVALUE getMaxValue(){ return m_MaxValue; }
    _COORDINATEVALUE getMinValue(){ return m_MinValue; }

    QVector<_COORDINATEVALUE>::const_iterator constBegIte(){ return (m_CoodValue.begin()+m_removeCount); }
    QVector<_COORDINATEVALUE>::const_iterator constEndIte(){ return m_CoodValue.end(); }
protected:
    QVector<_COORDINATEVALUE> m_CoodValue;
    int m_MaxPointCount;//实际数据点,根据总数据点是否达到这和数目而对m_removeCount进行操作
    int m_removeCount;//被移除的次数,会先记录,到达一定数据时候会重新分分配m_CoodValue大小,提高了添加固定点数据时候的速度
    _TICKERTYPE m_Type;
    QColor m_Color;
    _COORDINATEVALUE TrackValue;//踪点的值
    bool IsValid;//判断跟踪点的值是否有效
    bool m_Visible;//是否绘制,既可见度

    _COORDINATEVALUE m_MaxValue;
    _COORDINATEVALUE m_MinValue;
};
/**
 * @brief The Ticker class
 * 坐标轴数据结构类,这里描述了坐标的主刻度,子刻度,刻度长度,
 * 坐标范围,坐标轴的类型(X还是Y 参考_TICKERTYPE)等信息
 */
class Ticker
{
public:
    explicit Ticker()
    {
       MainLineCount=15;
       SubLineCount=6;
       Mainlength=10;
       Sublength=10;
       Type=_TICKERTYPE::XAxis;
       m_RangeBeg=0;
       m_RangeEnd=500;
    }
    virtual ~Ticker(){}

    void setMainLineCount(const int count){ MainLineCount=count; }
    void setSubLineCount(const int count){ SubLineCount=count; }
    void setMainLineLength(const int length){ Mainlength=length; }
    void setSubLineLength(const int length){ Sublength=length; }
    void setAxisType(_TICKERTYPE type){ Type=type; }
    void setTextType(_TEXT_TYPE Type){ m_TextType=Type; }
    void setRangeBeg(const double &Value);
    void setRangeEnd(const double &Value);
    void setRange(const double &BegValue,const double &EndValue);
    void moveRange(const double &Value);//根据数值偏移,正数往前,负数往后
    void moveRangePercent(const double &Value);//根据百分比偏移,正数往前,负数往后
    void setZoom(const double value);//根据中心值进行缩放,大于1表示放大,小于1表示缩小

    int getMainLineCount(){ return MainLineCount; }
    int getSubLineCount(){ return SubLineCount; }
    int getMainLineLength(){ return Mainlength; }
    int getSubLineLength(){ return Sublength; }
    _TICKERTYPE getAxisType(){ return Type; }
    double getRangeBeg(){ return m_RangeBeg; }
    double getRangeEnd(){ return m_RangeEnd; }
    double getRangeMidvalue(){ return (m_RangeBeg+(m_RangeEnd-m_RangeBeg)/2.0); }//获取轴的中心值
    _Scale &getScale(){ return m_Scale; }
    void InitScale();
    void setScaleCoor(const double scaleFactor);//根据siteVal左边点进行缩放
    _TEXT_TYPE getTextType(){ return m_TextType; }
protected:
    int MainLineCount;
    int SubLineCount;
    int Mainlength;
    int Sublength;
    double m_RangeBeg;
    double m_RangeEnd;

    _TICKERTYPE Type;
    _TEXT_TYPE m_TextType;
    _Scale m_Scale;
private:
   friend class CoorAxis;
};
#endif // ADDPOINT_H

头文件mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "qtimer.h"
#include "plot.h"
#include <QLabel>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE


class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

public slots:
    void slots_TimeOut();
    void slots_Refresh();
    void slots_UpdateTrack();
protected:
    QTimer *m_Timer;
    QTimer *m_TimerRefresh;

    CurveWid *m_Curve;

    QVector<QLabel *> m_labList;
    bool IsAddOk=false;
private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

c文件

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

#include <QDateTime>
#include <QDebug>
#include "qgridlayout.h"
#include "math.h"
#include "qpainter.h"
#include <QVBoxLayout>
#include <QGridLayout>

#define _POINTCOUNT 50000
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->statusbar->hide();


    ui->centralwidget->setStyleSheet("QWidget{background-color: rgb(0, 0, 0);}");
    m_Curve=new CurveWid;
    connect(m_Curve->m_Plot,&Plot::sig_UpdateTrack,this,&MainWindow::slots_UpdateTrack);

    m_Timer=new QTimer(this);
    m_TimerRefresh=new QTimer(this);
    m_Timer->start(100);
    m_TimerRefresh->start(100);
    connect(m_Timer,&QTimer::timeout,this,&MainWindow::slots_TimeOut);
    connect(m_TimerRefresh,&QTimer::timeout,this,&MainWindow::slots_Refresh);

    m_Curve->m_AxisL->ticker()->setRange(-100,1200);
    m_Curve->m_AxisR->ticker()->setRange(-100,600);
    m_Curve->m_AxisX->ticker()->setRange(-100,_POINTCOUNT*0.01);
    qint64 Time=QDateTime::currentMSecsSinceEpoch();
    m_Curve->m_AxisX->ticker()->setTextType(_TEXT_TYPE::_TimeMs);
//    m_Curve->m_AxisX->ticker()->setRange(Time-600000,Time);

    QGridLayout *G_layout=new QGridLayout;
    G_layout->setVerticalSpacing(3);
    G_layout->setContentsMargins(0,0,0,0);
    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
    for(int i=0;i<16;i++)
    {
        QLabel *labValue=new QLabel;
        labValue->setText(QString("Qlabel %1").arg(i+1));
        labValue->setAlignment(Qt::AlignCenter);
        labValue->setStyleSheet("QLabel{color:rgb(0, 122, 255);}");
        labValue->setFixedHeight(36);
        G_layout->addWidget(labValue,i/8,i%8);

        QColor color=QColor(qrand() % 256, qrand() % 256, qrand() % 256);
        int r,g,b;
        color.getRgb(&r,&g,&b);
        labValue->setStyleSheet(QString("QLabel{ color:rgb(%0,%1,%2);}").arg(r).arg(g).arg(b));
        m_labList.append(labValue);

        m_Curve->m_Plot->addGraph();
        m_Curve->m_Plot->Graph(i)->setColor(color);
        m_Curve->m_Plot->Graph(i)->setPointCountMax(_POINTCOUNT);
        if((i%2)!=0){
            m_Curve->m_Plot->Graph(i)->setAxisType(_TICKERTYPE::YAxis_R);
        }else{
            m_Curve->m_Plot->Graph(i)->setAxisType(_TICKERTYPE::YAxis_L);
        }
    }

    QVBoxLayout *V_layout=new QVBoxLayout;
    V_layout->setContentsMargins(0,0,0,0);
    V_layout->addWidget(m_Curve);
    V_layout->addLayout(G_layout);

    ui->centralwidget->setLayout(V_layout);


    for(int i=0;i<_POINTCOUNT*0.01;i++){
        slots_TimeOut();
    }
    IsAddOk=true;
}

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

void MainWindow::slots_TimeOut()
{
    double w = (3.14/100)*5;  //w为角速度 ,可以理解为波浪的密度,越大密度越大
    double A = 40;    //  A表示振幅,可以理解为水波的高度,越大高度越高
    static double x;
    x++;
    double waveY = (double)(A * sin(w * x ));// waveY随着x的值改变而改变,从而得到正弦曲线

    static double count=0;
    count+=1;
    qint64 Time=QDateTime::currentMSecsSinceEpoch();
    for(int i=0;i<m_Curve->m_Plot->getGraphCount();i++)
    {
        int num = qrand()%(200-100)+100;//产生300-500范围的随机岁
        float data=0;//除以10获取 30.0到50.0的随机数
        data+=80*i+num;
//        m_Curve->m_Plot->Graph(i)->addData(count,data);
        m_Curve->m_Plot->Graph(i)->addData(count,waveY+80*i);
    }

    if(IsAddOk)
    {
        if(count>m_Curve->m_AxisX->ticker()->getRangeEnd())
        {
            m_Curve->m_AxisX->ticker()->moveRange(count-m_Curve->m_AxisX->ticker()->getRangeEnd());
        }
    }
}

void MainWindow::slots_Refresh()
{
    m_Curve->reUpdateAll();
}

void MainWindow::slots_UpdateTrack()
{
    for(int i=0;i<m_Curve->m_Plot->getGraphCount();i++){
        m_labList[i]->setText(QString::number(m_Curve->m_Plot->Graph(i)->getTrackValue().Value,'f',2));
    }
}

源码下载

工程:TestCurve
链接: 源码下载

到此这篇关于Qt自定义Plot实现曲线绘制的文章就介绍到这了,更多相关Qt自定义曲线绘制内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python插入排序算法的实现代码
Nov 21 Python
一文总结学习Python的14张思维导图
Oct 17 Python
基于Django与ajax之间的json传输方法
May 29 Python
python 从csv读数据到mysql的实例
Jun 21 Python
tensorflow使用神经网络实现mnist分类
Sep 08 Python
Python函数基础实例详解【函数嵌套,命名空间,函数对象,闭包函数等】
Mar 30 Python
python获取点击的坐标画图形的方法
Jul 09 Python
在notepad++中实现直接运行python代码
Dec 18 Python
基于torch.where和布尔索引的速度比较
Jan 02 Python
Python学习之os模块及用法
Jun 03 Python
Jupyter Notebook 安装配置与使用详解
Jan 06 Python
全网最全python库selenium自动化使用详细教程
Jan 12 Python
Python 正则模块详情
Nov 02 #Python
Python 数据可视化之Bokeh详解
Nov 02 #Python
Python 数据可视化之Matplotlib详解
分位数回归模型quantile regeression应用详解及示例教程
Python常遇到的错误和异常
Nov 02 #Python
Python 数据可视化之Seaborn详解
关于python中模块和重载的问题
You might like
php 设计模式之 工厂模式
2008/12/19 PHP
php实现在服务器上创建目录的方法
2015/03/16 PHP
PHP中使用curl入门教程
2015/07/02 PHP
详解WordPress中过滤链接与过滤SQL语句的方法
2015/12/18 PHP
使用PHP如何实现高效安全的ftp服务器(一)
2015/12/20 PHP
在Mac OS上搭建Nginx+PHP+MySQL开发环境的教程
2015/12/21 PHP
Yii2中关联查询简单用法示例
2016/08/10 PHP
PHP基于DOMDocument解析和生成xml的方法分析
2017/07/17 PHP
php curl上传、下载、https登陆实现代码
2017/07/23 PHP
php PDO属性设置与操作方法分析
2018/12/27 PHP
TP5框架实现一次选择多张图片并预览的方法示例
2020/04/04 PHP
探讨jQuery的ajax使用场景(c#)
2013/12/03 Javascript
中止javascript执行的方法
2014/02/14 Javascript
Bootstrap入门书籍之(零)Bootstrap简介
2016/02/17 Javascript
jQuery图片加载显示loading效果
2016/11/04 Javascript
概述javascript在Google IE中的调试技巧
2016/11/24 Javascript
微信小程序中使元素占满整个屏幕高度实现方法
2016/12/14 Javascript
基于Vue实现后台系统权限控制的示例代码
2017/08/29 Javascript
nodejs读取并去重excel文件
2018/04/22 NodeJs
详解基于Vue-cli搭建的项目如何和后台交互
2018/06/29 Javascript
Vue项目中添加锁屏功能实现思路
2018/06/29 Javascript
基于elementUI实现图片预览组件的示例代码
2019/03/31 Javascript
微信小程序云开发之模拟后台增删改查
2019/05/16 Javascript
[04:49]2014DOTA2国际邀请赛 Newbee顺利挺进总决赛 ImbaTV独家专访
2014/07/19 DOTA
python中的代码编码格式转换问题
2015/06/10 Python
django模板语法学习之include示例详解
2017/12/17 Python
python日志logging模块使用方法分析
2019/05/23 Python
python+openCV利用摄像头实现人员活动检测
2019/06/22 Python
Python使用百度api做人脸对比的方法
2019/08/28 Python
Python 将json序列化后的字符串转换成字典(推荐)
2020/01/06 Python
2014元旦晚会策划方案
2014/02/19 职场文书
生物科学专业自荐书
2014/06/20 职场文书
2014年小学重阳节活动策划方案
2014/09/16 职场文书
2015新年寄语(一句话)
2014/12/08 职场文书
郭明义电影观后感
2015/06/08 职场文书
导游词之黄果树瀑布
2019/09/20 职场文书