我想繪製一些從簡單的投票應用程式產生的資料的圖表。我過去曾對 pyplot 進行過修改,但我沒有嘗試從頭開始創建任何東西。幸運的是,它非常流行,並且在 StackOverflow 和其他地方可以找到大量範例。
我進行了搜索,並從這個與隨時間更新圖表相關的答案開始。
import matplotlib.pyplot as plt import numpy as np # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() x = np.linspace(0, 6*np.pi, 100) y = np.sin(x) fig = plt.figure() ax = fig.add_subplot(111) line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma for phase in np.linspace(0, 10*np.pi, 500): line1.set_ydata(np.sin(x phase)) fig.canvas.draw() fig.canvas.flush_events()
此程式碼動畫改變正弦波的相位。
前兩行導入我想要使用的函式庫:matplotlib.pyplot 進行繪圖和處理 GUI。
ion() 方法,如果我理解的話(儘管我可能不理解),使 pyplot 驅動 GUI。您也可以在 tkinter 程式中使用它,或使用它來產生靜態圖像,但在我們的範例中,讓它為我們處理圖形的 GUI 是有意義的。 (這就是稍後調用的flush_events()正在做的事情:允許與圖形視窗進行互動。)
此範例使用 numpy 方法 linspace() 建立 x 值。它傳回一個 numpy 數組,這是一個奇特的 Python 列表。
使用 np.sin 而不是 math.sin 的原因是廣播。這是將函數應用於列表中每個項目的 numpy 術語。事實上,我發現使用 map:
無需 numpy 也可以實現相同的事情
map(lambda n: math.sin(n), x)
但是numpy廣播使用起來方便簡單。
現在是 pyplot 設定。首先,建立一個新的「圖形」(圖)。在此圖中,新增一個子圖(ax)-可能有很多。 111 有相當深奧的解釋,「建立一個 1x1 網格,並將這個子圖放在第一個單元格中。」
在此子圖(或軸集)中,使用傳遞的 x 和 y 值繪製一條線。 (點以直線連接並連續繪製。)「r-」是指定紅色實線的簡寫方式。我們可以指定多行,因此plot()傳回一個元組;上面的程式碼使用元組解包來提取我們想要的一個值。
這是一個好的開始,但我需要隨著時間的推移延長 x 軸。如有必要,此程式碼也不會更新 y 軸的邊界 - 它被鎖定到為第一個圖計算的任何邊界。更多的搜尋讓我找到了這個答案。引用他們的話:
您將需要更新軸的 dataLim,然後根據 dataLim 更新軸的 viewLim。適當的方法是axes.relim()和ax.autoscale_view()方法。
當然,聽起來不錯。根據他們的範例,我創建了一個在 x 和 y 上都成長的演示圖。
import matplotlib.pyplot as plt import numpy as np from threading import Thread from time import sleep x = list(map(lambda x: x / 10, range(-100, 100))) x_next_max = 100 y = np.sin(x) # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() fig = plt.figure() ax = fig.add_subplot(111) line1 = ax.plot(x, y, 'r-')[0] # Returns a tuple of line objects growth = 0 while True: x.append(x_next_max / 10) x_next_max = 1 line1.set_xdata(x) line1.set_ydata(np.sin(x) np.sin(np.divide(x, 100)) np.divide(x, 100)) ax.relim() ax.autoscale() fig.canvas.draw() fig.canvas.flush_events() sleep(0.1)
現在我已經有所進展了。但是,這是一個阻塞循環,我需要偶爾更新我的資料。如果我有多個線程,我就必須擔心更新變數時的線程安全性問題。在這種情況下,我可以偷懶,因為我知道該變數僅每 5 分鐘更新一次(或無論輪詢函數運行多久);變數在程式碼行中間不存在被覆蓋的危險。
import matplotlib.pyplot as plt import numpy as np from threading import Timer from time import sleep x = list(map(lambda x: x / 10, range(-100, 100))) x_next_max = 100 y = np.sin(x) # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() fig = plt.figure() ax = fig.add_subplot(111) line1 = ax.plot(x, y, 'r-')[0] # Plot returns a tuple of line objects growth = 0 new_x = None dT = 1 def grow(): global new_x, x_next_max while True: new_x = x [x_next_max / 10] x_next_max = 1 sleep(dT) # grow every dT seconds t = Thread(target=grow) t.start() while True: if new_x: x = new_x new_x = None line1.set_xdata(x) line1.set_ydata(np.sin(x) np.sin(np.divide(x, 100)) np.divide(x, 100)) ax.relim() ax.autoscale() fig.canvas.draw() fig.canvas.flush_events() sleep(0.1)
只有當成長執行緒為 new_x 賦值時,此圖才會更新。請注意,flush_events() 呼叫位於「if」語句之外,因此它被頻繁地呼叫。
免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。
Copyright© 2022 湘ICP备2022001581号-3