I am working on a program where I need two different graphs to be animated. I am having trouble puzzling out how to do so with the structure I am using. I will paste my code below so that you can try it. I have stripped it down as much as I can while still retaining core functionality so hopefully it isn't too hard to understand. In it's current state the animation lines are not doing anything so please let me know where I have gone wrong.
from Tkinter import * #Used for GUI elements
import time #Used for timing elements
import matplotlib #Used for graphing
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
import numpy as np #Used for arrays to find min/max of float array
import random #Only used for fake data input
class tkgui:
def __init__(self, parent):
#--------------The following are variables that need to be accessed by other functions----------------------
#Raw input values
self.x = 209500
self.y = 0
self.timeElapsed = 0
#State values
self.curFrame = 1
#List Values
self.timeList = np.array([])
self.yList = np.array([])
self.xList = np.array([])
self.lastX = 0
#----------------------------------------------------------------------------------------------------------
#Make Tkinter fullscreen
w, h = 320,240 #int(str(root.winfo_screenwidth())), int(str(root.winfo_screenheight())) #320, 240 is the RPiTFT
#The base layer of the GUI
topLevelContainer = Frame(parent)
topLevelContainer.pack()
#The two 'screens' to switch between. They contain everything on the GUI
self.buttonsFrame = Frame(topLevelContainer)
self.graphFrame = Frame(topLevelContainer)
#Stack the frames so that they are switchable
for frame in self.buttonsFrame, self.graphFrame:
frame.grid(row=0, column=0, sticky='news', padx=5, pady=(10, 10))
buttonsFrameButtons = Frame(self.buttonsFrame)
buttonsFrameButtons.pack(side=LEFT, padx=(0, 50))
#X button
self.xButton = Button(buttonsFrameButtons, command=self.xButtonClick)
self.xButton.configure(text="X", background="#C8C8C8", width=6, padx=35, pady=35)
self.xButton.pack(side=TOP, pady=10)
#Y button
self.yButton = Button(buttonsFrameButtons, command=self.yButtonClick)
self.yButton.configure(text="Y", background="#C8C8C8", width=6, padx=35, pady=35)
self.yButton.pack(side=TOP, pady=10)
#Bar graph
buttonsFrameBar = Frame(self.buttonsFrame)
buttonsFrameBar.pack(side=LEFT)
self.fBar = Figure(figsize=(2, 4), dpi=50)
aBar = self.fBar.add_subplot(111)
self.xBar = aBar.bar([0, 1], [0, 0], width=1)
lAxes = self.fBar.gca()
lAxes.axes.get_xaxis().set_ticklabels([])
aBar.set_ylim([-30000, 30000])
self.fBar.tight_layout()
self.buttonsFrame.tkraise()
#Setup the matplotlib graph
self.fGraph = Figure(figsize=(5, 3), dpi=50)
#Create the Y axis
aGraph = self.fGraph.add_subplot(111)
aGraph.set_xlabel("Time (s)")
aGraph.set_ylabel("Y")
self.yLine, = aGraph.plot([],[], "r-")
#Create the X axis
a2Graph = aGraph.twinx()
self.xLine, = a2Graph.plot([], [])
a2Graph.set_ylabel("X")
#Final matplotlib/Tkinter setup
self.canvasGraph = FigureCanvasTkAgg(self.fGraph, master=self.graphFrame)
self.canvasGraph.show()
self.canvasGraph.get_tk_widget().pack(side=LEFT, fill=BOTH, expand=1)
self.canvasBar = FigureCanvasTkAgg(self.fBar, master=buttonsFrameBar)
self.canvasBar.show()
self.canvasBar.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=1)
#Resize the plot to fit all of the labels in
self.fGraph.subplots_adjust(bottom=0.13, left=0.15, right=0.87)
def refreshGraph(self, frameno): #Redraw the graph with the updated arrays and resize it accordingly
#Update data
self.yLine.set_data(self.timeList, self.yList)
self.xLine.set_data(self.timeList, self.xList)
#Update y axis
ax = self.canvasGraph.figure.axes[0]
ax.set_xlim(self.timeList.min(), self.timeList.max())
ax.set_ylim(self.yList.min(), self.yList.max())
#Update x axis
ax2 = self.canvasGraph.figure.axes[1]
ax2.set_xlim(self.timeList.min(), self.timeList.max())
ax2.set_ylim(self.xList.min(), self.xList.max())
#Redraw
self.canvasGraph.draw()
def refreshBar(self, frameno):
curX = self.x
dif = curX - self.lastX
i = [dif]
for rect, h in zip(self.xBar, i):
rect.set_height(h)
if dif > 0:
rect.set_color('b')
else:
rect.set_color('r')
self.canvasBar.draw()
self.lastX=curX
def switchFrame(self): #Switch the current screen. Either x/y buttons or graph
if self.curFrame:
self.graphFrame.tkraise()
self.curFrame = 0
else:
self.buttonsFrame.tkraise()
self.curFrame = 1
def xButtonClick(self):
self.switchFrame()
def yButtonClick(self):
self.close()
def close(e): #Exit the program
sys.exit()
#Initialisation of global variables
lastTime = 0 #Used for the 'last time' iterated
yState = 0
def updateNumbers(): #Used to generate fake input variables. Will be replaced by ADC values
global lastTime
global yState
curTime = time.time() #Update the time each time the function is called
if curTime - lastTime > 0.5: #Only update numbers every 0.5 seconds
gui.x = random.randrange(200000, 230000) #Generates x
if yState:
gui.y = gui.y - 20 #Decrease y
if gui.y < 1:
yState = 0 #Until it gets to a minimum of 0
else:
gui.y = gui.y + 20 #Increase y
if gui.y > 1300:
yState = 1 #Until it reaches a maximum of 1300
gui.yList = np.append(gui.yList, gui.y) #Add the new y values to the array
gui.xList = np.append(gui.xList, gui.x/10000.0) #Add the new x values to the array
lastTime = time.time() #Record the last time iterated for timing purposes
gui.timeElapsed += 0.5
gui.timeList = np.append(gui.timeList, gui.timeElapsed) #Add the latest time to the array
## gui.refreshGraph() #Call the function that will redraw the graph with the new figures
if __name__ == "__main__":
root = Tk() #Root Tkinter setup
gui = tkgui(root) #Setup the gui class
updateNumbers()
aniGraph = animation.FuncAnimation(gui.fGraph,gui.refreshGraph,interval=500,frames=100,repeat=True)
aniBar = animation.FuncAnimation(gui.fBar,gui.refreshBar,interval=500,frames=100,repeat=True)
while(1): #Main loop
updateNumbers() #Update fake values
root.update() #Update the gui loop
root.mainloop() #Tkinter main loop
To be clear, I am only asking how to get the animation working for this code.
Your code works for me - I see all animation - but if you what to run it without While(1):
(or more pythonic While True:
) then you can use root.after(milliseconds, function_name)
. You can event use it instead of FuncAnimation
.
And it lets you control function - start/stop it.
if self.run_bar:
root.after(100, self.refreshBar)
You use start it (or restart it)
self.run_bar = True
self.refreshBar()
and you can stop it
self.run_bar = False
See all # <--
in code.
from Tkinter import * #Used for GUI elements
import time #Used for timing elements
import matplotlib #Used for graphing
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
import numpy as np #Used for arrays to find min/max of float array
import random #Only used for fake data input
class TkGUI: # <-- CamelCase names for classes
# <-- empty line for readabelity
def __init__(self, parent):
#--- The following are variables that need to be accessed by other functions----------------------
#Raw input values
self.x = 209500
self.y = 0
self.timeElapsed = 0
#State values
self.curFrame = 1
#List Values
self.timeList = np.array([])
self.yList = np.array([])
self.xList = np.array([])
self.lastX = 0
#-----------------------------------------------------------
#Make Tkinter fullscreen
w, h = 320,240 #int(str(root.winfo_screenwidth())), int(str(root.winfo_screenheight())) #320, 240 is the RPiTFT
#The base layer of the GUI
topLevelContainer = Frame(parent)
topLevelContainer.pack()
#The two 'screens' to switch between. They contain everything on the GUI
self.buttonsFrame = Frame(topLevelContainer)
self.graphFrame = Frame(topLevelContainer)
#Stack the frames so that they are switchable
for frame in self.buttonsFrame, self.graphFrame:
frame.grid(row=0, column=0, sticky='news', padx=5, pady=(10, 10))
buttonsFrameButtons = Frame(self.buttonsFrame)
buttonsFrameButtons.pack(side=LEFT, padx=(0, 50))
#X button
self.xButton = Button(buttonsFrameButtons, command=self.xButtonClick)
self.xButton.configure(text="X", background="#C8C8C8", width=6, padx=35, pady=35)
self.xButton.pack(side=TOP, pady=10)
#Y button
self.yButton = Button(buttonsFrameButtons, command=self.yButtonClick)
self.yButton.configure(text="Y", background="#C8C8C8", width=6, padx=35, pady=35)
self.yButton.pack(side=TOP, pady=10)
#Bar graph
buttonsFrameBar = Frame(self.buttonsFrame)
buttonsFrameBar.pack(side=LEFT)
self.fBar = Figure(figsize=(2, 4), dpi=50)
aBar = self.fBar.add_subplot(111)
self.xBar = aBar.bar([0, 1], [0, 0], width=1)
lAxes = self.fBar.gca()
lAxes.axes.get_xaxis().set_ticklabels([])
aBar.set_ylim([-30000, 30000])
self.fBar.tight_layout()
self.buttonsFrame.tkraise()
#Setup the matplotlib graph
self.fGraph = Figure(figsize=(5, 3), dpi=50)
#Create the Y axis
aGraph = self.fGraph.add_subplot(111)
aGraph.set_xlabel("Time (s)")
aGraph.set_ylabel("Y")
self.yLine, = aGraph.plot([],[], "r-")
#Create the X axis
a2Graph = aGraph.twinx()
self.xLine, = a2Graph.plot([], [])
a2Graph.set_ylabel("X")
#Final matplotlib/Tkinter setup
self.canvasGraph = FigureCanvasTkAgg(self.fGraph, master=self.graphFrame)
self.canvasGraph.show()
self.canvasGraph.get_tk_widget().pack(side=LEFT, fill=BOTH, expand=1)
self.canvasBar = FigureCanvasTkAgg(self.fBar, master=buttonsFrameBar)
self.canvasBar.show()
self.canvasBar.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=1)
#Resize the plot to fit all of the labels in
self.fGraph.subplots_adjust(bottom=0.13, left=0.15, right=0.87)
def refreshGraph(self): # <-- without argument
'''Redraw the graph with the updated arrays and resize it accordingly''' # <-- docstring used by documentation generator and IDE as help
#Update data
self.yLine.set_data(self.timeList, self.yList)
self.xLine.set_data(self.timeList, self.xList)
#Update y axis
ax = self.canvasGraph.figure.axes[0]
ax.set_xlim(self.timeList.min(), self.timeList.max())
ax.set_ylim(self.yList.min(), self.yList.max())
#Update x axis
ax2 = self.canvasGraph.figure.axes[1]
ax2.set_xlim(self.timeList.min(), self.timeList.max())
ax2.set_ylim(self.xList.min(), self.xList.max())
#Redraw
self.canvasGraph.draw()
# run again after 100ms (0.1s)
root.after(100, self.refreshGraph) # <-- run again like in loop
def refreshBar(self): # <-- without argument
curX = self.x
dif = curX - self.lastX
i = [dif]
for rect, h in zip(self.xBar, i):
rect.set_height(h)
if dif > 0:
rect.set_color('b')
else:
rect.set_color('r')
self.canvasBar.draw()
self.lastX=curX
# run again after 100ms (0.1s)
root.after(100, self.refreshBar) # <-- run again like in loop
def switchFrame(self): #Switch the current screen. Either x/y buttons or graph
if self.curFrame:
self.graphFrame.tkraise()
self.curFrame = 0
else:
self.buttonsFrame.tkraise()
self.curFrame = 1
def xButtonClick(self):
self.switchFrame()
def yButtonClick(self):
self.close()
def close(e): # Exit the program
sys.exit()
#Initialisation of global variables
lastTime = 0 #Used for the 'last time' iterated
yState = 0
def updateNumbers(): #Used to generate fake input variables. Will be replaced by ADC values
global lastTime
global yState
curTime = time.time() #Update the time each time the function is called
if curTime - lastTime > 0.5: #Only update numbers every 0.5 seconds
gui.x = random.randrange(200000, 230000) #Generates x
if yState:
gui.y = gui.y - 20 #Decrease y
if gui.y < 1:
yState = 0 #Until it gets to a minimum of 0
else:
gui.y = gui.y + 20 #Increase y
if gui.y > 1300:
yState = 1 #Until it reaches a maximum of 1300
gui.yList = np.append(gui.yList, gui.y) #Add the new y values to the array
gui.xList = np.append(gui.xList, gui.x/10000.0) #Add the new x values to the array
lastTime = time.time() #Record the last time iterated for timing purposes
gui.timeElapsed += 0.5
gui.timeList = np.append(gui.timeList, gui.timeElapsed) #Add the latest time to the array
# run again after 100ms (0.1s)
root.after(100, updateNumbers) # <-- run again like in loop
if __name__ == "__main__":
root = Tk()
gui = TkGUI(root)
# <-- vvv - without While and without FuncAnimation - vvv
updateNumbers() # run first time
gui.refreshBar() # run first time
gui.refreshGraph() # run first time
# <-- ^^^ - without While and without FuncAnimation - ^^^
root.mainloop() # Tkinter main loop
EDIT: Of course you can keep FuncAnimation
without after
and use after
only in updateNumbers
from Tkinter import * #Used for GUI elements
import time #Used for timing elements
import matplotlib #Used for graphing
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
import numpy as np #Used for arrays to find min/max of float array
import random #Only used for fake data input
class TkGUI: # <-- CamelCase names for classes
# <-- empty line for readabelity
def __init__(self, parent):
#--- The following are variables that need to be accessed by other functions----------------------
#Raw input values
self.x = 209500
self.y = 0
self.timeElapsed = 0
#State values
self.curFrame = 1
#List Values
self.timeList = np.array([])
self.yList = np.array([])
self.xList = np.array([])
self.lastX = 0
#-----------------------------------------------------------
#Make Tkinter fullscreen
w, h = 320,240 #int(str(root.winfo_screenwidth())), int(str(root.winfo_screenheight())) #320, 240 is the RPiTFT
#The base layer of the GUI
topLevelContainer = Frame(parent)
topLevelContainer.pack()
#The two 'screens' to switch between. They contain everything on the GUI
self.buttonsFrame = Frame(topLevelContainer)
self.graphFrame = Frame(topLevelContainer)
#Stack the frames so that they are switchable
for frame in self.buttonsFrame, self.graphFrame:
frame.grid(row=0, column=0, sticky='news', padx=5, pady=(10, 10))
buttonsFrameButtons = Frame(self.buttonsFrame)
buttonsFrameButtons.pack(side=LEFT, padx=(0, 50))
#X button
self.xButton = Button(buttonsFrameButtons, command=self.xButtonClick)
self.xButton.configure(text="X", background="#C8C8C8", width=6, padx=35, pady=35)
self.xButton.pack(side=TOP, pady=10)
#Y button
self.yButton = Button(buttonsFrameButtons, command=self.yButtonClick)
self.yButton.configure(text="Y", background="#C8C8C8", width=6, padx=35, pady=35)
self.yButton.pack(side=TOP, pady=10)
#Bar graph
buttonsFrameBar = Frame(self.buttonsFrame)
buttonsFrameBar.pack(side=LEFT)
self.fBar = Figure(figsize=(2, 4), dpi=50)
aBar = self.fBar.add_subplot(111)
self.xBar = aBar.bar([0, 1], [0, 0], width=1)
lAxes = self.fBar.gca()
lAxes.axes.get_xaxis().set_ticklabels([])
aBar.set_ylim([-30000, 30000])
self.fBar.tight_layout()
self.buttonsFrame.tkraise()
#Setup the matplotlib graph
self.fGraph = Figure(figsize=(5, 3), dpi=50)
#Create the Y axis
aGraph = self.fGraph.add_subplot(111)
aGraph.set_xlabel("Time (s)")
aGraph.set_ylabel("Y")
self.yLine, = aGraph.plot([],[], "r-")
#Create the X axis
a2Graph = aGraph.twinx()
self.xLine, = a2Graph.plot([], [])
a2Graph.set_ylabel("X")
#Final matplotlib/Tkinter setup
self.canvasGraph = FigureCanvasTkAgg(self.fGraph, master=self.graphFrame)
self.canvasGraph.show()
self.canvasGraph.get_tk_widget().pack(side=LEFT, fill=BOTH, expand=1)
self.canvasBar = FigureCanvasTkAgg(self.fBar, master=buttonsFrameBar)
self.canvasBar.show()
self.canvasBar.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=1)
#Resize the plot to fit all of the labels in
self.fGraph.subplots_adjust(bottom=0.13, left=0.15, right=0.87)
def refreshGraph(self, i):
'''Redraw the graph with the updated arrays and resize it accordingly''' # <-- docstring used by documentation generator and IDE as help
#Update data
self.yLine.set_data(self.timeList, self.yList)
self.xLine.set_data(self.timeList, self.xList)
#Update y axis
ax = self.canvasGraph.figure.axes[0]
ax.set_xlim(self.timeList.min(), self.timeList.max())
ax.set_ylim(self.yList.min(), self.yList.max())
#Update x axis
ax2 = self.canvasGraph.figure.axes[1]
ax2.set_xlim(self.timeList.min(), self.timeList.max())
ax2.set_ylim(self.xList.min(), self.xList.max())
#Redraw
self.canvasGraph.draw()
def refreshBar(self, i):
curX = self.x
dif = curX - self.lastX
i = [dif]
for rect, h in zip(self.xBar, i):
rect.set_height(h)
if dif > 0:
rect.set_color('b')
else:
rect.set_color('r')
self.canvasBar.draw()
self.lastX=curX
def switchFrame(self):
'''Switch the current screen. Either x/y buttons or graph'''
if self.curFrame:
self.graphFrame.tkraise()
self.curFrame = 0
else:
self.buttonsFrame.tkraise()
self.curFrame = 1
def xButtonClick(self):
self.switchFrame()
def yButtonClick(self):
self.close()
def close(e): # Exit the program
sys.exit()
#Initialisation of global variables
lastTime = 0 #Used for the 'last time' iterated
yState = 0
def updateNumbers(): #Used to generate fake input variables. Will be replaced by ADC values
global lastTime
global yState
curTime = time.time() #Update the time each time the function is called
if curTime - lastTime > 0.5: #Only update numbers every 0.5 seconds
gui.x = random.randrange(200000, 230000) #Generates x
if yState:
gui.y = gui.y - 20 #Decrease y
if gui.y < 1:
yState = 0 #Until it gets to a minimum of 0
else:
gui.y = gui.y + 20 #Increase y
if gui.y > 1300:
yState = 1 #Until it reaches a maximum of 1300
gui.yList = np.append(gui.yList, gui.y) #Add the new y values to the array
gui.xList = np.append(gui.xList, gui.x/10000.0) #Add the new x values to the array
lastTime = time.time() #Record the last time iterated for timing purposes
gui.timeElapsed += 0.5
gui.timeList = np.append(gui.timeList, gui.timeElapsed) #Add the latest time to the array
# run again after 100ms (0.1s)
root.after(100, updateNumbers) # <-- run again like in loop
if __name__ == "__main__":
root = Tk()
gui = TkGUI(root)
aniGraph = animation.FuncAnimation(gui.fGraph,gui.refreshGraph,interval=500,frames=100,repeat=True)
aniBar = animation.FuncAnimation(gui.fBar,gui.refreshBar,interval=500,frames=100,repeat=True)
# <-- vvv - without While - vvv
updateNumbers() # run first time
# <-- ^^^ - without While - ^^^
root.mainloop() # Tkinter main loop
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.