Control real-time animations from a menubar but only the page in the foreground

32 Views Asked by At

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:

  1. I noticed that the animations are running in background. I am not sure how to stop that.
  2. I don't know how to program the start and stop option in the menubar to control the current graph.
  3. I am not sure how to modify my function animate_graph that 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.

Image for Start Page

Image for Sensor 1 Page

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

0

There are 0 best solutions below