I was wondering how you can completely delete a plot from a tkinter window.
Assuming i would have a tkinter project like the following:
import tkinter as tk
from pandas import DataFrame
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
data1 = {'Country': ['US','CA','GER','UK','FR'],
'GDP_Per_Capita': [45000,42000,52000,49000,47000]
}
df1 = DataFrame(data1,columns=['Country','GDP_Per_Capita'])
root= tk.Tk()
figure1 = plt.Figure(figsize=(6,5), dpi=100)
ax1 = figure1.add_subplot(111)
bar1 = FigureCanvasTkAgg(figure1, root)
bar1.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
df1 = df1[['Country','GDP_Per_Capita']].groupby('Country').sum()
df1.plot(kind='bar', legend=True, ax=ax1)
ax1.set_title('Country Vs. GDP Per Capita')
def close_plot():
plt.close(figure1)
button_delete = Button(root, text='Delete', command = lambda: close_plot()).place(height=30, width = 100, rely=0.02, relx = 0.4)
root.mainloop()
I veen trying to use matplotlib.pyplot.close within a button but it doens't seem to work.
Anyone have a clue how to get rid of this plot.
Thank you very much!
Your figure is embedded in a tkinter widget. You need to keep a reference to that widget, and use its methods to add/remove it from the tkinter window:
Here we use pack_forget(): the widget is removed from the window, but still exists, and could be reused later.
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
def remove_plot():
w.pack_forget() # here you remove the widget from the tk window
# w.destroy()
if __name__ == '__main__':
# data
x, y = [1, 2, 3, 4], [1, 4, 9, 16]
# matplotlib stuff
figure1 = plt.Figure(figsize=(6, 5), dpi=100)
ax1 = figure1.add_subplot(111)
ax1.plot(x, y)
ax1.set_title('Country Vs. GDP Per Capita')
# tkinter stuff
root = tk.Tk()
bar1 = FigureCanvasTkAgg(figure1, root)
w = bar1.get_tk_widget()
w.pack(side=tk.LEFT, fill=tk.BOTH) # here you insert the widget in the tk window
button_delete = tk.Button(root, text='Remove Plot', command=remove_plot)
button_delete.place(height=30, width=100, rely=0.02, relx=0.4) # place is an odd choice of geometry manager, you will have to adjust it every time the title changes
root.mainloop()
Alternatively, if you don't need that figure any longer, you could use w.destroy (commented line) to destroy the widget.
Related
I would like to change the color of the toolbar when making a matplotlib figure in tkinter. I have managed to find and change the color of two parts. There is one remaining.
My code comes directly from https://matplotlib.org/stable/gallery/user_interfaces/embedding_in_tk_sgskip.html?highlight=embedding%20tk with three additional lines to change colors.
import tkinter
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
import numpy as np
root = tkinter.Tk()
root.wm_title("Embedding in Tk")
fig = Figure(figsize=(5, 4), dpi=100)
t = np.arange(0, 3, .01)
fig.add_subplot().plot(t, 2 * np.sin(2 * np.pi * t))
canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea.
canvas.draw()
color = "#d469a3"
toolbar = NavigationToolbar2Tk(canvas, root, pack_toolbar=False)
toolbar.config(background=color)
toolbar._message_label.config(background=color)
toolbar.update()
button = tkinter.Button(master=root, text="Quit", command=root.quit)
button.pack(side=tkinter.BOTTOM)
toolbar.pack(side=tkinter.BOTTOM)
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)
tkinter.mainloop()
This gives me the window:
What is the small, grey rectangle I have pointed out? How do I change its color?
It is an empty label. You can get a reference to it via winfo_children:
print (toolbar.winfo_children()[-2])
# .!navigationtoolbar2tk.!label
And to change its color:
toolbar.winfo_children()[-2].config(background=color)
There are plenty of web examples (1,2,3,4) and threads (1,2,3) about imbedding a plot into a tkinter window, but very few that address plotting in a separate environment and importing the resulting graph to the tkinter window.
In a nutshell, I have a program that calculates many different values, and exports those values to a separate file that creates a large number of plots. My tkinter application accepts parameters in Entry boxes, before applying them to the main file that does all the calculations. Typically, I would just follow the examples I linked, but with such a large number of plots being generated and the need to be able to select any particular graph I need at a given time, this would be inefficient and time consuming to brute-force. There must be a better way!
Below is a simplified example of how I am trying to accomplish this task:
import tkinter as tk
from matplotlib import pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,
NavigationToolbar2Tk)
import numpy as np
def example_plot(A):
# Plot generated outside of tkinter environment, but controlled by
# variable within tkinter window.
x = np.linspace(0, 10, 50)
y = A*x**2
fig, ax = plt.subplots()
ax.plot(x,y)
ax.set_xlabel('x')
ax.set_ylabel('y')
return fig
window = tk.Tk()
window.geometry('256x256')
variableEntry = tk.Entry(width = 10)
variableLabel = tk.Label(window, text = "A")
variableEntry.grid(row = 0, column = 0)
variableLabel.grid(row = 0, column = 1)
def plotButton():
A = variableEntry.get()
A = int(A)
figure = Figure(figsize = (1,1), dpi = 128)
add = figure.add_subplot(1,1,1)
example = example_plot(A)
add.imshow(example)
canvas = FigureCanvasTkAgg(figure)
canvas.get_tk_widget().grid(row = 2, column = 0)
toolbar = NavigationToolbar2Tk(canvas)
toolbar.update()
canvas._tkcanvas.grid(row = 3 , column = 0)
canvas.show()
applyButton = tk.Button(master = window, text = "Apply", command = plotButton)
applyButton.grid(row = 1,column = 0)
window.mainloop()
When I run this, set A to some integer and press apply, I get an error
TypeError: Image data of dtype object cannot be converted to float
It seems that add.imshow() doesn't like that I fed it the figure. Is there some way to obtain the figure (ie: example = example_plot(A)) and store it to display later?
Try this:
import tkinter as tk
from matplotlib import pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, \
NavigationToolbar2Tk
import numpy as np
def example_plot(A):
# Plot generated outside of tkinter environment, but controlled by
# variable within tkinter window.
x = np.linspace(0, 10, 50)
y = A*x*x # This should run slightly faster
fig, ax = plt.subplots()
ax.plot(x,y)
ax.set_xlabel("x")
ax.set_ylabel("y")
return fig
window = tk.Tk()
frame = tk.Frame(window)
frame.pack()
variable_entry = tk.Entry(frame, width=10)
variable_label = tk.Label(frame, text="A")
variable_entry.pack(side="left", fill="x")
variable_label.pack(side="left")
def plot():
A = int(variable_entry.get())
figure = Figure(figsize=(1, 1), dpi=128)
add = figure.add_subplot(1, 1, 1)
figure = example_plot(A)
canvas = FigureCanvasTkAgg(figure)
canvas.get_tk_widget().pack()
toolbar = NavigationToolbar2Tk(canvas, window)
toolbar.update()
canvas.get_tk_widget().pack()
# canvas.show() # There is no need for this
apply_button = tk.Button(window, text="Apply", command=plot)
apply_button.pack(fill="x")
window.mainloop()
Your example_plot returns a Figure so you can use figure = example_plot(A) and then FigureCanvasTkAgg(figure). I also added a frame and tried to make everything look better.
I wanna update a embedded chart in a Tkinter GUI with a button and I am not really sure how to do this, right now the closest solution to this that i found is this:
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from tkinter import *
# Creation of the mainwindow and container frame
root = Tk()
root.state('zoomed')
root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
mainframe = Frame(root)
mainframe.grid(row=0, column=0, sticky='nsew')
mainframe.tkraise()
# Embed the chart to the Tkinter Window
fig1 = plt.Figure(figsize=(8, 3), dpi=100)
plot1 = fig1.add_subplot(111)
chart1 = FigureCanvasTkAgg(fig1, mainframe)
chart1.get_tk_widget().place(x=300, y=10)
# Button section
def plot_graph():
A = [2, 5, 7, 4, 2, 0, 1, 2, 3]
fig1 = plt.Figure(figsize=(8, 3), dpi=100)
plot1 = fig1.add_subplot(111).plot(A)
chart1 = FigureCanvasTkAgg(fig1, mainframe)
chart1.get_tk_widget().place(x=300, y=10)
plot_btn = Button(mainframe, text='Plot graph', command=plot_graph)
plot_btn.grid(row=0,column=0)
# Mainloop
root.mainloop()
The problem with this is that when i press the plot button a new graph is created and inserted over the previous one and i dont want to do that, i just want to update the first one that i created. I am pretty new with Tkinter, any idea of how i can do this?
I need some help for my current code. I want to create a window by tkinter and show a plot in a canvas I created by matplotlib before. This point I reached yet. My problem is that I want to clear this canvas by hitting a button. To clear a canvas I suppose to initialize this before I can fill it with the plot.
So my question is: How can I fill a plot in a created canvas?
Below you can find a small code that shows my state of arts.
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,
NavigationToolbar2Tk)
def plot():
fig = Figure(figsize = (5, 5), dpi = 100)
y = [i**2 for i in range(101)]
# adding the subplot
plot1 = fig.add_subplot(111)
# plotting the graph
plot1.plot(y)
# creating the Tkinter canvas
# containing the Matplotlib figure
output = FigureCanvasTkAgg(fig, master = window)
output.draw()
# placing the canvas on the Tkinter window
output.get_tk_widget().pack()
def clear_plot():
canvas.delete('all')
# the main Tkinter window
window = Tk()
# setting the title
window.title('Plotting in Tkinter')
# dimensions of the main window
window.geometry("700x700")
canvas = Canvas(window, width=500, height=500)
canvas.pack()
# button that displays the plot
plot_button = Button(master = window, command = plot, height = 2, width = 10, text = "Plot")
clear_button = Button(master = window, command = clear_plot, height = 2, width = 10, text = "clear", background = "yellow")
# place the button
plot_button.pack()
clear_button.pack()
# run the gui
window.mainloop() ```
There is no direct way to clear the figure from the math plot canvas. So you can instead clear the canvas by destroying the widget itself using destroy method of tkinter canvas (note you cannot destroy the mathplot canvas itself as it doesn't have any methods such as destroy).
To place math plot canvas on tkinter canvas just set master as canvas object (output = FigureCanvasTkAgg(fig, master = canvas))
(Here is your corrected code)
from tkinter import *
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,
NavigationToolbar2Tk)
def plot():
global output, fig
fig = Figure(figsize = (5, 5), dpi = 100)
y = [i**2 for i in range(101)]
# adding the subplot
plot1 = fig.add_subplot(111)
# plotting the graph
plot1.plot(y)
# creating the Tkinter canvas
# containing the Matplotlib figure
output = FigureCanvasTkAgg(fig, master = canvas)
output.draw()
# placing the canvas on the Tkinter window
output.get_tk_widget().pack()
def clear_plot():
global output
if output:
for child in canvas.winfo_children():
child.destroy()
# or just use canvas.winfo_children()[0].destroy()
output = None
# the main Tkinter window
window = Tk()
output = None
fig = None
# setting the title
window.title('Plotting in Tkinter')
# dimensions of the main window
window.geometry("700x700")
canvas = Canvas(window, width=500, height=500, bg='white')
canvas.pack()
# button that displays the plot
plot_button = Button(master = window, command = plot, height = 2, width = 10, text = "Plot")
clear_button = Button(master = window, command = clear_plot, height = 2, width = 10, text = "clear", background = "yellow")
# place the button
plot_button.pack()
clear_button.pack()
# run the gui
window.mainloop()
Or you could use
def clear_plot():
global output
if output:
output.get_tk_widget().destroy()
output = None
here the output is your FigureCanvasTkAgg instance for anyone else looking to achieve this. And you just want to temporarily hide the plot output.get_tk_widget().pack_forget() and to display it again output.get_tk_widget().pack()
update
output.get_tk_widget() Return the Tk widget used to implement FigureCanvasTkAgg which means you can also use all the methods of
canvas. So,output.get_tk_widget().delete('all') works as well
I've got multiple pages in a notebook and have multiple graphs on each.
Is there a way to use one navigation toolbar for all graphs on a notebook page? Or how can I put each navigation toolbar directly underneath the graph it controls?
I've tried adding padx and pady which I thought would force it under the graph, but it keeps putting both toolbars underneath the Quit button.
I'm pretty new to Python and am really struggling to find an answer to this question!
import numpy as np
import matplotlib.pyplot as plt
import tkinter as app
from tkinter import ttk
from matplotlib.collections import LineCollection
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
x = np.linspace(1, 10, 10)
y = np.square(x)
z = x
#Make the root widget
root = app.Tk()
#Make the notebook
nb = ttk.Notebook(root)
nb.pack()
root.title("Test")
#plot first graph
f1 = app.Frame(nb)
nb.add(f1, text="Test")
app.Label(f1, text="Test window 1").pack(padx=5, pady=5)
fig1 = Figure(figsize=(5, 4), dpi=100)
fig1.add_subplot(111).plot(x, y)
canvas1 = FigureCanvasTkAgg(fig1, master=f1)
canvas1.draw()
canvas1.get_tk_widget().pack(side=app.TOP, fill=app.BOTH, expand=1)
#add toolbar
toolbar1 = NavigationToolbar2Tk(canvas1, f1)
toolbar1.update()
canvas1.get_tk_widget().pack(side=app.TOP, fill=app.BOTH, expand=1)
#plot second graph
fig2 = Figure(figsize=(5, 4), dpi=100)
fig2.add_subplot(111).plot(x, z)
canvas2 = FigureCanvasTkAgg(fig2, master=f1)
canvas2.draw()
canvas2.get_tk_widget().pack(side=app.TOP, fill=app.BOTH, expand=1, padx=5, pady=5)
#add toolbar
toolbar2 = NavigationToolbar2Tk(canvas2, f1)
toolbar2.update()
canvas2.get_tk_widget().pack(side=app.TOP, fill=app.BOTH, expand=1)
app.Button(f1, text='Exit', command=root.destroy).pack(padx=50, pady=50)
nb.select(f1)
nb.enable_traversal()
#Enter the mainloop
root.mainloop()