实现了qt绘制曲线功能,包含arm触摸屏多点触控缩放(只支持两点),实时曲线绘制,数据点根据绘制宽度优化,跟踪点数据获取,双坐标等功能
头文件 plot.h
/*
* 作者:老人与海
* 博客:https://blog.csdn.net/qq_41340733
* 代码不保证稳定性,请勿用于商业用途
*/
#ifndef PLOT_H
#define PLOT_H
#include
#include
#include
#include
#include
#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
#include
#include
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
#include "qtimer.h"
#include "plot.h"
#include
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
#include
#include "qgridlayout.h"
#include "math.h"
#include "qpainter.h"
#include
#include
#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
链接: 源码下载