Reputation: 1991
I have some code that opens a plot in a tkinter toplevel widget. When I grab the corner of the toplevel to resize it, I would like the plot to resize along with the window.
import Tkinter
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg
from matplotlib.figure import Figure
class grapher_gui(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.graph()
def graph(self):
x_vals = [0,3,10,15]
y_vals = [232,120,45,23]
toplevel = Tkinter.Toplevel(width=2000)
figure = Figure(figsize=(10,5))
ax = figure.add_subplot(111)
plot = ax.plot(x_vals, y_vals, 'k-')
ax.set_xlabel('Time')
ax.set_ylabel('Numbers')
ax.set_title('Title')
canvas = FigureCanvasTkAgg(figure, master=toplevel)
canvas.show()
canvas.get_tk_widget().grid(row=0)
toolbar = NavigationToolbar2TkAgg(canvas, toplevel)
toolbar.grid(row=1, sticky=Tkinter.W)
toolbar.update()
toplevel.mainloop()
if __name__ == "__main__":
app = grapher_gui(None)
app.title('Grapher')
app.mainloop()
The only thing I could think of to try was adding sticky='NSEW'
to canvas.get_tk_widget().grid(row=0)
, but that doesn't work.
Upvotes: 3
Views: 14817
Reputation: 11
In my experience if you use grid you need to use rowconfigure and columnconfigure on the parent widgets and then also the child widgets in order to make them resize properly.
import tkinter
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
class grapher_gui(tkinter.Tk):
def __init__(self, parent):
tkinter.Tk.__init__(self, parent)
# Add col, row configure to the parent of toplevel
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
self.parent = parent
self.graph()
def graph(self):
x_vals = [0,3,10,15]
y_vals = [232,120,45,23]
toplevel = tkinter.Toplevel(width=2000)
# Add col, row configure to the toplevel itself
toplevel.columnconfigure(0, weight=1)
toplevel.rowconfigure(0, weight=1)
figure = Figure(figsize=(10,5))
ax = figure.add_subplot(111)
plot = ax.plot(x_vals, y_vals, 'k-')
ax.set_xlabel('Time')
ax.set_ylabel('Numbers')
ax.set_title('Title')
canvas = FigureCanvasTkAgg(figure, master=toplevel)
canvas.draw()
canvas.get_tk_widget().grid(row=0)
toplevel.mainloop()
if __name__ == "__main__":
app = grapher_gui(None)
app.title('Grapher')
app.mainloop()
Upvotes: 1
Reputation: 729
Try using pack
with expand=True
instead of grid
with sticky
. This simplified example works for me and the chart resizes with the window:
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import Tkinter as tk
import ttk
class My_GUI:
def __init__(self,master):
self.master=master
master.title("Dashboard")
f = Figure(figsize=(5,5), dpi=100)
a = f.add_subplot(111)
a.scatter([1,2,3,4,5,6,7,8],[5,6,1,3,8,9,3,5])
canvas1=FigureCanvasTkAgg(f,master)
canvas1.show()
canvas1.get_tk_widget().pack(side="top",fill='both',expand=True)
canvas1.pack(side="top",fill='both',expand=True)
root=tk.Tk()
gui=My_GUI(root)
root.mainloop()
I know it may be difficult depending on how far along your gui is to redesign using pack... but may be worthwhile instead of making a custom resize event handler. I do sympathize though, would be great if grid and sticky worked for the charts :/
Upvotes: 9
Reputation: 49330
You have to bind an event to the window resize - in other words, instead of having the program watch for something like a mouse click and do something when it happens, have the program watch for the window being resized and do something when that happens.
In graph()
, add:
self.toplevel.bind("<Configure>", resize)
Then make the variables instance variables (e.g. self.toplevel
instead of toplevel
) so you can refer to them from other methods.
Then add an instance method def resize(event):
that does whatever you would need to do to redraw the plot with a new size. You can base the size off of the Toplevel
window's dimensions by accessing toplevel.winfo_height()
and toplevel.winfo_width()
(add self
if you've made that an instance variable).
When the Toplevel
window is resized, that event will be "seen" and routed to the given callback (the method that's bound to that event).
Here's a small example that prints the size of the root
window whenever you resize it:
>>> import tkinter as tk
>>> root = tk.Tk()
>>> def onsize(event):
... print(root.winfo_width(), root.winfo_height())
...
>>> root.bind("<Configure>", onsize)
By the way, you only need to call mainloop()
once, in the main
area. Don't call it for every Toplevel
window.
Upvotes: 1