【问题标题】:How to speed up tkinter embedded matplot lib and python如何加速 tkinter 嵌入式 matplotlib 和 python
【发布时间】:2017-04-18 16:36:10
【问题描述】:

我有一个用于从 arduino 发送查询数据的 GUI。然后我去现场绘制这些数据而没有放慢速度。我目前正在绘制的数字越多,我的速度就越慢,当我第一次在 matlab 中编写这个 GUI 时,我遇到了类似的问题。我想知道我是否可以让这更快,因为我最终希望能够一次绘制到多个窗口。我从之前的建议中了解到动画库,但没有成功地让它在我的 tkinter 对象中工作,据我了解,这将是加速绘图的理想方法,除了直接绘制到画布上。我一次只需要绘制大约 200 个数据点。这是当前的 GUI 代码:

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()

但最重要的部分在这里:

        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()

【问题讨论】:

    标签: python user-interface matplotlib tkinter tkinter-canvas


    【解决方案1】:

    您需要使用 matplotlib 动画功能以获得最快的响应。这是 tkinter 窗口中的示例。我显然无法测试它,因为我没有你的 arduino,所以我注释掉了 arduino 部分并添加了一些随机部分。

    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()
    

    为了提高速度,请取消打印功能。打印到终端非常慢。此外,您可能希望将串行初始化移动到只调用一次的地方。

    对于我的计算机,我能达到的最快速度是 25 毫秒/帧。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-03
      • 1970-01-01
      • 1970-01-01
      • 2018-04-28
      • 2013-09-14
      • 2019-04-08
      • 1970-01-01
      • 2016-03-19
      相关资源
      最近更新 更多