I am working with matplotlib and tkinter in Raspberry pi 4B. I have three different sensor readings shown in real-time. My goal is to pick a sensor from the menubar, which would open the corresponding page. In the page, I would just have a blank graph. From the start/stop menu, I will start reading, which would start displaying the readings right away. I will also stop the animation from the menu.
Issue:
- I noticed that the animations are running in background. I am not sure how to stop that.
- I don't know how to program the start and stop option in the menubar to control the current graph.
- I am not sure how to modify my function
animate_graphthat reads the frame that called it. This seems more of a simple issue, but I don't know what parameter I should pass to this function.
Here is my code:
import tkinter as tk
from tkinter import *
from tkinter import ttk
import matplotlib
matplotlib.use("TkAgg") #backend of matplotlib??
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure #figure we call to format the gaph
import matplotlib.pyplot as plt #to plot the graph
import matplotlib.animation as animation #to create the real-time graph
from matplotlib import style #to change the way the graph will look
style.use("dark_background")
import numpy as np
import datetime as dt
#-----------------------------END OF IMPORT -----------------------------------------------#
LARGE_FONT = ("Times New Roman", 12)
NORM_FONT = ("Times New Roman", 9)
#----------------------------------FUNCTION------------------------------------------------#
def popupmsg(title, msg):
popup = tk.Tk()
popup.wm_title(title)
label = ttk.Label(popup,text = msg, font = NORM_FONT)
label.pack(side = "top",fill = "x",pady = 10)
b1 = ttk.Button(popup,label = "Okay", command=popup.destroy)
b1.pack()
popup.mainloop()
fig_s1,ax1_s1 = plt.subplots()
ax2_s1 = ax1_s1.twinx()
x_s1 = []
y1_s1 = []
y2_s1 = []
def animate_graph(i,x,y1,y2,ax1,ax2): #add frame here
reading1,reading2 = (5-3) * np.random.random() + 3,(5-3) * np.random.random() + 3
#append xs and ys with time and temp respectively
x.append(dt.datetime.now().strftime('%H:%M:%S'))
y1.append(reading1)
y2.append(reading2)
#limit xs and ys to the last 10 seconds
x = x[-10:]
y1 = y1[-10:]
y2 = y2[-10:]
#Draw x and y lists
#y-axis for temperature
color = 'tab:red'
ax1.clear()
ln1= ax1.plot(x,y1,color = color, linestyle = 'dashed', label = 'Read1')
ax1.tick_params(axis = 'y', labelcolor = color)
#y-axis for relative humidity
color = 'tab:blue'
ax2.clear()
ln2 = ax2.plot(x,y2,color = color,label = 'Read2')
ax2.tick_params(axis = 'y', labelcolor = color)
#get different axes labels together
lns = ln1+ln2
labs = [l.get_label() for l in lns]
ax1.legend(lns,labs,bbox_to_anchor = (0,1.02,1,0.102),loc = 3,ncol = 3,borderaxespad = 0)
#x-axis label rotated to 45 degrees to avoid overlapping
ax1.tick_params(axis = 'x', labelrotation = 45)
# #show the graph
# canvas = FigureCanvasTkAgg(fig_s1,frame)
# canvas.draw()
# canvas.get_tk_widget().pack(side = tk.TOP, fill = tk.BOTH, expand = True)
# #add navigation bar
# toolbar = NavigationToolbar2Tk(canvas,frame)
# toolbar.update()
# canvas._tkcanvas.pack(side = tk.TOP,fill = tk.BOTH, expand = True)
#------------------------------------PAGES-----------------------------------#
class main(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self,*args,**kwargs)
tk.Tk.wm_title(self, "GUI")
container = tk.Frame(self)
container.pack(side = "top" , fill = "both" , expand = True)
container.grid_rowconfigure(0, weight = 1)
container.grid_columnconfigure(0,weight = 1)
menubar = tk.Menu(container)
filemenu = tk.Menu(menubar,tearoff = 0)
filemenu.add_command(label = "Made By", command = lambda: popupmsg("Made By","Not supported yet"))
filemenu.add_separator()
filemenu.add_command(label = "Exit", command = quit)
#add filemenu TO THE menubar
menubar.add_cascade(label = "File", menu= filemenu)
#sensor menu
snsrmenu = tk.Menu(menubar,tearoff = 0)
snsrmenu.add_command(label = "Sensor 1", command = lambda: self.show_frame(GraphS1))
snsrmenu.add_command(label = "Sensor 2", command = lambda: self.show_frame(GraphS2))
snsrmenu.add_command(label = "Sensor 3", command = lambda: self.show_frame(GraphS3))
snsrmenu.add_separator()
snsrmenu.add_command(label = "Exit", command = quit)
#add filemenu TO THE menubar
menubar.add_cascade(label = "Sensors", menu= snsrmenu)
#start stop menubar for the graphs
startstopmenu = tk.Menu(menubar,tearoff=0)
startstopmenu.add_command(label = "Start reading", command = lambda: popupmsg("!","Not supported yet"))
startstopmenu.add_command(label = "Stop reading", command = lambda: popupmsg("!","Not supported yet"))
#add filemenu TO THE menubar
menubar.add_cascade(label = "Start/Stop Graph", menu= startstopmenu)
tk.Tk.config(self,menu=menubar)
self.frames = {}
for f in (StartPage,GraphS1,GraphS2,GraphS3):
frame = f(container,self)
self.frames[f] = frame
frame.grid(row = 0,column = 0, sticky = "nsew")
self.show_frame(StartPage)
def show_frame(self,cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self,parent,control):
tk.Frame.__init__(self,parent)
label = tk.Label(self,text = "StartPage", font = LARGE_FONT)
label.pack(pady = 10, padx = 10)
b1 = ttk.Button(self,text = "Open page to run sensor 1",
command = lambda: control.show_frame(GraphS1))
b1.pack()
b2 = ttk.Button(self,text = "Exit",
command = quit)
b2.pack()
class GraphS1(tk.Frame):
def __init__(self,parent,control):
tk.Frame.__init__(self,parent)
label = tk.Label(self,text = "Real-time Graph ",font = LARGE_FONT)
label.pack()
b1 = ttk.Button(self,text = "Go back to StartPage",
command = lambda: control.show_frame(StartPage))
b1.pack()
b2 = ttk.Button(self,text = "Pause",
command = lambda: anim.event_source.stop())
b2.pack()
b3 = ttk.Button(self,text = "Play",
command = lambda: anim.event_source.start())
b3.pack()
b4 = ttk.Button(self,text = "Exit",
command = quit)
b4.pack()
class GraphS2(tk.Frame):
def __init__(self,parent,control):
tk.Frame.__init__(self,parent)
label = tk.Label(self,text = "Real-time Graph S2",font = LARGE_FONT)
label.pack()
b1 = ttk.Button(self,text = "Go back to StartPage",
command = lambda: control.show_frame(StartPage))
b1.pack()
b4 = ttk.Button(self,text = "Exit",
command = quit)
b4.pack()
class GraphS3(tk.Frame):
def __init__(self,parent,control):
tk.Frame.__init__(self,parent)
label = tk.Label(self,text = "Real-time Graph S3",font = LARGE_FONT)
label.pack()
b1 = ttk.Button(self,text = "Go back to StartPage",
command = lambda: control.show_frame(StartPage))
b1.pack()
b4 = ttk.Button(self,text = "Exit",
command = quit)
b4.pack()
#--------------------CALL MAIN CLASS-------------------------------------------------------#
#call main class
gui = main()
# gui.geometry("1280x720")
gui.state('zoomed') #maximise window
#call animation class
anim = animation.FuncAnimation(fig_s1,animate_graph,
fargs = (x_s1,y1_s1,y2_s1,ax1_s1,ax2_s1),interval = 1000)
#run in loop
gui.mainloop()
I have only programmed the sensor 1 page, but not the other two. I am assuming it will be the same approach for the other two.
Thanks in advance.
EDIT1: Made huge changes to the project to something I can work with. Won't need this anymore. Just leaving this here incase anyone has a solution to it. might help someone else