I am using Python 3.14 on Windows 11. I have 2 frames (left and right) inside a top frame and a bottom frame as status bar. I want the left frame to be a square, giving the rest of the horizontal space to the right frame if the window grows.
Bryan Oakley's solution is not suitable because I want to pack or grid more widgets in the right frame. Gary Kerr's solution I am unable to make it work.
Total window width is 700px and the height is 200px (181px the top container and 19px the bottom container). I want the left (dark gray) container to be 181x181, and the right (green) container 700 - 181 = 519 pixels wide and 181 pixels high. These sizes are just an example after resizing the main window. After forcing relative weights with columnconfigure and expanding the window to the right, the left container keeps growing and is not a square:
import tkinter as tk
from tkinter import ttk
from tkinter import Label
class App:
def __init__(self, root):
self.root = root
self.root.geometry("400x200+900+400")
# Styles
self.style = ttk.Style(self.root)
self.style.configure('Dark.TFrame', background='#343434')
self.style.configure('LightGreen.TFrame', background='#a4edde')
# Main frames: top (for content) and bottom (status bar)
self.top_container = ttk.Frame(self.root)
self.bottom_container = ttk.Frame(self.root)
self.top_container.pack(fill='both', expand=True)
self.bottom_container.pack(fill='x', expand=False)
# Left container for future Canvas map
self.left_container = ttk.Frame(self.top_container, style='Dark.TFrame')
# Right container for data display
self.right_container = ttk.Frame(self.top_container, style='LightGreen.TFrame')
self.top_container.rowconfigure(0, weight=1)
self.top_container.columnconfigure(0, weight=1)
self.top_container.columnconfigure(1, weight=2)
self.left_container.grid(row=0, column=0, sticky='NSWE')
self.right_container.grid(row=0, column=1, sticky='NSWE')
# Labels just for displaying sizes
self.left_label = ttk.Label(self.left_container, text="Left")
self.right_label = ttk.Label(self.right_container, text="Right")
self.bottom_label = ttk.Label(self.bottom_container, text="Bottom")
self.left_label.pack()
self.right_label.pack()
self.bottom_label.pack()
self.root.bind('<Configure>', self.resize)
def resize(self, event):
# Remove the binding. It will be bound again later.
self.root.unbind('<Configure>')
w, h = event.width, event.height
w1, h1 = self.root.winfo_width(), self.root.winfo_height()
self.root.update_idletasks()
W = self.top_container.winfo_width()
H = self.top_container.winfo_height()
lw = self.left_container.winfo_width()
lh = self.left_container.winfo_height()
rw = self.right_container.winfo_width()
rh = self.right_container.winfo_height()
self.bottom_label.config(text=f'{W=} {H=}')
self.left_label.config(text=f'{lw=} {lh=}')
self.right_label.config(text=f'{rw=} {rh=}')
print(f'SIZES: {W=} {H=} {w=} {h=} {lw=} {rw=} {lh=} {rh=}')
print('LEFT WEIGHT', self.top_container.columnconfigure(0)['weight'])
print('RIGHT WEIGHT', self.top_container.columnconfigure(1)['weight'])
if W > H:
if lw != H:
# self.left_container.grid_forget()
# self.right_container.grid_forget()
self.top_container.columnconfigure(0, weight=H)
self.top_container.columnconfigure(1, weight=W-H)
# self.left_container.grid(row=0, column=0, sticky='NSWE')
# self.right_container.grid(row=0, column=1, sticky='NSWE')
else:
self.top_container.columnconfigure(0, weight=1)
self.top_container.columnconfigure(1, weight=1)
elif H > W:
# TO DO
pass
# Enable binding again
self.root.bind('<Configure>', self.resize)
if __name__ == '__main__':
root = tk.Tk()
app = App(root)
root.mainloop()
This worked for me:
# Source - https://stackoverflow.com/q/79815659
# Posted by Mike Duke, modified by community. See post 'Timeline' for change history
# Retrieved 2025-11-10, License - CC BY-SA 4.0
import tkinter as tk
from tkinter import ttk
from tkinter import Label
class App:
def __init__(self, root):
self.root = root
self.root.geometry("400x200+900+400")
# Styles
self.style = ttk.Style(self.root)
self.style.configure('Dark.TFrame', background='#343434')
self.style.configure('LightGreen.TFrame', background='#a4edde')
# Main frames: top (for content) and bottom (status bar)
self.top_container = ttk.Frame(self.root)
self.bottom_container = ttk.Frame(self.root)
self.top_container.pack(fill='both', expand=True)
self.bottom_container.pack(fill='x', expand=False)
# Left container for future Canvas map
self.left_container = ttk.Frame(self.top_container, style='Dark.TFrame')
# Right container for data display
self.right_container = ttk.Frame(self.top_container, style='LightGreen.TFrame')
self.top_container.rowconfigure(0, weight=1)
self.top_container.columnconfigure(0, weight=0)
self.top_container.columnconfigure(1, weight=1)
self.left_container.grid(row=0, column=0, sticky='NSWE')
self.right_container.grid(row=0, column=1, sticky='NSWE')
# Labels just for displaying sizes
self.left_label = ttk.Label(self.left_container, text="Left")
self.right_label = ttk.Label(self.right_container, text="Right")
self.bottom_label = ttk.Label(self.bottom_container, text="Bottom")
self.left_label.pack()
self.right_label.pack()
self.bottom_label.pack()
self.left_container.bind('<Configure>', self.resize)
def resize(self, event):
w, h = event.width, event.height
widget = event.widget
grid_info = widget.grid_info()
container = grid_info['in']
column = grid_info['column']
print(f'''resize event: {event!r} - {widget!r} - {container!r} - {column!r}''')
container.columnconfigure(column, minsize=event.height)
if __name__ == '__main__':
root = tk.Tk()
app = App(root)
root.mainloop()
The details:
weight=0); set the other column to get all the extra space (weight=1).<Configure> event to the square widget itself.minsize set to the widget's new height (from the resize event).