问题
I have a GUI that I am using to send a query data from an arduino. I then went to live graph this data without slow down. I currently am getting slow down with the more numbers that I am plotting and i had a similar problem when I first wrote this GUI in matlab. I wanted to know if i could make this faster as I eventually want to be able to graph to multiple windows at once. I am aware of the animation library from a previous suggestion but was unsuccessful with getting it to work insife of my tkinter object from what I understand that would be the ideal way to speed up the graphing besides for drawing straight to the canvas. I only need to plot about 200 points of data at a time. Here is the current GUI code:
import Tkinter
import numpy as np
import serial
import time
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import matplotlib.animation as animation
from math import cos, sin
from collections import deque
class App:
def __init__(self, master):
frame = Tkinter.Frame(master)
self.Max_press = Tkinter.StringVar()
self.Max_press.set("10")
self.Min_press = Tkinter.StringVar()
self.Min_press.set("0")
self.Cycle_per_minute = Tkinter.StringVar()
self.Cycle_per_minute.set("12")
self.Duration_cycle = Tkinter.StringVar()
self.Duration_cycle.set("1")
self.respiration = Tkinter.LabelFrame(frame, text="Respiration
Testing", borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10)
self.respiration.grid(row=0, column=0, padx=20, pady=20)
self.max_pressure = Tkinter.Label(self.respiration, text="Maximum
Pressure (mmHg)")
self.max_pressure.grid(row=0, column=0, padx=5, pady=5)
self.Max_pressure =
Tkinter.Entry(self.respiration,textvariable=self.Max_press)
self.Max_pressure.grid(row=1, column=0, padx=5, pady=5)
self.min_pressure = Tkinter.Label(self.respiration, text="Minimum
Pressure (mmHg)")
self.min_pressure.grid(row=2, column=0, padx=5, pady=5)
self.Min_pressure = Tkinter.Entry(self.respiration,
textvariable=self.Min_press)
self.Min_pressure.grid(row=3, column=0, padx=5, pady=5)
self.cycles_per_minute = Tkinter.Label(self.respiration,
text="Cycles Per Minute")
self.cycles_per_minute.grid(row=4, column=0, padx=5, pady=5)
self.Cycles_per_minute =
Tkinter.Entry(self.respiration,textvariable=self.Cycle_per_minute)
self.Cycles_per_minute.grid(row=5, column=0, padx=5, pady=5)
self.duration_of_test = Tkinter.Label(self.respiration,
text="Duration (minutes)")
self.duration_of_test.grid(row=6, column=0, padx=5, pady=5)
self.Duration_of_test = Tkinter.Entry(self.respiration,
textvariable=self.Duration_cycle)
self.Duration_of_test.grid(row=7, column=0, padx=5, pady=5)
self.run_respiration = Tkinter.Button(self.respiration, text="RUN
RESPIRATION", command=self.getData)
self.run_respiration.grid(row=8, column=0, padx=5, pady=5)
self.burst = Tkinter.LabelFrame(frame, text="Burst Test",
borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10 )
self.burst.grid(row=0, column=1, padx=20, pady=20)
self.burst_pressure = Tkinter.Button(self.burst, text="RUN BURST
TEST")
self.burst_pressure.grid(row=0, column=0, padx=5, pady=5)
self.test_options = Tkinter.LabelFrame(frame, text="Test Options",
borderwidth=10, relief=Tkinter.GROOVE, padx=10, pady=10 )
self.test_options.grid(row=0, column=2, padx=20, pady=35)
self.stop = Tkinter.Button(self.test_options, text="STOP", bd=10,
height=5, width=10)
self.stop.grid(row=0, column=0, padx=10, pady=25)
self.pause = Tkinter.Button(self.test_options, text="PAUSE", bd=10,
height=5, width=10)
self.pause.grid(row=1, column=0, padx=10, pady=25)
self.reset = Tkinter.Button(self.test_options, text="RESET", bd=10,
height=5, width=10)
self.reset.grid(row=2, column=0, padx=10, pady=25)
self.save = Tkinter.Button(self.test_options, text="SAVE", bd=10,
height=5, width=10)
self.save.grid(row=3, column=0, padx=10, pady=25)
self.xdata = deque([0]*200,maxlen=200)
self.ydata = deque([0]*200,maxlen=200)
self.fig = plt.Figure()
self.ax1 = self.fig.add_subplot(111)
self.line, = self.ax1.plot(self.xdata, self.ydata, lw=2)
self.canvas = FigureCanvasTkAgg(self.fig,master=master)
self.canvas.show()
self.canvas.get_tk_widget().grid(row=0, column=3, padx=20, pady=20)
frame.grid(row=0, column=0, padx=20, pady=20)
def getData(self):
press_max = float(self.Max_press.get())
press_min = float(self.Min_press.get())
duration = float(self.Duration_cycle.get())*60*20
cycle_time = float(self.Cycles_per_minute.get())
i = 0
x = []
y = []
amp = (press_max - press_min)/2
offset = amp + press_min
spb = 60/cycle_time
while (i < duration):
x.append(i)
sine = amp*np.sin((x[i]*(np.pi*4))/(2*spb)) + offset + 1
y.append(sine)
i = i + 1
arduinoData = serial.Serial('com5', 115200)
arduinoData.flushInput()
press = []
t = []
i = 0
start = time.time()
while (i < duration + 1):
while (arduinoData.inWaiting()==0):
pass
arduinoString = arduinoData.readline()
dataArray = int(arduinoString)
i = i + 1
print (dataArray)
self.plotData(dataArray,i)
end = time.time()
print (end - start)
def plotData(self, y, x ):
self.xdata.append(x)
self.ydata.append(y)
self.ax1.plot(self.xdata, self.ydata)
self.canvas.show()
root = Tkinter.Tk()
app = App(root)
root.mainloop()
But the most important part is here:
self.fig = plt.Figure()
self.ax1 = self.fig.add_subplot(111)
self.line, = self.ax1.plot(self.xdata, self.ydata, lw=2)
self.canvas = FigureCanvasTkAgg(self.fig,master=master)
self.canvas.show()
self.canvas.get_tk_widget().grid(row=0, column=3, padx=20, pady=20)
frame.grid(row=0, column=0, padx=20, pady=20)
def getData(self):
press_max = float(self.Max_press.get())
press_min = float(self.Min_press.get())
duration = float(self.Duration_cycle.get())*60*20
cycle_time = float(self.Cycles_per_minute.get())
i = 0
x = []
y = []
amp = (press_max - press_min)/2
offset = amp + press_min
spb = 60/cycle_time
while (i < duration):
x.append(i)
sine = amp*np.sin((x[i]*(np.pi*4))/(2*spb)) + offset + 1
y.append(sine)
i = i + 1
arduinoData = serial.Serial('com5', 115200)
arduinoData.flushInput()
press = []
t = []
i = 0
start = time.time()
while (i < duration + 1):
while (arduinoData.inWaiting()==0):
pass
arduinoString = arduinoData.readline()
dataArray = int(arduinoString)
i = i + 1
print (dataArray)
self.plotData(dataArray,i)
end = time.time()
print (end - start)
def plotData(self, y, x ):
self.xdata.append(x)
self.ydata.append(y)
self.ax1.plot(self.xdata, self.ydata)
self.canvas.show()
root = Tkinter.Tk()
app = App(root)
root.mainloop()
回答1:
You need to use the matplotlib animation capabilities for the fastest response. Here's an example inside a tkinter window. I obviously can't test it since I don't have your arduino, so I commented out the arduino parts and added some random parts.
import Tkinter as tk
import serial
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
import matplotlib.animation as animation
from collections import deque
import random
HISTORY_LEN = 200
class App(tk.Frame):
def __init__(self, master=None, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
self.running = False
self.ani = None
btns = tk.Frame(self)
btns.pack()
lbl = tk.Label(btns, text="Number of points")
lbl.pack(side=tk.LEFT)
self.points_ent = tk.Entry(btns, width=5)
self.points_ent.insert(0, '500')
self.points_ent.pack(side=tk.LEFT)
lbl = tk.Label(btns, text="update interval (ms)")
lbl.pack(side=tk.LEFT)
self.interval = tk.Entry(btns, width=5)
self.interval.insert(0, '30')
self.interval.pack(side=tk.LEFT)
self.btn = tk.Button(btns, text='Start', command=self.on_click)
self.btn.pack(side=tk.LEFT)
self.fig = plt.Figure()
self.ax1 = self.fig.add_subplot(111)
self.line, = self.ax1.plot([], [], lw=2)
self.canvas = FigureCanvasTkAgg(self.fig,master=master)
self.canvas.show()
self.canvas.get_tk_widget().pack()
def on_click(self):
if self.ani is None:
return self.start()
if self.running:
self.ani.event_source.stop()
self.btn.config(text='Un-Pause')
else:
self.ani.event_source.start()
self.btn.config(text='Pause')
self.running = not self.running
def start(self):
self.xdata = deque([], maxlen=HISTORY_LEN)
self.ydata = deque([], maxlen=HISTORY_LEN)
#~ self.arduinoData = serial.Serial('com5', 115200)
#~ self.arduinoData.flushInput()
self.points = int(self.points_ent.get()) + 1
self.ani = animation.FuncAnimation(
self.fig,
self.update_graph,
frames=self.points,
interval=int(self.interval.get()),
repeat=False)
self.running = True
self.btn.config(text='Pause')
self.ani._start()
def update_graph(self, i):
self.xdata.append(i)
#~ self.ydata.append(int(self.arduinoData.readline()))
self.ydata.append(random.randrange(100)) # DEBUG
self.line.set_data(self.xdata, self.ydata)
self.ax1.set_ylim(min(self.ydata), max(self.ydata))
self.ax1.set_xlim(min(self.xdata), max(self.xdata))
if i >= self.points - 1:
#~ self.arduinoData.close()
self.btn.config(text='Start')
self.running = False
self.ani = None
return self.line,
def main():
root = tk.Tk()
app = App(root)
app.pack()
root.mainloop()
if __name__ == '__main__':
main()
For more speed, cut out the print functions. Printing to the terminal is very slow. Also, you may want to move the serial initialization to someplace where it's only called once.
For my computer, the fastest I could go was 25 ms / frame.
来源:https://stackoverflow.com/questions/43477681/how-to-speed-up-tkinter-embedded-matplot-lib-and-python