Resizing Tkinter Frames with fixed aspect-ratio
There are at least a couple ways to solve this. The simplest, IMO, is to have your padding widget be a container for your content widget, and then you explicitly set the width and height of the content widget using place
. This is one of the edge cases where place
is preferred over grid
or pack
.
In the following example I've created a function which lets you pass in a content frame, a padding frame, and an aspect ratio. It then constrains the size of the content frame by the aspect ratio and the size of the container. It will make the content window fill the container in the X dimension and then set the height appropriately. If the resulting window is too tall to be visible, it sets the max height to the height of the container and adjusts the width instead.
I've tried to keep most of the code from the question intact, even though this isn't exactly how I would normally choose to code it. I've given the widgets distinct colors so it's easier to see what is happening.
import Tkinter as tkfrom Tkconstants import *r=tk.Tk()def set_aspect(content_frame, pad_frame, aspect_ratio): # a function which places a frame within a containing frame, and # then forces the inner frame to keep a specific aspect ratio def enforce_aspect_ratio(event): # when the pad window resizes, fit the content into it, # either by fixing the width or the height and then # adjusting the height or width based on the aspect ratio. # start by using the width as the controlling dimension desired_width = event.width desired_height = int(event.width / aspect_ratio) # if the window is too tall to fit, use the height as # the controlling dimension if desired_height > event.height: desired_height = event.height desired_width = int(event.height * aspect_ratio) # place the window, giving it an explicit size content_frame.place(in_=pad_frame, x=0, y=0, width=desired_width, height=desired_height) pad_frame.bind("<Configure>", enforce_aspect_ratio)pad_frame = tk.Frame(borderwidth=0, background="bisque", width=200, height=200)pad_frame.grid(row=0, column=0, sticky="nsew", padx=10, pady=20)content_frame=tk.Frame(r,borderwidth=5,relief=GROOVE, background="blue")tk.Label(content_frame,text='content').pack()set_aspect(content_frame, pad_frame, aspect_ratio=2.0/1.0) r.rowconfigure(0, weight=1)r.columnconfigure(0, weight=1)r.mainloop()
This will work best if the containing widget has contents that easily adjust to the size of the container. If there is a complex layout of widgets within, some widgets could get chopped off if they don't fit when the window is shrunk below its natural size.
You could bind a function to the <Configure>
event for a Frame
which contains the content and padding frames. The <Configure>
event will be fired when you resize a window. Use the event's width and height attributes to fix the size of the content frame by updating the weights of the rows and columns using rowconfigure
and columnconfigure
You will need two rows and two columns in the container frame to have a square content frame. With a tall window, you need padding in the second row. And with a wide window you need padding in the second column.
A working example:
import Tkinter as tkfrom Tkconstants import *class Application(tk.Frame): def __init__(self, master, width, height): tk.Frame.__init__(self, master) self.grid(sticky=N + S + E + W) master.rowconfigure(0, weight=1) master.columnconfigure(0, weight=1) self._create_widgets() self.bind('<Configure>', self._resize) self.winfo_toplevel().minsize(150, 150) def _create_widgets(self): self.content = tk.Frame(self, bg='blue') self.content.grid(row=0, column=0, sticky=N + S + E + W) self.rowconfigure(0, weight=1) self.rowconfigure(1, weight=1) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=1) def _resize(self, event): '''Modify padding when window is resized.''' w, h = event.width, event.height w1, h1 = self.content.winfo_width(), self.content.winfo_height() print w1, h1 # should be equal if w > h: self.rowconfigure(0, weight=1) self.rowconfigure(1, weight=0) self.columnconfigure(0, weight=h) self.columnconfigure(1, weight=w - h) elif w < h: self.rowconfigure(0, weight=w) self.rowconfigure(1, weight=h - w) self.columnconfigure(0, weight=1) self.columnconfigure(1, weight=0) else: # width = height self.rowconfigure(0, weight=1) self.rowconfigure(1, weight=0) self.rowconfigure(0, weight=1) self.columnconfigure(1, weight=0)root = tk.Tk()app = Application(master=root, width=100, height=100)app.mainloop()