How to start and stop animation.FuncAnimation by pressing a button? - python

I'm new to python programming, so sorry if it's a stupid question.
I am using python, matplotlib and tkinter to plot a live graph of data in a tkinter gui. Therefore I followed the tutorial of this link: https://pythonprogramming.net/plotting-live-bitcoin-price-data-tkinter-matplotlib/
Here, the animation function is used, to plot the data. I would like to start and stop this animationFunc by pressing a button on the gui, but don't know how to realize it.
I would be grateful for any kind of help!
Thanks
Here is the code:
import Tkinter as tk
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,
NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
import serial
import sys
ser = serial.Serial(port='COM3', baudrate = 19200, bytesize=8, parity='N',
stopbits=1, timeout=None,rtscts=0)
LARGE_FONT = ("Verdana", 12)
style.use('ggplot')
f = Figure(figsize=(5,5), dpi = 100)
a = f.add_subplot(111)
yList = []
def animate(i):
# I receive data as a string with a hex number, so here I read it, take
#the number and append it to the yList
r = ser.readline()
he = r.split(" ")[2]
g = int(he,16)
d = str(g)
yList.append(g)
a.clear()
a.plot(yList)
class Sensors(tk.Tk):
def __init__(self,*args, **kwargs ):
tk.Tk.__init__(self,*args,**kwargs)
container = tk.Frame(self)
container.pack(side="top", fill = "both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (ChooseSensor, Sensor3):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(ChooseSensor)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
#here I want to define the function to stop the animate function and to
#stop the serial port
def stopPlot(event,cont):
#command to serial port to stop sensor
ser.write(b'S1,P,0\r\n')
#here i want to create the function to start the animate function
def animate_start(self,cont):
#command to configure and start sensor
ser.write(b'S1,S,2\r\n')
ser.write(b'S1,T,1\r\n')
ser.write(b'S1,P,1\r\n')
class ChooseSensor(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text = "Choose Sensor", font= LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = tk.Button(self,text="Graph Page",
command=lambda:controller.show_frame(Sensor3))
button1.pack()
class Sensor3(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text = "Graph Page", font= LARGE_FONT)
label.pack(pady=10, padx=10)
button1 = tk.Button(self,text="Start Plot",
command=lambda:controller.animate_start(self))
button1.pack()
button2 = tk.Button(self,text="Stop Plot",
command=lambda:controller.stopPlot(self))
button2.pack()
button3 = tk.Button(self,text="Back to Home",
command=lambda:controller.show_frame(ChooseSensor))
button3.pack()
canvas = FigureCanvasTkAgg(f, self)
canvas.show()
canvas.get_tk_widget().pack(side=tk.TOP, fill = tk.BOTH, expand = True)
app = Sensors()
ani = animation.FuncAnimation(f,animate, interval=1000)
app.mainloop()
EDIT:
I tried to do something like this:
def animate_start(self,cont):
global anim_start
anim_start=True
#Konfiguriert und schreibt Sensor1
ser.write(b'S1,S,2\r\n')
ser.write(b'S1,T,1\r\n')
ser.write(b'S1,P,1\r\n')
return anim_start
def stopPlot(event,cont):
anim_start=False
ser.write(b'S1,P,0\r\n')
return anim_start
And then execute the animation function only, when anim_start == True:
app = Sensors()
if anim_start == True:
ani = animation.FuncAnimation(f,animate, interval=1000)
app.mainloop()
Now I can see, that the commands are send and data is received (LED lights at the USB Serial bridge blink), but there is no data added to the yList anymore and so no graph is plotted

Related

How to link two classes from two different python files

So I'm building a website for my school project using tkinter (specifically customtkinter). What i want to achieve is that when I click the manager button on the main window, the manager window to open up. The main window and manager window exist in two different python files. Unfortunately its been nothing but errors for the past 5 hours for me.
Below is the code for the main window
from tkinter import *
from tkinter import messagebox,ttk
from PIL import ImageTk, Image
import Py_SQL_conn as PS
import tkinter.messagebox
import customtkinter
from customtkinter import *
from tkcalendar import Calendar, DateEntry
from datetime import date
from datetime import datetime
import trailtk19 as tr
#try to increase window size...
customtkinter.set_appearance_mode("System") # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue"
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
class SampleApp(customtkinter.CTk):
def __init__(self, *args, **kwargs):
customtkinter.CTk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = CTkFrame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage,Booking_page,Ava_Sch_page,Passenger_page):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class Booking_page(CTkFrame):
def __init__(self, parent, controller):
CTkFrame.__init__(self, parent)
self.controller = controller
main_frame=CTkFrame(master=self)
main_frame.pack(fill=BOTH, expand=1)
but1=CTkButton(master=main_frame,width=190,text="Main",command=lambda: controller.show_frame("StartPage"))
but1.pack(side=LEFT,pady=0,padx=0)
but2=CTkButton(master=main_frame,width=190,text="See sch",command=lambda: controller.show_frame("Ava_Sch_page"))
but2.pack(side=LEFT,pady=0,padx=0)
class Ava_Sch_page(CTkFrame):
def __init__(self, parent, controller):
CTkFrame.__init__(self, parent)
self.controller = controller
frame1 = CTkFrame(master=self)
frame1.pack(fill=BOTH, expand=1)
my_canvas = Canvas(frame1)
my_canvas.pack(side=LEFT, fill=BOTH, expand=1)
ctk_textbox_scrollbar = customtkinter.CTkScrollbar(frame1, command=my_canvas.yview)
ctk_textbox_scrollbar.pack(side=RIGHT, fill=Y)
my_canvas.configure(yscrollcommand=ctk_textbox_scrollbar.set)
my_canvas.bind('<Configure>', lambda e: my_canvas.configure(scrollregion = my_canvas.bbox("all")))
main_frame=CTkFrame(my_canvas,width=1300,height=2000+1000,fg_color="white")
main_frame.pack()
my_canvas.create_window((0,0), window=main_frame, anchor="nw")
header=CTkFrame(main_frame,width=1300,height=55,corner_radius=0,fg_color="#57a1f8")
header.place(x=0,y=0)
man_lab=CTkLabel(header,text="Welcome Manager",bg='white',text_font=('Microsoft YaHei UI Light',23))
man_lab.place(x=10,y=6)
#Navigation bar
nav_frame=CTkFrame(main_frame,width=925,corner_radius=0,fg_color="#1f6aa5",height=35)
nav_frame.place(x=0,y=55)
but1=CTkButton(nav_frame,text="Bookings",fg_color="#144870",corner_radius=0,height=35,width=90,state=DISABLED,command=lambda: controller.show_frame("Book_det"))
but1.pack(side=LEFT,pady=0,padx=0)
but2=CTkButton(nav_frame,text="Passengers",corner_radius=0,height=35,width=100,state=DISABLED,command=lambda: controller.show_frame("Pass_det"))
but2.pack(side=LEFT,pady=0,padx=0)
but3=CTkButton(nav_frame,text='',corner_radius=0,height=35,width=1000,state=DISABLED)
but3.pack(side=LEFT,pady=0,padx=0,fill=BOTH)
path = "aurora.jpeg"
self.img = ImageTk.PhotoImage(Image.open(path))
Label(master=main_frame,image=self.img,height=650,width=510).place(x=500,y=50)
customtkinter.CTkButton(master=main_frame,width=190,text="Booking",command=lambda: controller.show_frame("Booking_page")).place(x=1000,y=150)
customtkinter.CTkButton(master=main_frame,width=190,text="details",command=lambda: controller.show_frame("Passenger_page")).place(x=1000,y=1950)
class Passenger_page(CTkFrame):
def __init__(self, parent, controller):
CTkFrame.__init__(self, parent)
self.controller = controller
main_frame=CTkFrame(master=self)
main_frame.pack(fill=BOTH, expand=1)
customtkinter.CTkButton(master=main_frame,width=190,text="Avail",command=lambda: controller.show_frame("Ava_Sch_page")).pack(side=LEFT,pady=0,padx=0)
customtkinter.CTkButton(master=main_frame,width=190,text="Main",command=lambda: controller.show_frame("StartPage")).pack(side=LEFT,pady=0,padx=0)
class StartPage(CTkFrame):
def __init__(self, parent, controller):
CTkFrame.__init__(self, parent)
self.controller = controller
def confirm():
global na
u=user.get()
p=passw.get()
datsum=PS.connectorconn("Select * from managers;")
for x in datsum:
if (u==x[3]) and (p==x[4]):
con="yes"
break
else:
con="yes"
if con=="yes":
controller.show_frame("Booking_page")
#Website opens into booking page
frame1 = CTkFrame(master=self)
frame1.pack(fill=BOTH, expand=1)
# Create A Canvas
my_canvas = Canvas(frame1)
my_canvas.pack(side=LEFT, fill=BOTH, expand=1)
# Add A Scrollbar To The Canvas
ctk_textbox_scrollbar = customtkinter.CTkScrollbar(frame1, command=my_canvas.yview)
ctk_textbox_scrollbar.pack(side=RIGHT, fill=Y)
##my_scrollbar = ttk.Scrollbar(main_frame, orient=VERTICAL, command=my_canvas.yview)
##my_scrollbar.pack(side=RIGHT, fill=Y)
# Configure The Canvas
my_canvas.configure(yscrollcommand=ctk_textbox_scrollbar.set)
my_canvas.bind('<Configure>', lambda e: my_canvas.configure(scrollregion = my_canvas.bbox("all")))
# Create ANOTHER Frame INSIDE the Canvas
main_frame=CTkFrame(my_canvas,width=1300,height=2000)
main_frame.pack()
# Add that New frame To a Window In The Canvas
my_canvas.create_window((0,0), window=main_frame, anchor="nw")
#Code for scroll bar ends here. To use again, copy paste the above code and when adding widgets to the frame, make sure to put master=mainframe.
#NOTE: Only use place geometry manager to add subsequent widgets.
header=CTkFrame(main_frame,width=1300,height=55,corner_radius=0,fg_color="#57a1f8")
header.place(x=0,y=55)
man_lab=CTkLabel(header,text=" ",bg='white',text_font=('Microsoft YaHei UI Light',23))
man_lab.place(x=10,y=6)
#Navigation bar
nav_frame=CTkFrame(main_frame,width=925,corner_radius=0,fg_color="#1f6aa5",height=35)
nav_frame.place(x=0,y=108)
but=CTkButton(nav_frame,text="Schedule",corner_radius=0,height=35,state=DISABLED,command=lambda: controller.show_frame("Pass_det"))
but.pack(side=LEFT,pady=0,padx=0)
but1=CTkButton(nav_frame,text="Book flight",fg_color="#144870",corner_radius=0,height=35,state=DISABLED,command=lambda: controller.show_frame("Book_det"))
but1.pack(side=LEFT,pady=0,padx=0)
but2=CTkButton(nav_frame,text="View ticket",corner_radius=0,height=35,state=DISABLED,command=lambda: controller.show_frame("Booking_page"))
but2.pack(side=LEFT,pady=0,padx=0)
but3=CTkButton(nav_frame,text="Cancel ticket",corner_radius=0,height=35,state=DISABLED,command=lambda: controller.show_frame("Booking_page"))
but3.pack(side=LEFT,pady=0,padx=0)
but4=CTkButton(nav_frame,text="About us",corner_radius=0,height=35,state=DISABLED,command=lambda: controller.show_frame("Booking_page"))
but4.pack(side=LEFT,pady=0,padx=0)
def y():
tr.Display()
but5=CTkButton(nav_frame,text="Manager Log",corner_radius=0,height=35,command=lambda: y)
but5.pack(side=LEFT,pady=0,padx=0)
but6=CTkButton(nav_frame,text='',corner_radius=0,height=35,width=1000,state=DISABLED)
but6.pack(side=LEFT,pady=0,padx=0,fill=BOTH)
root=SampleApp()
root.title("Main_window")
root.attributes('-fullscreen',True)
root.resizable(False,False)
root.configure(bg="white")
def on_closing():
if messagebox.askokcancel("Quit", "Do you want to quit?"):
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()
And below is the manger window
from tkinter import *
from tkinter import messagebox,ttk
from PIL import ImageTk, Image
import Py_SQL_conn as PS
import tkinter.messagebox
import customtkinter
from customtkinter import *
#try to increase window size...
customtkinter.set_appearance_mode("System") # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue"
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
class SampleApp(customtkinter.CTk):
def __init__(self, *args, **kwargs):
customtkinter.CTk.__init__(self, *args, **kwargs)
# the container is where we'll stack a bunch of frames
# on top of each other, then the one we want visible
# will be raised above the others
container = CTkFrame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage,Book_det,Pass_det):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("StartPage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
class StartPage(CTkFrame):
def __init__(self, parent, controller):
CTkFrame.__init__(self, parent)
self.controller = controller
def confirm():
global na
u=user.get()
p=passw.get()
datsum=PS.connectorconn("Select * from managers;")
#error if not in datsum and then the stuff i guess....good prgress
for x in datsum:
if (u==x[3]) and (p==x[4]):
con="yes"
break
else:
con="no"
if con=="yes":
controller.show_frame("Book_det")
else:
messagebox.showwarning("Warning","Invalid Credentials.")
main_frame=CTkFrame(master=self)
main_frame.pack(fill=BOTH, expand=1)
path = "aurora.jpeg"
self.img = ImageTk.PhotoImage(Image.open(path))
Label(master=main_frame,image=self.img,height=650,width=510).place(x=85,y=50)
frame=CTkFrame(master=main_frame,width=350,height=350,bg="white")
frame.place(x=480,y=83)
heading=CTkLabel(master=frame,text="Manager Login",fg="#57a1f8",bg='white',text_font=('Microsoft YaHei UI Light',23))
heading.place(x=61,y=25)
#username
user= CTkEntry(master=frame,width=290,placeholder_text="Username", border=0,bg='white',text_font=('Microsoft YaHei UI Light',11))
user.place(x=30,y=120)
##user.bind('<FocusIn>',on_enter)
##user.bind('<FocusOut>',on_leave)
##CTkFrame(master=frame,width=295,bg='black').place(x=25,y=107)
#Password
passw= customtkinter.CTkEntry(master=frame,width=290,placeholder_text="Password",bg='white',border=0,text_font=('Microsoft YaHei UI Light',11))
passw.place(x=30,y=180)
##passw.bind('<FocusIn>',on_enter)
##passw.bind('<FocusOut>',on_leave)
##CTkFrame(master=frame,width=295,bg='black').place(x=25,y=177)
#Submit button
customtkinter.CTkButton(master=frame,width=190,text="Log in",command=lambda: confirm()).place(x=80,y=270)
class Book_det(CTkFrame):
def __init__(self, parent, controller):
CTkFrame.__init__(self, parent)
#For every frame...
self.controller = controller
main_frame1=CTkFrame(master=self)
main_frame1.pack(fill=BOTH, expand=1)
header=CTkFrame(main_frame1,width=925,height=55,corner_radius=0,fg_color="#57a1f8")
header.pack()
man_lab=CTkLabel(header,text="Welcome Manager",bg='white',text_font=('Microsoft YaHei UI Light',23))
man_lab.place(x=10,y=6)
nav_frame=CTkFrame(main_frame1,width=925,corner_radius=0,fg_color="#1f6aa5",height=35)
nav_frame.pack(anchor="w",fill=BOTH)
but1=CTkButton(nav_frame,text="Bookings",fg_color="#144870",corner_radius=0,height=35,width=90,command=lambda: controller.show_frame("Book_det"))
but1.pack(side=LEFT,pady=0,padx=0)
but2=CTkButton(nav_frame,text="Passengers",corner_radius=0,height=35,width=100,command=lambda: controller.show_frame("Pass_det"))
but2.pack(side=LEFT,pady=0,padx=0)
but3=CTkButton(nav_frame,text='',corner_radius=0,height=35,width=1000,state=DISABLED)
but3.pack(side=LEFT,pady=0,padx=0,fill=BOTH)
fs_frame = CTkFrame(main_frame1)
fs_frame.pack(fill=BOTH,expand=1,pady=25,padx=25)
# fs_frame.pack()
reg_dep = ttk.Treeview(fs_frame,height=18)
reg_dep['columns'] = ('BookingID', 'Dep_Des_Code', 'Arr_Des_Code', 'Passenger_ID','Departure_Date','Return_Date','Booked_Seat')
reg_dep.column("#0", width=0, stretch=NO)
reg_dep.column('BookingID',anchor=CENTER, width=120)
reg_dep.column('Dep_Des_Code',anchor=CENTER, width=140)
reg_dep.column('Arr_Des_Code',anchor=CENTER,width=140)
reg_dep.column('Passenger_ID',anchor=CENTER,width=140)
reg_dep.column('Departure_Date',anchor=CENTER,width=160)
reg_dep.column('Return_Date',anchor=CENTER,width=160)
reg_dep.column('Booked_Seat',anchor=CENTER,width=140)
reg_dep.heading("#0",text="",anchor=CENTER)
reg_dep.heading('BookingID',text='BookingID',anchor=CENTER)
reg_dep.heading('Dep_Des_Code',text='Dep_Des_Code',anchor=CENTER)
reg_dep.heading('Arr_Des_Code',text='Arr_Des_Code',anchor=CENTER)
reg_dep.heading('Passenger_ID',text='Passenger_ID',anchor=CENTER)
reg_dep.heading('Departure_Date',text='Departure_Date',anchor=CENTER)
reg_dep.heading('Return_Date',text='Return_Date',anchor=CENTER)
reg_dep.heading('Booked_Seat',text='Booked_Seat',anchor=CENTER)
import mysql.connector as mycon
conn=mycon.connect(host="localhost",user="root",passwd="12345678",database="aurora_airlines")
cursor=conn.cursor()
cursor.execute("Select * from bookings;")
data=cursor.fetchall()
Records=[]
for row in data:
Records.append(row)
count=cursor.rowcount
for iid in range(count):
reg_dep.insert(parent='',index='end',iid=iid,text='',
values=(Records[iid][0],Records[iid][1],Records[iid][2],Records[iid][3], Records[iid][4],Records[iid][5],Records[iid][6]))
reg_dep.pack(pady=85)
## button = customtkinter.CTkButton(main_frame1,corner_radius=0,height=10,text="Go to the start page",
## command=lambda: controller.show_frame("StartPage"))
## button.grid(row=0,column=0)
## button1 = customtkinter.CTkButton(main_frame1, text="go to page two",
## command=lambda: controller.show_frame("PageTwo"))
## button1.grid(row=0,column=1)
class Pass_det(CTkFrame):
def __init__(self, parent, controller):
CTkFrame.__init__(self, parent)
#For every frame...
self.controller = controller
main_frame1=CTkFrame(master=self)
main_frame1.pack(fill=BOTH, expand=1)
header=CTkFrame(main_frame1,width=925,height=55,corner_radius=0,fg_color="#57a1f8")
header.pack()
man_lab=CTkLabel(header,text="Welcome Manager",bg='white',text_font=('Microsoft YaHei UI Light',23))
man_lab.place(x=10,y=6)
nav_frame=CTkFrame(main_frame1,height=37,width=925,corner_radius=0,fg_color="#1f6aa5")
nav_frame.pack(anchor="w",fill=BOTH,padx=0,pady=0)
but1=CTkButton(nav_frame,text="Bookings",corner_radius=0,height=35,width=90,command=lambda: controller.show_frame("Book_det"))
but1.pack(side=LEFT,pady=0,padx=0)
but2=CTkButton(nav_frame,text="Passengers",fg_color="#144870",corner_radius=0,height=35,width=100,command=lambda: controller.show_frame("Pass_det"))
but2.pack(side=LEFT,pady=0,padx=0)
but3=CTkButton(nav_frame,text='',corner_radius=0,height=35,width=1000,state=DISABLED)
but3.pack(side=LEFT,pady=0,padx=0,fill=BOTH)
fs_frame = CTkFrame(main_frame1)
fs_frame.pack(fill=BOTH,expand=1,pady=25,padx=25)
# fs_frame.pack()
reg_dep = ttk.Treeview(fs_frame,height=22)
CTkLabel(fs_frame,text="").pack()
reg_dep['columns'] = ('Passenger_ID', 'First_Name', 'Last_Name',
'Passport_ID', 'Meal_Preference','Phone_number',
'Email', 'Class')
reg_dep.column("#0", width=0, stretch=NO)
reg_dep.column('Passenger_ID',anchor=CENTER, width=120)
reg_dep.column('First_Name',anchor=CENTER, width=140)
reg_dep.column('Last_Name',anchor=CENTER,width=140)
reg_dep.column('Passport_ID',anchor=CENTER,width=140)
reg_dep.column('Meal_Preference',anchor=CENTER,width=160)
reg_dep.column('Phone_number',anchor=CENTER,width=160)
reg_dep.column('Email',anchor=CENTER,width=140)
reg_dep.column('Class',anchor=CENTER,width=160)
reg_dep.heading("#0",text="",anchor=CENTER)
reg_dep.heading('Passenger_ID',text='Passenger_ID',anchor=CENTER)
reg_dep.heading('First_Name',text='First_Name',anchor=CENTER)
reg_dep.heading('Last_Name',text='Last_Name',anchor=CENTER)
reg_dep.heading('Passport_ID',text='Passport_ID',anchor=CENTER)
reg_dep.heading('Meal_Preference',text='Meal_Preference',anchor=CENTER)
reg_dep.heading('Phone_number',text='Phone_number',anchor=CENTER)
reg_dep.heading('Email',text='Email',anchor=CENTER)
reg_dep.heading('Class',text='Class',anchor=CENTER)
import mysql.connector as mycon
conn=mycon.connect(host="localhost",user="root",passwd="12345678",database="aurora_airlines")
cursor=conn.cursor()
cursor.execute("Select * from passengers;")
data=cursor.fetchall()
Records=[]
for row in data:
Records.append(row)
count=cursor.rowcount
for iid in range(count):
reg_dep.insert(parent='',index='end',iid=iid,text='',
values=(Records[iid][0],Records[iid][1],Records[iid][2],Records[iid][3], Records[iid][4],Records[iid][5],Records[iid][6],Records[iid][7]))
reg_dep.pack(pady=44)
def Display():
root=SampleApp()
root.title("Manager_Login")
root.geometry('925x500+300+200')
root.resizable(False,False)
root.configure(bg="white")
root.mainloop()
I keep getting the 'image "pyimage2" doesn't exist' error. Then when I used the call method, it kept opening another python window which I don't want. I also don't want to merge the above codes into one file since I have a lot of code to add for the other parts of the project.
I apologize for the messy code since these were just test files
I'm just a beginner in python so I might have made some mistakes I'm not aware of. I would be grateful if anyone could help me! Thank you!

How do you use Toplevel() to detach/open a new graph window in Tkinter?

I'm a total newbie to programming in Tkinter and could use some help while I figure out how Tkinter deals with classes, functions and so on.
I'm trying to make a way to "detach" the graph window by a button in the graphical window. I need to be able to open the class, GraphPage_cpu -> see the page with the plots and then press the "test" button and have it open a new window with the cpu measure plot.
Also I have to give a huge credit to the user, "j_4321". He really helped me a lot with figuring out how to plot cpu measures!
This is the main function for trying to detach (or "re-open" the graphical in a new window):
def detach_graph(self):
self.detach_graph = Toplevel(self.parent)
self.app = GraphPage_cpu(self.detach_graph)
Error message:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\ProgramData\Anaconda3\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "<ipython-input-12-f73649f067eb>", line 124, in new_window
self.newWindow = Toplevel(self.parent)
AttributeError: 'GraphPage_cpu' object has no attribute 'parent'
Full code:
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
from tkinter import Toplevel
from tkinter.filedialog import askopenfilename
from tkinter.messagebox import showinfo, showwarning, askquestion
from tkinter import OptionMenu
from tkinter import StringVar
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
from matplotlib import style
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.dates as mdates
from psutil import cpu_percent
from psutil import virtual_memory
from datetime import datetime, timedelta
from sklearn.metrics import silhouette_score
from sklearn.cluster import KMeans
import sklearn.cluster as cluster
import scipy.spatial.distance as sdist
from sklearn.ensemble import IsolationForest
import pandas as pd
import numpy as np
import seaborn as sn
from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import StandardScaler
RANDOM_STATE = 42 #used to help randomly select the data points
low_memory=False
LARGE_FONT= ("Verdana", 12)
style.use("ggplot")
f = Figure(figsize=(5,5), dpi=100)
a = f.add_subplot(111)
class Analyticsapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
#tk.Tk.iconbitmap(self, default="iconimage_kmeans.ico") #Icon for program
tk.Tk.wm_title(self, "Advanched analytics")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, GraphPage_cpu):
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, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text=
"Advanched analytics", font=LARGE_FONT)
label.pack(pady=10, padx=10)
button3 = ttk.Button(self, text="CPU Usage",
command=lambda: controller.show_frame(GraphPage_cpu))
button3.pack(fill='x')
class GraphPage_cpu(tk.Frame):
def __init__(self, parent, controller, nb_points=360):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="CPU Usage", font=LARGE_FONT)
label.pack(pady=10, padx=10, side='top')
# matplotlib figure
self.figure = Figure(figsize=(5, 5), dpi=100)
self.ax = self.figure.add_subplot(111)
# format the x-axis to show the time
myFmt = mdates.DateFormatter("%H:%M:%S")
self.ax.xaxis.set_major_formatter(myFmt)
# initial x and y data
dateTimeObj = datetime.now() + timedelta(seconds=-nb_points)
self.x_data = [dateTimeObj + timedelta(seconds=i) for i in range(nb_points)]
self.y_data = [0 for i in range(nb_points)]
# create the plot
self.plot = self.ax.plot(self.x_data, self.y_data, label='CPU')[0]
self.ax.set_ylim(0, 100)
self.ax.set_xlim(self.x_data[0], self.x_data[-1])
self.canvas = FigureCanvasTkAgg(self.figure, self)
toolbar = NavigationToolbar2Tk(self.canvas, self)
toolbar.update()
button1 = ttk.Button(self, text="Back",
command=lambda: controller.show_frame(StartPage))
button1.pack(side='bottom')
button2 = ttk.Button(self, text="Test",
command=self.new_window)
button2.pack(side='bottom')
self.canvas.get_tk_widget().pack(side='top', fill=tk.BOTH, expand=True)
self.animate_cpu()
def detach_graph(self):
self.detach_graph = Toplevel(self.parent)
self.app = GraphPage_cpu(self.detach_graph)
def animate_cpu(self):
# append new data point to the x and y data
self.x_data.append(datetime.now())
self.y_data.append(cpu_percent())
# remove oldest data point
self.x_data = self.x_data[1:]
self.y_data = self.y_data[1:]
# update plot data
self.plot.set_xdata(self.x_data)
self.plot.set_ydata(self.y_data)
self.ax.set_xlim(self.x_data[0], self.x_data[-1])
self.canvas.draw_idle() # redraw plot
self.after(1000, self.animate_cpu) # repeat after 1s
app = Analyticsapp()
app.geometry('500x400')
app.mainloop()
If I understand the issue (cannot run due to dependencies),
you need to get the parent of some widget.
tkinter uses 'master', not 'parent', so try:
self.newWindow = Toplevel(self.master)

Tkinter plt.figure() does not plot, but Figure() does

I started building a Tkinter application and was initially using matplotlib's Figure and figure.add_subplot. With that everything works perfectly. For more customization, I now want to move to pyplot and subplot2grid, but in doing so, suddenly all of my tkinter variable stop working.
In my MWE, the variable gArrChoice tracks which radio button is selected and should default to the first option. Based on this option, the graph should plot a line hovering around 0.1. If the second option gets selected, the graph should change to hover around 5. The graph auto-updates ever 2.5 seconds. If you comment out the 3 lines below "Working" and use the 3 "Not Working" lines instead, the default settings of the variable stops working and switching between radio buttons has no effect anymore. Declaring a inside the animate function does not change the problem.
How can I use plt with Tkinter and not destroy my variables?
MWE:
import tkinter as tk
import matplotlib
matplotlib.use("TkAgg") #make sure you use the tkinter backend
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.animation as animation
import numpy as np
gArrChoice = 0
#Working - using Figure and add_subplot
from matplotlib.figure import Figure
f = Figure()
a = f.add_subplot(121)
#Not Working - using plt and subplot2grid
# from matplotlib import pyplot as plt
# f = plt.figure()
# a = plt.subplot2grid((10, 7), (0, 0), rowspan=10, colspan=5)
class BatSimGUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
frame = StartPage(container,self)
self.frames[StartPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
#Set defaults for global variable
global gArrChoice
gArrChoice = tk.IntVar()
gArrChoice.set(1)
radioArr1 = tk.Radiobutton(self, variable=gArrChoice, text="Exponential", value=1, command= lambda: print(gArrChoice.get()))
radioArr1.grid(row=2, column=0)
radioArr2 = tk.Radiobutton(self, variable=gArrChoice, text="Normal", value=2, command= lambda: print(gArrChoice.get()))
radioArr2.grid(row=3, column=0)
#Add Canvas
canvas = FigureCanvasTkAgg(f, self)
canvas.draw()
canvas.get_tk_widget().grid(row=1, column=1, columnspan=7, rowspan = 10)
def animate(i):
global gArrChoice
if gArrChoice.get() == 1:
lam = np.random.exponential(scale=.1, size = 100).reshape(-1,1)
else:
lam = np.random.normal(loc=5, scale=1, size = 100).reshape(-1,1)
a.clear()
a.step(list(range(100)), list(lam))
#Actually run the interface
app = BatSimGUI()
app.geometry("800x600")
ani = animation.FuncAnimation(f, animate, interval = 2500)
app.mainloop()
I'think that an OO approach it'would be better.
See below, I've use thread and queue to manage the plot animation, you can even set time interval and change on fly the graph type
Good job anyway, very interesting
#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import threading
import queue
import time
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
try:
from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk as nav_tool
except:
from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg as nav_tool
import numpy as np
class MyThread(threading.Thread):
def __init__(self, queue, which, ops, interval):
threading.Thread.__init__(self)
self.queue = queue
self.check = True
self.which = which
self.ops = ops
self.interval = interval
def stop(self):
self.check = False
def run(self):
while self.check:
if self.which.get() ==0:
lam = np.random.exponential(scale=.1, size = 100).reshape(-1,1)
else:
lam = np.random.normal(loc=5, scale=1, size = 100).reshape(-1,1)
time.sleep(self.interval.get())
args = (lam, self.ops[self.which.get()])
self.queue.put(args)
else:
args = (None, "I'm stopped")
self.queue.put(args)
class Main(ttk.Frame):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.which = tk.IntVar()
self.interval = tk.DoubleVar()
self.queue = queue.Queue()
self.my_thread = None
self.init_ui()
def init_ui(self):
f = ttk.Frame()
#create graph!
self.fig = Figure()
self.fig.suptitle("Hello Matplotlib", fontsize=16)
self.a = self.fig.add_subplot(111)
self.canvas = FigureCanvasTkAgg(self.fig, f)
toolbar = nav_tool(self.canvas, f)
toolbar.update()
self.canvas._tkcanvas.pack(fill=tk.BOTH, expand=1)
w = ttk.Frame()
ttk.Button(w, text="Animate", command=self.launch_thread).pack()
ttk.Button(w, text="Stop", command=self.stop_thread).pack()
ttk.Button(w, text="Close", command=self.on_close).pack()
self.ops = ('Exponential','Normal',)
self.get_radio_buttons(w,'Choice', self.ops, self.which,self.on_choice_plot).pack(side=tk.TOP, fill=tk.Y, expand=0)
ttk.Label(w, text = "Interval").pack()
tk.Spinbox(w,
bg='white',
from_=1.0, to=5.0,increment=0.5,
justify=tk.CENTER,
width=8,
wrap=False,
insertwidth=1,
textvariable=self.interval).pack(anchor=tk.CENTER)
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1)
f.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
def launch_thread(self):
self.on_choice_plot()
def stop_thread(self):
if self.my_thread is not None:
if(threading.active_count()!=0):
self.my_thread.stop()
def on_choice_plot(self, evt=None):
if self.my_thread is not None:
if (threading.active_count()!=0):
self.my_thread.stop()
self.my_thread = MyThread(self.queue,self.which, self.ops, self.interval)
self.my_thread.start()
self.periodiccall()
def periodiccall(self):
self.checkqueue()
if self.my_thread.is_alive():
self.after(1, self.periodiccall)
else:
pass
def checkqueue(self):
while self.queue.qsize():
try:
args = self.queue.get()
self.a.clear()
self.a.grid(True)
if args[0] is not None:
self.a.step(list(range(100)), list(args[0]))
self.a.set_title(args[1], weight='bold',loc='left')
else:
self.a.set_title(args[1], weight='bold',loc='left')
self.canvas.draw()
except queue.Empty:
pass
def get_radio_buttons(self, container, text, ops, v, callback=None):
w = ttk.LabelFrame(container, text=text,)
for index, text in enumerate(ops):
ttk.Radiobutton(w,
text=text,
variable=v,
command=callback,
value=index,).pack(anchor=tk.W)
return w
def on_close(self):
if self.my_thread is not None:
if(threading.active_count()!=0):
self.my_thread.stop()
self.parent.on_exit()
class App(tk.Tk):
"""Start here"""
def __init__(self):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_exit)
self.set_title()
self.set_style()
Main(self)
def set_style(self):
self.style = ttk.Style()
#('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
self.style.theme_use("clam")
def set_title(self):
s = "{0}".format('Simple App')
self.title(s)
def on_exit(self):
"""Close all"""
if messagebox.askokcancel("Simple App", "Do you want to quit?", parent=self):
self.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()
There seems to be a bug on updating the IntVar() when you use pyplot instead. But you can workaround it if you force a change in value in your radio buttons:
radioArr1 = tk.Radiobutton(self, variable=gArrChoice, text="Exponential", value=1, command= lambda: gArrChoice.set(1))
radioArr2 = tk.Radiobutton(self, variable=gArrChoice, text="Normal", value=2, command= lambda: gArrChoice.set(2))
Or you can make your IntVar as an attribute of StartPage instead which seems to work just fine.
import tkinter as tk
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.animation as animation
import numpy as np
from matplotlib import pyplot as plt
class BatSimGUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
self.frames = {}
self.start_page = StartPage(container,self)
self.frames[StartPage] = self.start_page
self.start_page.grid(row=0, column=0, sticky="nsew")
self.start_page.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.gArrChoice = tk.IntVar()
self.gArrChoice.set(1)
radioArr1 = tk.Radiobutton(self, variable=self.gArrChoice, text="Exponential", value=1)
radioArr1.grid(row=2, column=0)
radioArr2 = tk.Radiobutton(self, variable=self.gArrChoice, text="Normal", value=2)
radioArr2.grid(row=3, column=0)
self.f = plt.figure()
self.a = plt.subplot2grid((10, 7), (0, 0), rowspan=10, colspan=5)
canvas = FigureCanvasTkAgg(self.f, self)
canvas.draw()
canvas.get_tk_widget().grid(row=1, column=1, columnspan=7, rowspan = 10)
def animate(self,i):
if self.gArrChoice.get() == 1:
lam = np.random.exponential(scale=.1, size = 100).reshape(-1,1)
else:
lam = np.random.normal(loc=5, scale=1, size = 100).reshape(-1,1)
self.a.clear()
self.a.step(list(range(100)), list(lam))
app = BatSimGUI()
app.geometry("800x600")
ani = animation.FuncAnimation(app.start_page.f, app.start_page.animate, interval=1000)
app.mainloop()
It seems the problem is to replace
# Not Working - using plt and subplot2grid
from matplotlib import pyplot as plt
f = plt.figure()
a = plt.subplot2grid((10, 7), (0, 0), rowspan=10, colspan=5)
in a pyplot- independent fashion. One option is the use of gridspec:
from matplotlib.figure import Figure
f = Figure()
gs = f.add_gridspec(10,7)
a = f.add_subplot(gs[:, :5])

Putting an continuously updating animated plot with other components inside a tkinter gui

I want to show a plot within one of the pages in my GUI. The data plotted, theData will constantly change. Whenever theData is changed, the value of valueChangedtheData is written to 1. The mapping of time and data is here:
xsize=100
xdata,ydata = [],[]
def data_gen():
t = data_gen.t
global theData
global valueChangedtheData
while True:
if (valueChangedtheData == 1):
valueChangedtheData = 0;
t+=0.1
val=float(theData);
if val>1000:
continue
yield t, val
else: pass
def animate(data):
t, val = data
if t>-1:
xdata.append(t)
ydata.append(val)
if t>xsize: # Scroll to the left.
a.set_xlim(t-xsize, t)
line.set_data(xdata, ydata)
return line,
def on_close_figure(event):
sys.exit(0)
data_gen.t = -1
f = plt.figure()
f.canvas.mpl_connect('close_event', on_close_figure)
#f = Figure(figsize=(5,5), dpi=100)
a = f.add_subplot(111)
line, = a.plot([], [], lw=2)
a.set_ylim(0, 250)
a.set_xlim(0, xsize)
a.grid()
I've defined my the container of my GUI as such:
class Gui(Tk):
def __init__(self, *args, **kwargs):
Tk.__init__(self, *args, **kwargs)
container = Frame(self)
container.pack(side="top", fill = "both", expand = TRUE)
container.grid_rowconfigure(0, weight = 1)
global theData;
self.MyReading = StringVar()
self.frames={}
for F in (StartPage, PageOne):
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()
and the page showing the plot:
class StartPage(Frame): #The Graphical Page
def __init__(self, parent, controller):
Frame.__init__(self,parent)
label = Label(self, text="StartPage")
label.pack()
label1 = Label(self, textvariable = controller.theData)
label1.pack
canvas = FigureCanvasTkAgg(f, self)
canvas.draw() #changed from show to draw
canvas.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=True)
toolbar = NavigationToolbar2Tk(canvas, self)
toolbar.update()
canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=True)
And in order to start the animation and Gui:
root = Gui()
update_reading()
ani = animation.FuncAnimation(f, animate, data_gen, blit = False, interval=100, repeat = False)
root.mainloop()
With update_reading() updating the label:
def update_reading():
global theData
global valueChangedtheData
theData = randint(1,20) #This is just an example of the changing value
print(theData)
valueChangedtheData = 1;
root.MyReading.set(str(theData));
root.after(100,update_reading)
However, after adding the canvas on the page, all of the labels that rely on the variable classes would refuse to shop-up, including the value for theData but the plot is graphing. Also, labels that show images would also refuse to show-up. After commenting the canvas, data mapping and animation, the label would appear back. Am I missing an important initializing code? Also, during plotting, there is a considerable "sluggishness" happening in the gui window. Could this be alleviated through a better code writing? Thanks
Imports:
from tkinter import *
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import style
from random import randint
EDIT: Added missed and necessary code
EDIT2: Included all the imports

Data display GUI with multi threading Python

I'm basically simulating acquiring data from the serial port and plotting it on a graph displayed in a GUI which is made using Tkinter. The incoming serial data is simulated by a simple while loop which calculates a sine function and adds the value to a queue which is size 100, the data generating part of the program is written under the class named DataThread
import Tkinter as tk
import numpy as np
import matplotlib as plt
from collections import deque
import threading
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from time import sleep
class DataThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.ISCONNECTED = 1
self.d = deque(maxlen=100)
def run(self):
t = 1
while True:
wave = np.sin(2*np.pi*100*t*0.001)
self.d.append(wave)
print(wave)
t = t+1
sleep(1)
if self.ISCONNECTED == 0:
break
The other class generates the GUI , it creates an instance of the DataThread class and calling the function start_d should start the thread which generates the data. I would like to stop the thread by pressing the stop button but I'm not sure how to stop the thread.
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid(column=0, row=0, sticky=(tk.N, tk.S, tk.E, tk.W))
self.fig = Figure()
self.ax_1 = self.fig.add_subplot(111)
self.createWidgets()
self.a = DataThread()
def createWidgets(self):
self.frame = tk.Frame(borderwidth=5,
relief="sunken", width=300, height=20)
self.frame.grid(column=0, row=0, columnspan=10, rowspan=4,
sticky=(tk.N, tk.S, tk.E, tk.W))
self.frame.rowconfigure(0, weight=1)
self.frame.columnconfigure(0, weight=1)
self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame)
self.canvas.get_tk_widget().grid()
self.canvas.show()
self.namelbl = tk.Label(text="Start DAQ")
self.namelbl.grid(column=10, row=0, columnspan=2,
sticky=(tk.S, tk.W), padx=5)
self.start = tk.Button( text='Start',
command=self.quit)
self.start.grid(column=10, row=1,sticky=tk.N+tk.S+tk.E+tk.W)
self.stop = tk.Button( text='Stop',
command=self.stop_serial)
self.stop.grid(column=11, row=1,sticky=tk.N+tk.S+tk.E+tk.W)
def start_d(self):
self.a.ISCONNECTED=1
self.a.start()
def readSensor(self):
data2plot = self.a.d
self.ax_1.plot(range(data2plot),data2plot)
self.root.update()
self.root.after(527, self.readSensor)
def stop_serial(self):
self.a.ISCONNECTED=0
def run(self):
self.mainloop()
And the last part which simply runs the GUI
if __name__ == "__main__":
Application().run()
I based my code of the following question: Dynamically updating Tkinter window based on serial data
The difference being that both, the GUI thread and the data thread, are created simultaneously, but in my case it wouldn't work because I want to start the thread only when the start button is pressed.

Categories