Resizing Tkinter Frames with fixed aspect-ratio Resizing Tkinter Frames with fixed aspect-ratio tkinter tkinter

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()