我正在制作一個應用程式來讀取來自 arduino 板上傳感器的串行資料。嘗試使用 matplotlib.animation 類制作所述資料的實時圖表時會出現問題。進行繪圖時,GUI 小部件變得無回應。據我了解,使串行讀取程序在自己的執行緒上運行可能會解決該問題。我無法理解如何制作它以使其與 FuncAnimation 子類兼容。
def read_serial_data(port, bauds=9600):
s = serial.Serial(port, bauds)
line = s.readline()[0:-2]
return line
def getPorts():
return [port.device for port in serial.tools.list_ports.comports(include_links=False)]
class GUI():
def __init__(self):
self.root = Tk.Tk()
self._fig = plt.figure()
self.root.title('Measurement Dashboard')
self.root.state('normal')
self.root.config(background='#ffffff')
self._canvas = FigureCanvasTkAgg(self._fig, self.root)
self._canvas.get_tk_widget().grid(column = 1, row = 1)
self._canvas.draw()
self._animate = None
self._ax = self._fig.add_subplot(111)
self._ax.yaxis.grid(True, color = 'black', linestyle='--')
self._ax.xaxis.grid(True, color = 'black', linestyle='--')
self._ax.set_xlabel('time')
self._ax.set_ylabel('CO2')
self.filename = Tk.StringVar()
self.entry = ttk.Entry(self.root, textvariable = self.filename)
self.entry.grid(column = 2, row = 2)
self.info_var = Tk.StringVar()
self.info_entry = ttk.Entry(self.root, textvariable = self.info_var)
self.info_entry.grid(column = 2, row = 3)
self.port = Tk.StringVar()
self.ports = getPorts()
self._cb = ttk.Combobox(self.root, textvariable= self.port, values = self.ports)
self._cb.grid(column = 2, row = 1)
self.start_button = Tk.Button(self.root, text = 'Start', command = self.plot)
self.start_button.grid(column = 1, row = 2)
self.save_button = Tk.Button(self.root, text = 'Save info', command = self.save_info)
self.save_button.grid(column = 2, row = 4)
def save_info(self):
global info
info = self.info_var.get()
def start(self):
self.root.mainloop()
def plot(self):
if self._animate is None:
self.scope = Scope(self._ax, self.filename.get())
self._canvas.draw_idle()
self._animate = animation.FuncAnimation(self._fig, self.scope.animate, frames = self.update, interval=2000, blit=False)
def update(self):
line = read_serial_data(self.port.get())
data = line.decode('utf-8')
yield data
time = datetime.now()
duration = time - start_time
measurement = {'time': time, 'dur': duration.seconds, 'CO2': data, 'info': info}
write_csv_line(self.filename.get(), measurement)
self.root.after(10000, self.update)
if __name__ == "__main__":
gui = GUI()
gui.start()
thread = Thread(target=read_serial_data,args=(gui.port,))
thread.start()
uj5u.com熱心網友回復:
您實際上并不需要另一個執行緒,但可以使用串行埠上的非阻塞 IO 來執行此操作,并使用 Tkinterafter呼叫來管理輪詢間隔。pyserialinWaiting用于測驗設備是否有輸入等待讀取。如果有位元組在等待,只讀取那些。
這是一個示例讀取器類,用于從串行設備讀取行,并在讀取完整行后將它們發布到應用程式處理程式方法。
class Reader(Serial):
def __init__(self, *args, **kwargs):
self.buffer = ''
super(Reader, self).__init__(*args, **kwargs)
def monitor(self, w, interval=10):
n = self.inWaiting()
if n != 0:
data = self.read(n).decode('ascii')
self.buffer = self.buffer data
ndx = self.buffer.find("\n")
if ndx != -1:
line = self.buffer[:ndx 1]
self.buffer = self.buffer[ndx 1:]
w.after_idle(lambda: w.parse_input(line))
w.after(interval, lambda: self.monitor(w, interval))
像這樣使用:
app = <tkinter application class instance>
reader = Reader(port, baudrate, timeout=0)
reader.flushInput()
reader.monitor(app, 10)
app.mainloop()
在這種情況下,每當讀取一行(由換行符分隔)時,它將呼叫實體parse_input上的方法。app
如果您決定使用執行緒,那么您需要一個佇列來將資料傳遞給 Tkinter UI 執行緒,并且必須確保您不會從作業執行緒呼叫 Tkinter 方法。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/448499.html
