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的Twisted框架编写简单的网络客户端
Apr 16 Python
Python减少循环层次和缩进的技巧分析
Mar 15 Python
Python实现代码统计工具(终极篇)
Jul 04 Python
用python处理MS Word的实例讲解
May 08 Python
TensorFlow实现iris数据集线性回归
Sep 07 Python
Python使用pandas对数据进行差分运算的方法
Dec 22 Python
基于python生成器封装的协程类
Mar 20 Python
解决py2exe打包后,总是多显示一个DOS黑色窗口的问题
Jun 21 Python
Python3 利用face_recognition实现人脸识别的方法
Mar 13 Python
Python基于gevent实现高并发代码实例
May 15 Python
详解Anaconda 的安装教程
Sep 23 Python
使用Python拟合函数曲线
Apr 14 Python
Python 正则模块详情
Nov 02 #Python
Python 数据可视化之Bokeh详解
Nov 02 #Python
Python 数据可视化之Matplotlib详解
分位数回归模型quantile regeression应用详解及示例教程
Python常遇到的错误和异常
Nov 02 #Python
Python 数据可视化之Seaborn详解
关于python中模块和重载的问题
You might like
站长助手-网站web在线管理程序 v1.0 下载
2007/05/12 PHP
php 文件上传系统手记
2009/10/26 PHP
QueryPath PHP 中的jQuery
2010/04/11 PHP
PHP遍历二维数组的代码
2011/04/22 PHP
用Json实现PHP与JavaScript间数据交换的方法详解
2013/06/20 PHP
php堆排序(heapsort)练习
2013/11/13 PHP
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 2611816 bytes)
2014/11/08 PHP
Laravel与CI框架中截取字符串函数
2016/05/08 PHP
PHP从二维数组得到N层分类树的实现代码
2016/10/11 PHP
php查找字符串中第一个非0的位置截取
2017/02/27 PHP
php实现数组纵向转横向并过滤重复值的方法分析
2017/05/29 PHP
PHP实现打包zip并下载功能
2018/06/12 PHP
PHP设计模式之建造者模式定义与用法简单示例
2018/08/13 PHP
jquery ajax同步异步的执行最终解决方案
2013/04/26 Javascript
javascript实现数组中的内容随机输出
2015/08/11 Javascript
JS实现选中当前菜单后高亮显示的导航条效果
2015/10/15 Javascript
vue-cli下的vuex的简单Demo图解(实现加1减1操作)
2018/02/26 Javascript
解决nodejs的npm命令无反应的问题
2018/05/17 NodeJs
微信小程序排坑指南详解
2018/05/23 Javascript
vue elementUI tree树形控件获取父节点ID的实例
2018/09/12 Javascript
从0到1构建vueSSR项目之node以及vue-cli3的配置
2019/03/07 Javascript
Python 过滤字符串的技巧,map与itertools.imap
2008/09/06 Python
深入解析Python中的WSGI接口
2015/05/11 Python
详解Python网络爬虫功能的基本写法
2016/01/28 Python
win8下python3.4安装和环境配置图文教程
2018/07/31 Python
使用Selenium破解新浪微博的四宫格验证码
2018/10/19 Python
Python写一个基于MD5的文件监听程序
2019/03/11 Python
如何在scrapy中捕获并处理各种异常
2020/09/28 Python
html5本地存储_动力节点Java学院整理
2017/07/12 HTML / CSS
Waterford美国官网:爱尔兰水晶制品品牌
2017/04/26 全球购物
PHP面试题集
2016/12/18 面试题
珠宝店促销方案
2014/03/21 职场文书
员工工作表扬信
2015/05/05 职场文书
2015暑期社会实践通讯稿
2015/07/18 职场文书
关于公司年会的开幕词
2016/03/04 职场文书
Python实现抖音热搜定时爬取功能
2022/03/16 Python