用PyQt5.QtChart實現動態曲線圖


之前一直用爬蟲捉取路由實時上下載的記錄用數據庫保存,有時可用matplotlib來查看時間與上下傳的曲線圖,但是靜態的,查詢的時間段需自行認定,雖然qt的日歷類可以比較方便取日期按日期查,但總覺要點有點麻煩,又不想看24小時的數據,截取最近幾小時觀看數據並實時展示應該是很好的方式,經百度發現pyqt有個很方便的庫來展示動態數據,這個庫是pyqtchart,安裝PyQt時並沒有這個庫的,需另行安裝,pip install pyqtchart安裝就可以了,下面就結合代碼和實際情況記錄一下。

import sqlite3,sys,time
from datetime import datetime
from PyQt5.QtChart import QDateTimeAxis,QValueAxis,QSplineSeries,QChart,QChartView
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QPainter
from PyQt5.QtCore import QDateTime,Qt,QTimer

class ChartView(QChartView,QChart): #原代碼如此,繼承了兩個類,其實去掉QChart也沒影響
    def __init__(self, *args, **kwargs):
        super(ChartView, self).__init__(*args, **kwargs)
        self.connect = sqlite3.connect("netdata.db") #數據庫,表名為t1,包括時間(年月日時分秒的方式,用sqlite的自動時間截生成的,為方便自己看,轉成年月日,結果是個坑),下載速度,上傳速度
        self.resize(1500, 500)
        self.setRenderHint(QPainter.Antialiasing)  # 抗鋸齒,注釋此行曲線很難看
        self.limitminute=240 #設置顯示多少分鍾內的活動
        self.maxspeed = 300 #預設y軸最大值
        self.chart_init()
        self.timer_init()

    def timer_init(self):
        #使用QTimer,2秒觸發一次,更新數據
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.drawLine)
        self.timer.start(2000)

    def chart_init(self):
        self.chart = QChart()
        self.series = QSplineSeries() #這個是平滑曲線類,而QSplineSeries()是折線類,根所自己需求選用,下載數據曲線
        self.series_upload = QSplineSeries() #上傳數據曲線
        #設置曲線名稱
        self.series.setName("下載速度")
        self.series_upload.setName('上傳速度')
        #把曲線添加到QChart的實例中
        self.chart.addSeries(self.series)
        self.chart.addSeries(self.series_upload)
        #聲明並初始化X軸,Y軸
        self.dtaxisX = QDateTimeAxis()
        self.vlaxisY = QValueAxis()
        #設置坐標軸顯示范圍
        self.dtaxisX.setMin(QDateTime.currentDateTime().addSecs(-self.limitminute*60))
        self.dtaxisX.setMax(QDateTime.currentDateTime().addSecs(0))
        self.vlaxisY.setMin(0)    
        self.vlaxisY.setMax(self.maxspeed) #設置y軸最大值

        #設置X軸時間樣式
        self.dtaxisX.setFormat("hh:mm") #關注就是幾小時內的數據,就留時分好了

        #設置坐標軸上的格點
        self.dtaxisX.setTickCount(15) #平均分的刻度分隔
        self.vlaxisY.setTickCount(10)
        #設置坐標軸名稱
        self.dtaxisX.setTitleText("時間")
        self.vlaxisY.setTitleText("速度(M)")
        #設置網格顯示,並設為灰色 
        self.vlaxisY.setGridLineVisible(True)
        self.vlaxisY.setGridLineColor(Qt.gray)
        self.dtaxisX.setGridLineVisible(True)
        self.dtaxisX.setGridLineColor(Qt.gray)
        #把坐標軸添加到chart中
        self.chart.addAxis(self.dtaxisX,Qt.AlignBottom)
        self.chart.addAxis(self.vlaxisY,Qt.AlignLeft)
        #把曲線關聯到坐標軸
        self.series.attachAxis(self.dtaxisX)
        self.series.attachAxis(self.vlaxisY)

        self.series_upload.attachAxis(self.dtaxisX)
        self.series_upload.attachAxis(self.vlaxisY)

        self.setChart(self.chart)
    def drawLine(self):
        #獲取當前時間
        bjtime = QDateTime.currentDateTime()
        #更新X軸坐標
        self.dtaxisX.setMin(bjtime.addSecs(-self.limitminute*60))
        self.dtaxisX.setMax(bjtime.addSecs(0))

        #設Y軸最大值,查詢數據庫最近4小時內的下載最大值,並乘1.2作為y軸最大值
        for xx in self.connect.execute(
                "select max(downdata) from t1 where time > datetime('now','-4 hour','localtime') order by time"):
            if xx:
                self.vlaxisY.setMax(int(xx[0] * 1.2))
            else:
                self.vlaxisY.setMax(self.maxspeed)

                
        if self.series.at(0): #self.serie存在索引0時,也就是起碼有一個數據對過舊數據進行清除,self.series.removePoints兩參數一個是索引,一個是從索引起始刪除多少個數值,兩條數據均如此處理
            if self.series.at(0).x()<bjtime.addSecs(-self.limitminute*60).toMSecsSinceEpoch(): #self.series.at(0).x()其實就是圖像x坐標值,與原始數據可能並不完全相等,小數點后的值是約去了的,bjtime的toMSecsSinceEpoch()其實與time.time()相約,不過前者是整數,是后者的1000倍,所以后面需要轉換
                self.series.removePoints(0, 1)

        if self.series_upload.at(0):
            if self.series_upload.at(0).x()<bjtime.addSecs(-self.limitminute*60).toMSecsSinceEpoch():
                self.series_upload.removePoints(0, 1)

        for xx in self.connect.execute("select * from t1 order by time desc limit 1"):
            #x1 = self.connect.execute("select strftime('%s',?)", (xx[0],)).fetchone()[0] #用此法轉出來的時間截與time.time()整好差8個時區,用sqlite不知如何處理了
            x1=time.mktime(datetime.strptime(xx[0], '%Y-%m-%d %H:%M:%S').timetuple()) #用py內置庫的辦法有日期轉為時間截
            x_time=int(x1)*1000 #再乘1000,以符號格式要求
            y0_value=xx[1] #取得下載數據
            y1_value=xx[2] #取得上傳數據

            #添加數據到曲線末端
            if self.series.at(0):  #因數據庫並非每秒更新,為免相同數據重復錄入,先判斷self.series起碼有一個數據
                if x_time!=self.series.at(self.series.count()-1).x(): #假如最新的時間軸與數據庫取得的不一致就錄入,相同就跳過
                    self.series.append(x_time, y0_value)
            else: #當self.series為空時起碼錄入第一個數據,下面另外一軸同樣處理
                self.series.append(x_time,y0_value)

            if self.series_upload.at(0):
                if x_time!=self.series_upload.at(self.series_upload.count()-1).x():
                    self.series_upload.append(x_time, y1_value)
            else:
                self.series_upload.append(x_time,y1_value)
            #print(self.series.count(),self.series_upload.count())

if __name__ == "__main__":
    app = QApplication(sys.argv)
    view = ChartView()
    view.show()
    sys.exit(app.exec_())

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM