I am trying to create a responsive layout where the content of the app changes depending on the width of the window (basically like any website works). The code I have so far is this:
import tkinter as tk
from tkinter import ttk
class App(tk.Tk):
def __init__(self, start_size, min_size):
super().__init__()
self.title('Responsive layout')
self.geometry(f'{start_size[0]}x{start_size[1]}')
self.minsize(min_size[0],min_size[1])
self.frame = ttk.Frame(self)
self.frame.pack(expand = True, fill = 'both')
size_notifier = SizeNotifier(self, {300: self.create_small_layout, 600: self.create_medium_layout}, min_size[0])
self.mainloop()
def create_small_layout(self):
self.frame.pack_forget()
self.frame = ttk.Frame(self)
ttk.Label(self.frame, text = 'Label 1', background = 'red').pack(expand = True, fill = 'both')
ttk.Label(self.frame, text = 'Label 2', background = 'green').pack(expand = True, fill = 'both')
ttk.Label(self.frame, text = 'Label 3', background = 'blue').pack(expand = True, fill = 'both')
ttk.Label(self.frame, text = 'Label 4', background = 'yellow').pack(expand = True, fill = 'both')
self.frame.pack(expand = True, fill = 'both')
def create_medium_layout(self):
self.frame.pack_forget()
self.frame = ttk.Frame(self)
self.frame.columnconfigure((0,1), weight = 1, uniform = 'a')
self.frame.rowconfigure((0,1), weight = 1, uniform = 'a')
self.frame.pack(expand = True, fill = 'both')
ttk.Label(self.frame, text = 'Label 1', background = 'red').grid(column = 0, row = 0, sticky = 'nsew') # sticky = 'nsew' causes configure loop
ttk.Label(self.frame, text = 'Label 2', background = 'green').grid(column = 1, row = 0)
ttk.Label(self.frame, text = 'Label 3', background = 'blue').grid(column = 0, row = 1)
ttk.Label(self.frame, text = 'Label 4', background = 'yellow').grid(column = 1, row = 1)
class SizeNotifier:
def __init__(self, window, size_dict, min_width):
self.window = window
self.min_width = min_width
self.size_dict = {key: value for key, value in sorted(size_dict.items())}
self.current_min_size = None
self.window.bind('<Configure>', self.check)
def check(self, event):
checked_size = None
window_width = event.width
if window_width >= self.min_width:
for min_size in self.size_dict:
delta = window_width - min_size
if delta >= 0:
checked_size = min_size
if checked_size != self.current_min_size:
self.current_min_size = checked_size
print(self.current_min_size) # infinite loop visible here -> print never stops
self.size_dict[self.current_min_size]()
app = App((400,300), (300,300))
The basic idea is this: There are 2 functions that create different layouts (create_small_layout and create_medium_layout) and the app hides/reveals one of them depending on the app width. Getting the width of the application is handled by the SizeNotifier class, the logic in there was added to only trigger a layout build function when a new minimum size was reached.
This entire thing works to a degree: In the create_medium_layout function, where I use the grid layout methods, things do work without the sticky argument. However, once I stick a widget to all sides of a cell (so 'nsew') configure is thrown into an infinite loop and the app stops displaying anything. using sticky with just 3 sides or less is fine though.
Is there a way around this?