Qt自定義Plot實(shí)現(xiàn)曲線繪制的詳細(xì)過程
簡(jiǎn)介
實(shí)現(xiàn)了qt繪制曲線功能,包含arm觸摸屏多點(diǎn)觸控縮放(只支持兩點(diǎn)),實(shí)時(shí)曲線繪制,數(shù)據(jù)點(diǎn)根據(jù)繪制寬度優(yōu)化,跟蹤點(diǎn)數(shù)據(jù)獲取,雙坐標(biāo)等功能
演示
代碼
頭文件 plot.h
/* * 作者:老人與海 * 博客:https://blog.csdn.net/qq_41340733 * 代碼不保證穩(wěn)定性,請(qǐng)勿用于商業(yè)用途 */ #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; //鼠標(biāo)跟中點(diǎn) 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;//鼠標(biāo)焦點(diǎn) bool m_IsInit; bool m_First; int m_radius; QPoint m_PointCenter;//繪制圓心 }; /** * @brief The CoorAxis class * 類名:CoorAxis * 功能:坐標(biāo)軸部件,用來繪制XY坐標(biāo)軸,使用時(shí)必須用Plot的ticker類進(jìn)行初始化,否則無法正常繪制坐標(biāo) */ 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);//按下事件,記錄坐標(biāo)和按下標(biāo)志 void mouseReleaseEvent(QMouseEvent *event);//抬起事件,取消按下標(biāo)志 void mouseMoveEvent(QMouseEvent *event);//鼠標(biāo)移動(dòng)事件,移動(dòng)曲線 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();//刷新跟蹤點(diǎn)數(shù)據(jù) public slots: protected: void paintEvent(QPaintEvent *event); void drawGrid(QPainter *painter);//繪制網(wǎng)格 void drawPath(QPainter *painter);//繪制曲線 void drawCursor(QPainter *painter);//繪制跟蹤點(diǎn) 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);//按下事件,記錄坐標(biāo)和按下標(biāo)志 void mouseReleaseEvent(QMouseEvent *event);//抬起事件,取消按下標(biāo)志 void mouseMoveEvent(QMouseEvent *event);//鼠標(biāo)移動(dòng)事件,移動(dòng)曲線 void showEvent(QShowEvent *event);//顯示事件 bool event(QEvent *event);//事件,實(shí)現(xiàn)多點(diǎn)觸控功能,目前只對(duì)兩個(gè)觸控點(diǎn)做了處理 public: QPixmap m_PixMapCursor;//跟蹤點(diǎn)繪制圖像緩沖 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;//是否繪制曲線,在多線程添加數(shù)據(jù)的時(shí)候調(diào)用,添加數(shù)據(jù)時(shí)禁止繪制曲線 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 * 代碼不保證穩(wěn)定性,請(qǐng)勿用于商業(yè)用途 */ #ifndef PLOTDATA_H #define PLOTDATA_H #include <QObject> #include <QVector> #include <QColor> class CoorAxis; //_COORDINATEVALUE單個(gè)數(shù)據(jù)點(diǎn)結(jié)構(gòu)體 typedef struct _COORDINATEVALUE { double Key; double Value; _COORDINATEVALUE() { Key=0; Value=0; } }_COORDINATEVALUE; //坐標(biāo)軸繪制的文本類型,包括時(shí)間毫秒,時(shí)間秒,數(shù)值 typedef enum _TEXT_TYPE { _TimeMs, _TimeS, CountVal }_TEXT_TYPE; //坐標(biāo)軸類型,XAxis表示X軸,YAxis_L表示左邊的Y軸,YAxis_R表示右邊的Y軸 typedef enum _TICKERTYPE { XAxis=0, YAxis_L=1, YAxis_R=2 }_TICKERTYPE; //坐標(biāo)縮放前的坐標(biāo)值,_begVal坐標(biāo)起始值,_endVal坐標(biāo)結(jié)束值,_siteVal坐標(biāo)中點(diǎn), //多點(diǎn)觸控的時(shí)候先記錄坐標(biāo)的原始值,然后根據(jù)手勢(shì)距離對(duì)原始進(jìn)行縮放 typedef struct _Scale { double _begVal=0.0; double _endVal=0.0; double _siteVal=0.0; }_Scale; /** * @brief The _XYList class * 功能:?jiǎn)螚l曲線的數(shù)據(jù)結(jié)構(gòu)類型,包括坐標(biāo)軸類型,曲線顏色, * 最多可繪制的曲線點(diǎn)數(shù),等功能等; */ class _XYList { public: explicit _XYList(); virtual ~_XYList(){} int Count();//實(shí)際有效的數(shù)據(jù)點(diǎn) int CountAll();//所有的數(shù)據(jù)點(diǎn),包括被移除的記錄 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;//實(shí)際數(shù)據(jù)點(diǎn),根據(jù)總數(shù)據(jù)點(diǎn)是否達(dá)到這和數(shù)目而對(duì)m_removeCount進(jìn)行操作 int m_removeCount;//被移除的次數(shù),會(huì)先記錄,到達(dá)一定數(shù)據(jù)時(shí)候會(huì)重新分分配m_CoodValue大小,提高了添加固定點(diǎn)數(shù)據(jù)時(shí)候的速度 _TICKERTYPE m_Type; QColor m_Color; _COORDINATEVALUE TrackValue;//蹤點(diǎn)的值 bool IsValid;//判斷跟蹤點(diǎn)的值是否有效 bool m_Visible;//是否繪制,既可見度 _COORDINATEVALUE m_MaxValue; _COORDINATEVALUE m_MinValue; }; /** * @brief The Ticker class * 坐標(biāo)軸數(shù)據(jù)結(jié)構(gòu)類,這里描述了坐標(biāo)的主刻度,子刻度,刻度長(zhǎng)度, * 坐標(biāo)范圍,坐標(biāo)軸的類型(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);//根據(jù)數(shù)值偏移,正數(shù)往前,負(fù)數(shù)往后 void moveRangePercent(const double &Value);//根據(jù)百分比偏移,正數(shù)往前,負(fù)數(shù)往后 void setZoom(const double value);//根據(jù)中心值進(jìn)行縮放,大于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);//根據(jù)siteVal左邊點(diǎn)進(jìn)行縮放 _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;//產(chǎn)生300-500范圍的隨機(jī)歲 float data=0;//除以10獲取 30.0到50.0的隨機(jī)數(shù) 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
鏈接: 源碼下載
到此這篇關(guān)于Qt自定義Plot實(shí)現(xiàn)曲線繪制的文章就介紹到這了,更多相關(guān)Qt自定義曲線繪制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python 3.10 的首個(gè) PEP 誕生,內(nèi)置類型 zip() 迎來新特性(推薦)
這篇文章主要介紹了Python 3.10 的首個(gè) PEP 誕生,內(nèi)置類型 zip() 迎來新特性,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07使用Python腳本來獲取Cisco設(shè)備信息的示例
這篇文章主要介紹了編寫Python腳本來獲取Python腳本來獲取Cisco設(shè)備信息的教程,文中的示例是獲取一臺(tái)思科交換機(jī)的腳本,需要的朋友可以參考下2015-05-05Python的數(shù)據(jù)結(jié)構(gòu)與算法的隊(duì)列詳解(3)
這篇文章主要為大家詳細(xì)介紹了Python的隊(duì)列,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03Python的Django框架下管理站點(diǎn)的基本方法
這篇文章主要介紹了Python的Django框架下管理站點(diǎn)的基本方法,需是Django站點(diǎn)部署的基礎(chǔ),要的朋友可以參考下2015-07-07十個(gè)簡(jiǎn)單使用的Python自動(dòng)化腳本分享
今天小編給大家分享10個(gè)Python高級(jí)腳本,幫助我們減少無謂的時(shí)間浪費(fèi),提高工作學(xué)習(xí)中的效率。文中示例代碼講解詳細(xì),需要的可以參考一下2022-05-05Python入門教程(四十)Python的NumPy數(shù)組創(chuàng)建
這篇文章主要介紹了Python入門教程(四十)Python的NumPy數(shù)組創(chuàng)建,NumPy 用于處理數(shù)組,NumPy 中的數(shù)組對(duì)象稱為 ndarray,我們可以使用 array() 函數(shù)創(chuàng)建一個(gè) NumPy ndarray 對(duì)象,需要的朋友可以參考下2023-05-05關(guān)于Python正則表達(dá)式 findall函數(shù)問題詳解
在寫正則表達(dá)式的時(shí)候總會(huì)遇到不少的問題,本文講述了Python正則表達(dá)式中 findall()函數(shù)和多個(gè)表達(dá)式元組相遇的時(shí)候會(huì)出現(xiàn)的問題2018-03-03使用python獲取csv文本的某行或某列數(shù)據(jù)的實(shí)例
下面小編就為大家分享一篇使用python獲取csv文本的某行或某列數(shù)據(jù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-04-04