Related
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)
I'm really struggling with an live Plot using FuncAnimation and Tkinter. I'm not sure what info is needed to reproduce the problem. Please forgive me if anything is missing.
In a stripped down version it works fine:
from numpy.random import normal
from numpy import exp
def fn(x):
A=3e-8
B=6e3
Inoise = 1e-10
if x == 0:
I = Inoise
elif A*x**2*exp(-B/x) < Inoise:
I = Inoise
else:
I = A*x**2*exp(-B/x)
return normal(1,0.4,1)[0]*I
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,NavigationToolbar2Tk
from matplotlib.animation import FuncAnimation
from matplotlib.pyplot import Figure
from tkinter import Frame
from queue import Queue
class LivePlotWindow(Frame):
def __init__(self, master,xlabel='',ylabel='',logx=False,logy=False):
Frame.__init__(self, master)
self.pack(fill='both', expand=1)
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
self.canvas = FigureCanvasTkAgg(self.fig, master=self)
self.toolbar = NavigationToolbar2Tk(self.canvas, self)
self.toolbar.update()
self.canvas.get_tk_widget().pack()
self.xlabel = xlabel
self.ylabel = ylabel
self.logx = logx
self.logy = logy
self.q = Queue()
self._axini()
def _axini(self):
self.ax.cla()
self.ax.set_xlabel(self.xlabel)
self.ax.set_ylabel(self.ylabel)
if self.logx==True:
self.ax.set_xscale('log', nonposx='clip')
if self.logy==True:
self.ax.set_yscale('log', nonposy='clip')
self.fig.tight_layout()
def _update(self,i):
print(i)
[x,y] = self.q.get(block=True)
print([x,y])
self.ax.plot(x,y,'b.') # update the data
def run(self,ncalls):
self._axini()
print(ncalls)
self.ani = FuncAnimation(self.fig, self._update, range(ncalls-1), interval=100, repeat=False)
print(ncalls)
def addP(self,x,y):
self.q.put([x,y])
def stop(self):
self.ani.event_source.stop()
from tkinter import Tk
root = Tk()
root.title('Kennlinien-Messprogramm')
root.geometry('700x500')
plot = LivePlotWindow(root,'Voltage in V','Current in A',logy=True)
n=100
plot.run(n)
for i in range(n):
plot.addP(i*10,fn(i*10))
root.mainloop()
But when I'm integrating it in a GUI with menu, its just not calling the animation function. It seems when I pass my Plot object as a command call its not working anymore? If I call the start function directly it wortks. I have no idea why:
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,NavigationToolbar2Tk
from matplotlib.animation import FuncAnimation
from matplotlib.pyplot import Figure
from tkinter import Tk,Frame,Menu
from queue import Queue
class LivePlotWindow(Frame):
def __init__(self, master,xlabel='',ylabel='',logx=False,logy=False):
Frame.__init__(self, master)
self.pack(fill='both', expand=1)
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
self.canvas = FigureCanvasTkAgg(self.fig, master=self)
self.toolbar = NavigationToolbar2Tk(self.canvas, self)
self.toolbar.update()
self.canvas.get_tk_widget().pack()
self.xlabel = xlabel
self.ylabel = ylabel
self.logx = logx
self.logy = logy
self.q = Queue()
self._axini()
def _axini(self):
self.ax.cla()
self.ax.set_xlabel(self.xlabel)
self.ax.set_ylabel(self.ylabel)
if self.logx==True:
self.ax.set_xscale('log', nonposx='clip')
if self.logy==True:
self.ax.set_yscale('log', nonposy='clip')
self.fig.tight_layout()
def _update(self,i):
print(i)
[x,y] = self.q.get(block=True)
print([x,y])
self.ax.plot(x,y,'b.') # update the data
def run(self,ncalls):
self._axini()
print(ncalls)
self.ani = FuncAnimation(self.fig, self._update, range(ncalls-1), interval=100, repeat=False)
print(ncalls)
def addP(self,x,y):
self.q.put([x,y])
def stop(self):
self.ani.event_source.stop()
from tkinter import Tk
root = Tk()
root.title('Kennlinien-Messprogramm')
root.geometry('700x500')
plot = LivePlotWindow(root,'Voltage in V','Current in A',logy=True)
def start(menu,plot):
menu.entryconfig('\u25B6', state='disabled')
menu.entryconfig('\u25A0', state='normal')
n=100
plot.run(n)
for i in range(n):
plot.addP(i*10,fn(i*10))
def stop(menu,plot):
plot.stop
menu.entryconfig('\u25B6', state='normal')
menu.entryconfig('\u25A0', state='disabled')
menu = Menu(root)
root.config(menu=menu)
menu.add_command(label='\u25B6', command=lambda: start(menu,plot))
#menu.entryconfig(1, fg='green')
menu.add_command(label='\u25A0', command=lambda: stop(menu,plot))
menu.entryconfig('\u25A0', state='disabled')
#start(menu,plot)
root.mainloop()
Any help would be highly appreciated! ;)
I've found a solution, although I don't know how smart it is... Now I just continously plot xdata and ydata. Since I pass the reference, its updating any changes outside the plot class. However, I redraw the hole plot every time...
from tkinter import Frame
from matplotlib.pyplot import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.animation import FuncAnimation
class LivePlotWindow(Frame):
def __init__(self, master, xdata, ydata, xlabel='',ylabel='',logx=False,logy=False):
Frame.__init__(self, master)
self.pack(fill='both', expand=1)
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
self.ax.set_xlabel(xlabel)
self.canvas = FigureCanvasTkAgg(self.fig, master=self)
self.canvas.get_tk_widget().pack()
self.xdata = xdata
self.ydata = ydata
self.xlabel = xlabel
self.ylabel = ylabel
self.logx = logx
self.logy = logy
self.ani = FuncAnimation(self.fig, self._update, interval=500)
def _update(self,i):
self.ax.cla()
self.ax.set_xlabel(self.xlabel)
self.ax.set_ylabel(self.ylabel)
if self.logx:
self.ax.set_xscale('log', nonposx='clip')
if self.logy:
self.ax.set_yscale('log', nonposy='clip')
#if self.millikan:
# self.ax.plot(1/self.xdata,self.ydata)
#else:
self.ax.plot(self.xdata,self.ydata,'b.') # update the data
self.fig.tight_layout()
I am currently working on a program where the class MainApplication creates a GUI and loads an image. A second class Func includes functions that connect or disconnect matplotlib's event handler and a callback function on_press.
One issue is that I am using MainApplication's class objects self.fig and self.ax in the class Func. But I only get a TypeError: init() missing 2 required positional arguments: 'fig' and 'ax'.
How can I manipulate a class' object from another class in general?
MainApplication.py
import sys
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.image as mpimg
from Func import Func
class MainApplication(Tk.Frame):
def __init__(self, parent, *args, **kwargs):
Tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
parent.iconify
parent.grid_rowconfigure(1, weight=1)
parent.grid_columnconfigure(1, weight=1)
top_frame = Tk.Frame(parent)
top_frame.grid(row=0)
mid_frame = Tk.Frame(parent)
mid_frame.grid(row=1)
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
self.ax.set_aspect('equal')
canvas = FigureCanvasTkAgg(self.fig, mid_frame)
canvas.get_tk_widget().grid(row=0, column=0, sticky="nsew")
canvas._tkcanvas.grid(row=0, column=0, sticky="nsew")
img = mpimg.imread('stinkbug.png')
self.ax.imshow(img)
self.fig.canvas.draw()
self.var1 = Tk.IntVar()
chkbx1 = Tk.Checkbutton(top_frame, text='connect', variable=self.var1, command=self.de_activate)
chkbx1.grid(row=0, column=0, sticky="w")
def de_activate(self):
fc = Func()
fc.__init__(self.fig, self.ax)
print('checkbutton: '+str(self.var1.get()))
if self.var1.get() == 1:
fc.connect()
print('on_press connected (cid='+str(self.cidpress)+')')
else:
fc.disconnect()
print('on_press disconnected (cid='+str(self.cidpress)+')')
if __name__ == '__main__':
root = Tk.Tk()
MainApplication(root).grid(row=0, column=0, sticky="nsew")
root.mainloop()
Func.py
class Func():
def __init__(self, fig, ax):
self.fig = fig
self.ax = ax
def connect(self):
self.cidpress = self.fig.canvas.mpl_connect('button_press_event', self.on_press)
def disconnect(self):
self.fig.canvas.mpl_disconnect(self.cidpress)
def on_press(self, event):
if event.inaxes != self.ax: return
print('button pressed')
A correct version of this would look like
import sys
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.image as mpimg
#from Func import Func
class Func():
def __init__(self, fig, ax):
self.fig = fig
self.ax = ax
def connect(self):
self.cidpress = self.fig.canvas.mpl_connect('button_press_event', self.on_press)
def disconnect(self):
self.fig.canvas.mpl_disconnect(self.cidpress)
def on_press(self, event):
if event.inaxes != self.ax: return
print('button pressed')
class MainApplication(Tk.Frame):
def __init__(self, parent, *args, **kwargs):
Tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
parent.iconify
parent.grid_rowconfigure(1, weight=1)
parent.grid_columnconfigure(1, weight=1)
top_frame = Tk.Frame(parent)
top_frame.grid(row=0)
mid_frame = Tk.Frame(parent)
mid_frame.grid(row=1)
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
self.fc = Func(self.fig, self.ax)
self.ax.set_aspect('equal')
canvas = FigureCanvasTkAgg(self.fig, mid_frame)
canvas.get_tk_widget().grid(row=0, column=0, sticky="nsew")
canvas._tkcanvas.grid(row=0, column=0, sticky="nsew")
img = mpimg.imread('house.png')
self.ax.imshow(img)
self.fig.canvas.draw()
self.var1 = Tk.IntVar()
chkbx1 = Tk.Checkbutton(top_frame, text='connect', variable=self.var1, command=self.de_activate)
chkbx1.grid(row=0, column=0, sticky="w")
def de_activate(self):
print('checkbutton: '+str(self.var1.get()))
if self.var1.get() == 1:
self.fc.connect()
print('on_press connected (cid='+str(self.fc.cidpress)+')')
else:
self.fc.disconnect()
print('on_press disconnected (cid='+str(self.fc.cidpress)+')')
if __name__ == '__main__':
root = Tk.Tk()
MainApplication(root).grid(row=0, column=0, sticky="nsew")
root.mainloop()
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])
I am making an embeded matplotlib graph GUI program.
I want to make overlaid graphs in upper graph window when users click the "Update" button.
But, There is no response when I click "Update" button.
I am using Spyder Python 3.6 Version.
Below is what I wrote.
import matplotlib.pyplot as plt
import csv
import numpy as np
import tkinter as tk
from tkinter import ttk
import matplotlib as plt
plt.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
from matplotlib import style
class Analysis_app(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
tk.Tk.wm_title(self, "SIM Analyser")
right_f = tk.Frame(self)
right_f.pack(side=tk.RIGHT)
self.entry1 = ttk.Entry(right_f).pack()
self.entry2 = ttk.Entry(right_f).pack()
self.entry3 = ttk.Entry(right_f).pack()
self.entry4 = ttk.Entry(right_f).pack()
self.entry5 = ttk.Entry(right_f).pack()
Button1 = ttk.Button(right_f, text='Update', command=self.plot).pack(side=tk.BOTTOM)
self.left_f = tk.Frame(self)
self.left_f.pack(side=tk.LEFT)
f = Figure(figsize=(10,6), dpi=100)
self.upplot = f.add_subplot(211)
self.botplot = f.add_subplot(212)
a =self.upplot
a.clear()
a.plot([1,2,3],[1,2,3])
canvas = FigureCanvasTkAgg(f, self)
canvas.show()
canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
toolbar = NavigationToolbar2TkAgg(canvas, self)
toolbar.update()
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
self.mainloop()
def plot(self):
a =self.upplot
a.clear()
a.plot([1,2,3],[5,2,5])
You get no response because the canvas is not redrawn after changing the content of the plot.
The solution is to replace canvas = FigureCanvasTkAgg(f, self) by self.canvas = FigureCanvasTkAgg(f, self) so that you can redraw the canvas in self.plot:
def plot(self):
a = self.upplot
a.clear()
a.plot([1,2,3],[5,2,5])
self.canvas.draw_idle()
And then you should see the change after clicking on the update button.