I have created a tkinter GUI that when a button is pushed a tk.toplevel is created with an interactive animated chart inside. When I run it for the first time and push the button to create the tk.toplevel with the chart, it works well, but when I close the tk.toplevel and push the button to open a new tk.toplevel, another extra chart appears. Apart from that, If I close the program and I want to run it again I have to close the terminal because it freezes.
I have tried removing plt.ion() and doing plt.show() but none of these options solve the problem. Also, I have seen the use of multiprocessing, but I don't really understand it well and I don't know why is it needed.
Why does it happen? How can I solve this problem?
Here is the code that I have used:
import tkinter as tk
import matplotlib
import matplotlib.figure as mf
import matplotlib.pyplot as plt
import matplotlib.animation as ani
from matplotlib.animation import FuncAnimation
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
class SampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
container = tk.Frame(self)
container.pack()
frame = MAININTERFACE(parent=container, controller=self)
frame.grid(row=0, column=0, sticky="nsew")
def graph(self):
grafica1=GRAPHICATION(controller=self)
grafica1.Graph()
class MAININTERFACE(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.button=tk.Button(self, text='GRAPH', command=lambda: self.controller.graph())
self.button.pack(pady=20)
class GRAPHICATION(tk.Frame):
def __init__(self,controller):
tk.Frame.__init__(self)
self.controller=controller
self.x=[]
self.y=[]
def animation_frame(self, i):
if i==0.0:
self.time=0
self.energy=0
else:
self.time=self.time+1
self.energy=self.energy+1
self.x.append(self.time)
self.y.append(self.energy)
self.line.set_data(self.x,self.y)
self.ax.axis([0,10,0,10])
def Graph(self):
self.graphtoplevel=tk.Toplevel(self.controller)
self.graphtoplevel.title('Toplevel')
self.fig, self.ax = plt.subplots()
self.graph=FigureCanvasTkAgg(self.fig, self.graphtoplevel)
self.image=self.graph.get_tk_widget()
plt.ion()
self.line, = self.ax.plot(self.x,self.y)
self.image.grid(row=0, column=0, sticky='nsew')
self.animation=FuncAnimation(self.fig,func=self.animation_frame,frames=np.arange(0,11,1),interval=500, repeat=False)
self.snap_cursor = SnaptoCursor(self.ax, self.x, self.y)
self.fig.canvas.mpl_connect('button_press_event', self.snap_cursor.mouse_click)
class SnaptoCursor(object):
"""
Like Cursor but the crosshair snaps to the nearest x, y point.
For simplicity, this assumes that *x* is sorted.
"""
def __init__(self, ax, x, y):
self.ax = ax
self.lx = ax.axhline(color='k', linewidth=0.5) # the horiz line
self.ly = ax.axvline(color='k', linewidth=0.5) # the vert line
self.x = x
self.y = y
# text location in axes coords
self.txt = ax.text(0.05, 0.9, '', transform=ax.transAxes)
def mouse_click(self, event):
if not event.inaxes:
return
x, y = event.xdata, event.ydata
indx = min(np.searchsorted(self.x, x), len(self.x) - 1)
x = self.x[indx]
y = self.y[indx]
# update the line positions
self.lx.set_ydata(y)
self.ly.set_xdata(x)
self.txt.set_text('x=%1.2f, y=%1.2f' % (x, y))
self.ax.figure.canvas.draw()
if __name__ == "__main__":
app = SampleApp()
app.geometry('500x200')
app.title('MAIN GUI')
app.mainloop()
You need to make changes to your def Graph(self) function, as there are lines that are creating a window with tk.Toplevel and then the plt is creating another window.
Make your changes as follows, the capitalized comments you must do, and the others are optional for setting of titles:
def Graph(self):
#REMOVE THESE LINES
#self.graphtoplevel=tk.Toplevel(self.controller)
#self.graphtoplevel.title('Toplevel')
#modify the window title to your liking by assigning num
self.fig, self.ax = plt.subplots(num="TopLevel")
#REMOVE THESE LINES
#self.graph=FigureCanvasTkAgg(self.fig, self.graphtoplevel)
#self.image=self.graph.get_tk_widget()
plt.ion()
self.line, = self.ax.plot(self.x,self.y)
#REMOVE THIS LINE
#self.image.grid(row=0, column=0, sticky='nsew')
self.animation=FuncAnimation(self.fig,func=self.animation_frame,frames=np.arange(0,11,1),interval=500, repeat=False)
self.snap_cursor = SnaptoCursor(self.ax, self.x, self.y)
self.fig.canvas.mpl_connect('button_press_event', self.snap_cursor.mouse_click)
# displaying a title on top of figure (or do not add if not needed)
plt.title("Specific Fig Desc Title")
#ADD THIS LINE TO SHOW WINDOW
plt.show()
Please see this for more on figure window title:
Set matplotlib default figure window title
Related
I am currently working on a program that I am creating with Tkinter. Thereby large matrices (signals) are read in, which I represent as an image. In addition, I would like to display the signal at the point X (red vertical line) in the adjacent plot (interactive).
# Imports
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import numpy as np
from tkinter import *
# Global: Selected points with cursor
points = []
# Cursor
class Cursor:
def __init__(self, ax):
self.ax = ax
self.background = None
self.horizontal_line = ax.axhline(color='r', lw=0.8, ls='--')
self.vertical_line = ax.axvline(color='r', lw=0.8, ls='--')
self._creating_background = False
ax.figure.canvas.mpl_connect('draw_event', self.on_draw)
def on_draw(self, event):
self.create_new_background()
def set_cross_hair_visible(self, visible):
need_redraw = self.horizontal_line.get_visible() != visible
self.horizontal_line.set_visible(visible)
self.vertical_line.set_visible(visible)
return need_redraw
def create_new_background(self):
if self._creating_background:
return
self._creating_background = True
self.set_cross_hair_visible(False)
self.ax.figure.canvas.draw_idle()
self.background = self.ax.figure.canvas.copy_from_bbox(self.ax.bbox)
self.set_cross_hair_visible(True)
self._creating_background = False
def on_mouse_move(self, event, mode: str, matrix=None):
if self.background is None:
self.create_new_background()
if not event.inaxes:
need_redraw = self.set_cross_hair_visible(False)
if need_redraw:
self.ax.figure.canvas.restore_region(self.background)
self.ax.figure.canvas.blit(self.ax.bbox)
else:
self.set_cross_hair_visible(True)
x, y = event.xdata, event.ydata
if mode == "both":
self.horizontal_line.set_ydata(y)
self.vertical_line.set_xdata(x)
self.ax.figure.canvas.restore_region(self.background)
self.ax.draw_artist(self.horizontal_line)
self.ax.draw_artist(self.vertical_line)
elif mode == "horizontal":
self.ax.cla()
self.ax.plot(matrix[:, int(x)], range(0, matrix.shape[0], 1))
self.ax.figure.canvas.draw_idle()
self.horizontal_line.set_ydata(y)
self.ax.figure.canvas.restore_region(self.background)
self.ax.draw_artist(self.horizontal_line)
self.ax.figure.canvas.blit(self.ax.bbox)
# Graphical User Interface
class ToolGUI:
def __init__(self, master):
self.master = master
# Matrix (Example)
self.matrix = np.random.rand(3000, 5000)
# Subplots
self.fig = plt.figure(constrained_layout=True)
self.spec = self.fig.add_gridspec(5, 6)
self.ax_main = self.fig.add_subplot(self.spec[:, :-1])
self.ax_main.imshow(self.matrix, cmap='gray', aspect='auto')
self.ax_right = self.fig.add_subplot(self.spec[:, -1:], sharey=self.ax_main)
self.ax_right.get_yaxis().set_visible(False)
# Canvas - Drawing Area
self.canvas = FigureCanvasTkAgg(self.fig, master=master)
self.canvas.get_tk_widget().grid(column=0, row=0, sticky=NSEW)
# Cursor with crosshair
self.cursor_main = Cursor(self.ax_main)
self.fig.canvas.mpl_connect('motion_notify_event', lambda event: self.cursor_main.on_mouse_move(event, mode="both"))
self.cursor_right = Cursor(self.ax_right)
self.fig.canvas.mpl_connect('motion_notify_event', lambda event: self.cursor_right.on_mouse_move(event, mode="horizontal", matrix=self.matrix))
# Update Canvas
self.canvas.draw() # Update canvas
# Create root window
root = Tk()
# Root window title
root.title("Tool")
# Create GUI
my_gui = ToolGUI(root)
# Execute Tkinter
root.mainloop()
This example is only a small part of my program. In my full program, for example, certain points are picked out manually. With the help of the interactive plot a more exact selection of such points is possible.
Unfortunately, the program runs very slowly due to the use of this interactive plot. Since I haven't been working with Python for too long, I would appreciate any suggestions for improvement!
Thanks in advance! - Stefan
I have been stuck on this for a while and cannot work out the issue, so was hoping somebody on here could assist.
I am attempting to create a program to graph some data stored in a .csv file. My program starts with an empty graph and requires the user to first select a file, and then prompts the user to specify which columns from the csv file should be plotted. For the purposes of getting assistance, I have created a smaller program to outline the issue that I am experiencing.
As part of the graphing process, I want to have 5 gridlines displayed on the x-axis at all times, with the interval between these lines determined by the current viewable x-axis limits. For this reason, I have created a 'CustomFormatter' class which inherits from matplotlib.ticker's Formatter class, and overridden the 'call' method to try and get the desired behaviour. I believe that I need to do this in order to keep track of the current viewable xlimits of the graph, which then enable me to calculate the required intervals.
When I run my code, the following GUI is generated, which seems to be working as intended:
enter image description here
However, once I click the 'Load Data' button, my specified data is loaded into the graph, but the graph's x-axis labels and gridlines disappear, as shown in the below image.
enter image description here
It is not until I use the built-in pan tool to pan the graph that these gridlines and labels reappear, as shown in the below image.
enter image description here
The same thing happens when I zoom in on the graph. After zooming, it is not until I pan again that the x-axis labels and gridlines re-calibrate themselves to the desired positions.
Any help that anyone is willing to provide to help me solve this issue would be greatly appreciated. Hopefully it is just something basic or silly that I am overlooking.
Cheers.
The code I used to generate my matplotlib graph embedded in a tkinter frame is shown below:
import tkinter as tk
import matplotlib
import matplotlib.dates as mdates
from matplotlib.ticker import Formatter
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
class CustomFormatter(Formatter):
def __init__(self, ax, graph):
super().__init__()
self.set_axis(ax)
self.ax = ax
self.graph = graph
def __call__(self, x, pos=None):
xlims = self.ax.get_xlim()
ylims = self.ax.get_ylim()
xmin, xmax = xlims[0], xlims[1]
xmin = mdates.num2date(xmin)
xmax = mdates.num2date(xmax)
x = mdates.num2date(x)
interval = (xmax - xmin) / 6
self.ax.set_xticks([xmin, xmin + 1 * interval, xmin + 2 * interval, xmin + 3 * interval, xmin + 4 * interval, xmin + 5 * interval, xmax])
self.ax.grid(b=True, which='major', axis='both')
return f"{x.hour}:{x.minute}:{x.second}\n{x.day}/{x.month}/{x.year}"
class App(object):
def __init__(self, master):
initialFrame = tk.Frame(master)
initialFrame.pack(fill=tk.BOTH, expand=1)
master.title('Tkinter Graph Example')
self.master = master
self._load_data_button = tk.Button(initialFrame, takefocus=0, text='Load data')
self._load_data_button.bind('<Button-1>', self.load_data)
self._load_data_button.pack(side=tk.TOP, fill=tk.BOTH, padx=2, pady=2, ipady=5)
self.graphFrame = tk.Frame(initialFrame, borderwidth=5, relief=tk.SUNKEN)
self.graphFrame.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
self.f=Figure(figsize=(5,5), dpi=100)
self.a=self.f.add_subplot(111)
self.graph = FigureCanvasTkAgg(self.f, self.graphFrame)
self.graph.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
self.toolbar = NavigationToolbar2Tk(self.graph, self.graphFrame)
self.toolbar.update()
self.graph._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
self.a.set_xlabel('Time')
self.a.set_ylabel('Y-Axis')
self.a.set_title('Test')
self.a.grid(b=True)
self.a.xaxis.set_major_formatter(CustomFormatter(self.a, self.graph))#, self.df, self.toPlot, self.index_dict, self.name_dict))
self.graph.draw()
def load_data(self, event):
data = [1, 4, 2, 7, 4, 6]
time = [18820.410517624932, 18820.414807940015, 18820.41623804504, 18820.41766815007, 18820.41862155342, 18820.420528360122]
self.a.plot(time, data)
self.a.set_xlim(xmin=time[0], xmax=time[-1])
self.graph.draw()
def main():
root = tk.Tk()
app = App(root)
root.geometry('%dx%d+0+0'%(800,600))
root.mainloop()
if __name__ == '__main__':
main()
In the end I managed to figure out a solution to my issue. I added the following two lines of code to my App's _init method, along with two new function definitions.
self.a.callbacks.connect('xlim_changed', self.on_xlims_change)
self.a.callbacks.connect('ylim_changed', self.on_ylims_change)
def on_xlims_change(self, event_ax):
self.graph.draw()
def on_ylims_change(self, event_ax):
self.graph.draw()
These event handlers enabled me to redraw the graph whenever the x or y limits are changed (as they are during zoom or pan operations). I am now observing my desired behaviour.
Full code is shown below.
import tkinter as tk
import matplotlib
import matplotlib.dates as mdates
from matplotlib.ticker import Formatter
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
class CustomFormatter(Formatter):
def __init__(self, ax, graph):
super().__init__()
self.set_axis(ax)
self.ax = ax
self.graph = graph
def __call__(self, x, pos=None):
xlims = self.ax.get_xlim()
ylims = self.ax.get_ylim()
xmin, xmax = xlims[0], xlims[1]
xmin = mdates.num2date(xmin)
xmax = mdates.num2date(xmax)
x = mdates.num2date(x)
interval = (xmax - xmin) / 6
self.ax.set_xticks([xmin, xmin + 1 * interval, xmin + 2 * interval, xmin + 3 * interval, xmin + 4 * interval, xmin + 5 * interval, xmax])
self.ax.grid(b=True, which='major', axis='both')
return f"{x.hour}:{x.minute}:{x.second}\n{x.day}/{x.month}/{x.year}"
class App(object):
def __init__(self, master):
initialFrame = tk.Frame(master)
initialFrame.pack(fill=tk.BOTH, expand=1)
master.title('Tkinter Graph Example')
self.master = master
self._load_data_button = tk.Button(initialFrame, takefocus=0, text='Load data')
self._load_data_button.bind('<Button-1>', self.load_data)
self._load_data_button.pack(side=tk.TOP, fill=tk.BOTH, padx=2, pady=2, ipady=5)
self.graphFrame = tk.Frame(initialFrame, borderwidth=5, relief=tk.SUNKEN)
self.graphFrame.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
self.f=Figure(figsize=(5,5), dpi=100)
self.a=self.f.add_subplot(111)
self.graph = FigureCanvasTkAgg(self.f, self.graphFrame)
self.graph.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)
self.toolbar = NavigationToolbar2Tk(self.graph, self.graphFrame)
self.toolbar.update()
self.graph._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
self.a.set_xlabel('Time')
self.a.set_ylabel('Y-Axis')
self.a.set_title('Test')
self.a.grid(b=True)
self.a.callbacks.connect('xlim_changed', self.on_xlims_change)
self.a.callbacks.connect('ylim_changed', self.on_ylims_change)
self.a.xaxis.set_major_formatter(CustomFormatter(self.a, self.graph))#, self.df, self.toPlot, self.index_dict, self.name_dict))
self.graph.draw()
def on_xlims_change(self, event_ax):
self.graph.draw()
def on_ylims_change(self, event_ax):
self.graph.draw()
def load_data(self, event):
data = [1, 4, 2, 7, 4, 6]
time = [18820.410517624932, 18820.414807940015, 18820.41623804504, 18820.41766815007, 18820.41862155342, 18820.420528360122]
self.a.plot(time, data)
self.a.set_xlim(xmin=time[0], xmax=time[-1])
self.graph.draw()
def main():
root = tk.Tk()
app = App(root)
root.geometry('%dx%d+0+0'%(800,600))
root.mainloop()
if __name__ == '__main__':
main()
Here is a tkinter program, boiled down from a GUI I am working on:
import tkinter as tk
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
class App(tk.Tk):
def __init__(self):
super(App, self).__init__()
self.main_frame = tk.Frame(self,)
self.main_frame.pack(fill=tk.BOTH, expand=1)
self.plot1_button = tk.Button(self.main_frame, text='Plot 1',
command=self.draw_plot1)
self.plot1_button.pack(fill=tk.X,expand=1)
self.plot2_button = tk.Button(self.main_frame, text='Plot 2',
command=self.draw_plot2)
self.plot2_button.pack(fill=tk.X,expand=1)
self.FIG, self.AX = plt.subplots()
self.canvas = FigureCanvasTkAgg(self.FIG, master=self.main_frame)
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
self.toolbar = NavigationToolbar2Tk(self.canvas, self.main_frame)
self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def draw_plot1(self):
self.clear_axes()
fig = self.AX.plot(np.random.rand(10),np.random.rand(10), color='red')
self.canvas.draw_idle()
self.toolbar.update()
def draw_plot2(self):
self.clear_axes()
im = self.AX.matshow(np.random.rand(100,100))
self.canvas.draw_idle()
self.toolbar.update()
cb = plt.colorbar(im, ax=self.AX)
def clear_axes(self):
for ax in self.FIG.axes:
ax.clear()
if ax != self.AX:
ax.remove()
root = App()
root.resizable(False, False)
root.mainloop()
The Plot 1 button draws a random line plot, while the Plot 2 button draws a random heatmap with a colorbar. The Plot 1 button can be clicked repeatedly, creating new random line plots as expected. After 10 clicks, the display looks fine:
:
But the Plot 2 button causes the figure to shrink each time it is clicked. After 10 clicks, the graph is uninterpretable:
Additionally, the figure size persists when clicking Plot 1 again:
These are the .png files saved from the application's toolbar, but the same can be seen in the GUI window. I have tried add updates to the GUI/canvas (e.g. self.update(), self.canvas.draw_idle()) at different locations but haven't found anything that affects the issue. I added the clear_axes() function because in the real GUI I have some figures with multiple axes and this removes them, but apparently it does not help here.
I have found that if the color bar is removed, the problem disappears (i.e. comment out cb = plt.colorbar(im, ax=self.AX)), but I would like to have this as part of the figure. Can anyone shed light on what is going on, or can anyone suggest a fix? I'm on matplotlib 3.2.1.
The problem is you are not clearing the colorbar when you clear the axes.
class App(tk.Tk):
def __init__(self):
super(App, self).__init__()
self.main_frame = tk.Frame(self,)
...
self.cb = None
...
def draw_plot2(self):
self.clear_axes()
im = self.AX.matshow(np.random.rand(100,100))
self.canvas.draw_idle()
self.toolbar.update()
self.cb = plt.colorbar(im, ax=self.AX)
def clear_axes(self):
if self.cb:
self.cb.remove()
self.cb = None
for ax in self.FIG.axes:
ax.clear()
if ax != self.AX:
ax.remove()
Also note that you should use matplotlib.figure.Figure instead of pyplot when working with tkinter. See this for the official sample.
When you run the code the error should be pretty obvious, just click your mouse in the black space and move it around. I'm not sure how the line segments are being created, I pulled it from the library and its kind of confusing. How can I get the line segments to plot along my scatter plot? When I looks at the segments array being generated I believe it’s creating pairs of x,x and y,y instead of x,y and x,y
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap
import numpy as np
import random
class Mouse_Game:
'''Creates a matplotlib canvas object that plot mouse coordinates when animated.'''
def __init__(self, root, width=400, height=400):
self.frame = tk.Frame(master=root)
self.status = False
self.fig = plt.Figure(dpi=100, facecolor='black')
self.ax = self.fig.add_axes([0,0,1,1], fc='black')
self.width = width
self.height = height
self.ax.axis('off')
#set up the canvas object and bind commands
self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame, resize_callback=self.configure) # A tk.DrawingArea.
self.canvas.draw()
self.canvas.get_tk_widget().pack(side='top', fill='both', expand=True)
self.fig.canvas.mpl_connect('button_press_event', self.animate)
self.fig.canvas.mpl_connect('motion_notify_event', self.motion)
def animate(self, event):
#Check if currently running
if not self.status:
self.status = True
self.capture = np.array([(event.xdata, event.ydata)]*40)
else:
self.status = False
#plot a line that follows the mouse around
while self.status:
self.ax.clear()
self.ax.set_xlim(0, self.width)
self.ax.set_ylim(0, self.height)
x = self.capture[:,0]
y = self.capture[:,1]
############################################################
points = self.capture.T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
###########################################################
lc = LineCollection(segments, cmap='magma')
lc.set_array(x)
lc.set_linewidth(2)
self.ax.add_collection(lc)
self.ax.scatter(x, y, marker='o')
self.fig.canvas.draw()
self.fig.canvas.flush_events()
def motion(self, event):
if self.status:
#Append mouse coordinates to array when the mouse moves
self.capture = self.capture[1:40]
self.capture = np.append(self.capture, [(event.xdata, event.ydata)], axis=0)
def configure(self, event):
#Used to adjust coordinate setting when the screen size changes
self.width, self.height = self.canvas.get_width_height()
def _quit():
#destroy window
root.quit()
root.destroy()
root = tk.Tk()
root.wm_title("Mouse Plot")
MG = Mouse_Game(root=root)
MG.frame.pack(expand=True, fill='both')
button = tk.Button(root, text="Quit", command=_quit)
button.pack(side='bottom', pady=10)
tk.mainloop()
I'm not sure why you transpose the array. If you leave the transposition out, it'll work just fine
points = self.capture.reshape(-1, 1, 2)
I am using matplotlib and create a figure adding a plot. How can I replace the plot by a new one or a new grid of plots?
In my present code I create an axes with the menu dosomething() then add other red lines with the menu dosomethingelse() over the same axes.
Everytime I dosomething(), a new figure is appended bellow the current one, but I actually want to replace the current axes by a new one in the same figure. How can I do that?
import numpy as np
from Tkinter import *
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class Test1:
def __init__(self, windows, data, axes):
self.windows = windows
self.data = data
self.figure = axes.figure
self.axes = axes
self.im = self.axes.plot(data)
def dosomething():
global test1
global fig
fig = Figure(figsize=(12, 4))
axes = fig.add_subplot(111)
canvas = FigureCanvasTkAgg(fig, master=windows)
canvas.get_tk_widget().pack()
data=np.arange(100)
test1=Test1(windows, data, axes)
def dosomethingelse():
global test1
test1.axes.plot(np.arange(100)+10*(np.random.rand(100)-0.5),'-r')
test1.figure.canvas.show()
windows = Tk()
menubar = Menu(windows)
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="Do something", command=dosomething)
filemenu.add_command(label="Do somethingelse", command=dosomethingelse)
menubar.add_cascade(label="Tool", menu=filemenu)
windows.config(menu=menubar)
windows.mainloop()
This doesn't looks like a very standard way of doing tkinter. At least it's not very clear to me what you're trying to achieve. You have the following line in your dosomething:
test1=Test1(windows, data, axes)
which is what is producing your new window every time you run it. Also, there's no need for global variables, when you're inside a class. Just use self.variable = ..., and the variable will be available throughout your class and to objects that you pass the class to.
I haven't tried this, but perhaps something like this:
def dosomething():
try:
self.canvas.get_tk_widget().destroy()
except:
pass
fig = Figure(figsize=(12, 4))
axes = fig.add_subplot(111)
self.canvas = FigureCanvasTkAgg(fig, master=windows)
self.canvas.get_tk_widget().pack()
data=np.arange(100) # not sure what this is for
def dosomethingelse():
try:
self.canvas.get_tk_widget().destroy()
except:
pass
fig = Figure(figsize=(12, 4))
fig.plot(np.arange(100)+10*(np.random.rand(100)-0.5),'-r')
self.canvas = FigureCanvasTkAgg(fig, master=windows)
self.canvas.get_tk_widget().pack()
I am posting the full code which worked for me after the answer of #DrXorile
import numpy as np
from Tkinter import *
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class Test1:
def __init__(self, data):
self.data = data
def dosomething(self):
try:
self.canvas.get_tk_widget().destroy()
except:
pass
self.figure = Figure(figsize=(12, 4))
self.axes = self.figure.add_subplot(111)
self.im = self.axes.plot(data)
self.canvas = FigureCanvasTkAgg(self.figure, master=windows)
self.canvas.get_tk_widget().pack()
def dosomethingelse(self):
self.axes.plot(np.arange(100)+10*(np.random.rand(100)-0.5),'-r')
self.figure.canvas.show()
data=np.arange(100) # data to plot
test1=Test1(data)
windows = Tk()
menubar = Menu(windows)
filemenu = Menu(menubar, tearoff=0)
filemenu.add_command(label="Do something", command=test1.dosomething)
filemenu.add_command(label="Do somethingelse", command=test1.dosomethingelse)
menubar.add_cascade(label="Tool", menu=filemenu)
windows.config(menu=menubar)
windows.mainloop()