Reputation: 85
I'm making an application that should visualize sensor data. Therefore I'm using python with tkinter as gui framework and matplotlib to visualize the data. The graph should show the current sensor value, so it should be animated.
However, since I don't want to animate the graph when the sensor is not connected, I added a connect
button, which needs to start the animation.
This all works as expected, however, the animation only starts after I resize the tkinter window.
I assume this triggers a redraw of all components, but I don't know how to trigger this from code.
I already tried calling the functions root.update()
and root.update_idletasks()
but that didn't help.
Here is a minimal example of the code:
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.animation as animation
import matplotlib.pyplot as plt
from matplotlib import style
root = tk.Tk()
style.use('fivethirtyeight')
ys = [i for i in range(100)]
def animate(i, ax1, line):
global ys
ys = ys[1:] + [ys[0]]
line.set_ydata(ys)
# Draw x and y lists
ax1.relim()
ax1.autoscale_view(True,True,True)
return line
class Gui():
def __init__(self):
global ys
self._fig = plt.figure()
self._ax1 = self._fig.add_subplot(1,1,1)
self._line, = self._ax1.plot(ys)
plt.xticks(ha='right')
plt.subplots_adjust(bottom=0.30)
plt.title('Air pressure measured')
plt.ylabel('Sensor value')
top_frame = tk.Frame(root, bg='cyan', width = 450, height=50)
top_frame.pack(side=tk.LEFT, anchor=tk.N, pady=10)
self.connectButton = tk.Button(top_frame,
text="connect",
command=self.toggleAnimate)
self.connectButton.pack()
self._canvas = FigureCanvasTkAgg(self._fig, master=root)
self._canvas.draw()
self._canvas.get_tk_widget().pack()
def start(self):
root.mainloop()
def toggleAnimate(self):
self._animate = animation.FuncAnimation(self._fig, animate, fargs=(self._ax1, self._line), interval=100, blit=False)
root.update_idletasks()
root.update()
if __name__ == "__main__":
g = Gui()
g.start()
Upvotes: 2
Views: 454
Reputation: 36662
You need to call draw_idle
once on the FigureCanvasTkAgg
to get things rolling.
I placed root
inside the GUI, and removed the calls to update
and update_idletasks
that were not necessary, and might have interfered with the mainloop
.
I also packed your canvas
inside the frame
, but you can relocate it to root
if you prefer.
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.animation as animation
import matplotlib.pyplot as plt
from matplotlib import style
style.use('fivethirtyeight')
ys = [i for i in range(100)]
def animate(i, ax1, line):
global ys
ys = ys[1:] + [ys[0]]
line.set_ydata(ys)
# Draw x and y lists
ax1.relim()
ax1.autoscale_view(True,True,True)
return line
class Gui():
def __init__(self):
self.root = tk.Tk()
self._fig = plt.figure()
self._ax1 = self._fig.add_subplot(1,1,1)
self._line, = self._ax1.plot(ys)
plt.xticks(ha='right')
plt.subplots_adjust(bottom=0.30)
plt.title('Air pressure measured')
plt.ylabel('Sensor value')
top_frame = tk.Frame(self.root, bg='cyan', width = 450, height=50)
top_frame.pack(side=tk.LEFT, anchor=tk.N, pady=10)
self.connectButton = tk.Button(top_frame,
text="connect",
command=self.toggleAnimate)
self.connectButton.pack()
self._canvas = FigureCanvasTkAgg(self._fig, master=top_frame) #master=self.root)
self._canvas.get_tk_widget().pack(expand=True, side=tk.LEFT, anchor=tk.N, pady=10, padx=10)
self._canvas.draw()
self._animate = None
def start(self):
self.root.mainloop()
def toggleAnimate(self):
if self._animate is None:
self._canvas.draw_idle()
self._animate = animation.FuncAnimation(self._fig, animate, fargs=(self._ax1, self._line), interval=100) #, blit=False)
if __name__ == "__main__":
g = Gui()
g.start()
Upvotes: 2