I am writing a small example with tkinter. Here what I got (things are layouted nicely) enter image description here
The codes for it are given below
import tkinter
from tkinter import ttk
from tkinter import messagebox
window = tkinter.Tk()
window.title("Example")
frame = tkinter.Frame(window)
frame.pack()
# Saving User Info
user_info_frame = tkinter.LabelFrame(frame, text="User Information")
user_info_frame.grid(row=0, column=0, padx=20, pady=10)
first_name_label = tkinter.Label(user_info_frame, text="First Name")
first_name_label.grid(row=0, column=0)
last_name_label = tkinter.Label(user_info_frame, text="Last Name")
last_name_label.grid(row=0, column=1)
first_name_entry = tkinter.Entry(user_info_frame)
last_name_entry = tkinter.Entry(user_info_frame)
first_name_entry.grid(row=1, column=0)
last_name_entry.grid(row=1, column=1)
title_label = tkinter.Label(user_info_frame, text="Title")
title_combobox = ttk.Combobox(user_info_frame, values=["", "Mr.", "Ms.", "Dr."])
title_label.grid(row=0, column=2)
title_combobox.grid(row=1, column=2)
age_label = tkinter.Label(user_info_frame, text="Age")
age_spinbox = tkinter.Spinbox(user_info_frame, from_=18, to=110)
age_label.grid(row=2, column=0)
age_spinbox.grid(row=3, column=0)
nationality_label = tkinter.Label(user_info_frame, text="Nationality")
nationality_combobox = ttk.Combobox(user_info_frame,
values=["Africa", "Antarctica", "Asia", "Europe", "North America", "Oceania",
"South America"])
nationality_label.grid(row=2, column=1)
nationality_combobox.grid(row=3, column=1)
for widget in user_info_frame.winfo_children():
widget.grid_configure(padx=10, pady=5)
# Saving Course Info
courses_frame = tkinter.LabelFrame(frame)
courses_frame.grid(row=1, column=0, sticky="news", padx=20, pady=10)
registered_label = tkinter.Label(courses_frame, text="Registration Status")
reg_status_var = tkinter.StringVar(value="Not Registered")
registered_check = tkinter.Checkbutton(courses_frame, text="Currently Registered",
variable=reg_status_var, onvalue="Registered", offvalue="Not registered")
registered_label.grid(row=0, column=0)
registered_check.grid(row=1, column=0)
numcourses_label = tkinter.Label(courses_frame, text="# Completed Courses")
numcourses_spinbox = tkinter.Spinbox(courses_frame, from_=0, to='infinity')
numcourses_label.grid(row=0, column=1)
numcourses_spinbox.grid(row=1, column=1)
numsemesters_label = tkinter.Label(courses_frame, text="# Semesters")
numsemesters_spinbox = tkinter.Spinbox(courses_frame, from_=0, to="infinity")
numsemesters_label.grid(row=0, column=2)
numsemesters_spinbox.grid(row=1, column=2)
for widget in courses_frame.winfo_children():
widget.grid_configure(padx=10, pady=5)
window.mainloop()
What I wanted to do next is : 1) add a scroll bar to it; 2) make it sizable with max/min window. However, it destroyed the (evenly)layout of the app. Here what I got enter image description here
it is not sizable with max/min window. Please see the picture below: enter image description here
The codes are below:
import tkinter
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
window = tkinter.Tk()
window.title("Example")
frame = tkinter.Frame(window)
frame.pack()
# root = tk.Tk()
# essential to enable full window resizing
window.rowconfigure(0, weight=1)
window.columnconfigure(0, weight=1)
# scrollregion is also essential when using scrollbars
canvas = tk.Canvas(
frame, scrollregion="0 0 10000 10000", width=600, height=400)
canvas.grid(row=0, column=0, sticky=tk.NSEW)
scroll = tk.Scrollbar(frame, orient=tk.VERTICAL, command=canvas.yview, cursor="arrow")
scroll.grid(row=0, column=1, sticky=tk.NS)
canvas.config(yscrollcommand=scroll.set)
###############################
# Saving User Info
user_info_frame = tkinter.LabelFrame(frame, text="User Information")
user_info_frame.grid(row=0, column=0, padx=20, pady=10)
first_name_label = tkinter.Label(user_info_frame, text="First Name")
first_name_label.grid(row=0, column=0)
last_name_label = tkinter.Label(user_info_frame, text="Last Name")
last_name_label.grid(row=0, column=1)
first_name_entry = tkinter.Entry(user_info_frame)
last_name_entry = tkinter.Entry(user_info_frame)
first_name_entry.grid(row=1, column=0)
last_name_entry.grid(row=1, column=1)
title_label = tkinter.Label(user_info_frame, text="Title")
title_combobox = ttk.Combobox(user_info_frame, values=["", "Mr.", "Ms.", "Dr."])
title_label.grid(row=0, column=2)
title_combobox.grid(row=1, column=2)
age_label = tkinter.Label(user_info_frame, text="Age")
age_spinbox = tkinter.Spinbox(user_info_frame, from_=18, to=110)
age_label.grid(row=2, column=0)
age_spinbox.grid(row=3, column=0)
nationality_label = tkinter.Label(user_info_frame, text="Nationality")
nationality_combobox = ttk.Combobox(user_info_frame,
values=["Africa", "Antarctica", "Asia", "Europe", "North America", "Oceania",
"South America"])
nationality_label.grid(row=2, column=1)
nationality_combobox.grid(row=3, column=1)
for widget in user_info_frame.winfo_children():
widget.grid_configure(padx=10, pady=5)
# Saving Course Info
courses_frame = tkinter.LabelFrame(frame)
courses_frame.grid(row=1, column=0, sticky="news", padx=20, pady=10)
registered_label = tkinter.Label(courses_frame, text="Registration Status")
reg_status_var = tkinter.StringVar(value="Not Registered")
registered_check = tkinter.Checkbutton(courses_frame, text="Currently Registered",
variable=reg_status_var, onvalue="Registered", offvalue="Not registered")
registered_label.grid(row=0, column=0)
registered_check.grid(row=1, column=0)
numcourses_label = tkinter.Label(courses_frame, text="# Completed Courses")
numcourses_spinbox = tkinter.Spinbox(courses_frame, from_=0, to='infinity')
numcourses_label.grid(row=0, column=1)
numcourses_spinbox.grid(row=1, column=1)
numsemesters_label = tkinter.Label(courses_frame, text="# Semesters")
numsemesters_spinbox = tkinter.Spinbox(courses_frame, from_=0, to="infinity")
numsemesters_label.grid(row=0, column=2)
numsemesters_spinbox.grid(row=1, column=2)
for widget in courses_frame.winfo_children():
widget.grid_configure(padx=10, pady=5)
window.columnconfigure(0, weight=1)
window.rowconfigure(1, weight=1)
frame.rowconfigure(0, weight=1)
frame.columnconfigure(0, weight=1)
user_info_frame.rowconfigure(0, weight=1)
user_info_frame.columnconfigure(0, weight=1)
courses_frame.rowconfigure(0, weight=1)
courses_frame.columnconfigure(0, weight=1)
item = canvas.create_window((0, 0), anchor=tk.NW, window=user_info_frame)
item1 = canvas.create_window((0, 150), anchor=tk.NW, window=courses_frame)
window.mainloop()
I would truly appreciate if someone could help me to fix this or giving me a pointer on how to fix this. THANK YOU SO MUCH!!
As stated in the comments you have to specify the width for the canvas object, using grid beforehand will not result in the desired outcome.
Note that user_info_frame.grid(...) and courses_frame.grid(...) are useless because they are override by later canvas.create_window(...). You need to specify the width option in canvas.create_window(...) in order to make the two label frames having same width. – acw1668
I adapted your code to an object oriented approach and adapted the widget sizing in such a way that both the width of the frames as well as the widgets inside adapt to window resizing:
import tkinter as tk # stick to one logic either using tk or tkinter!
from tkinter import ttk
class GUI(tk.Tk):
def __init__(self):
super().__init__() # this basically replaces window as self
self.title("Example")
self.minsize(width=600, height=400) # do not allow resizing smaller than your initial canvas size
max_height = 1000 # use this to determine length of scroll heigth and if desired max window heigth
#self.maxsize(width=650, height=max_height) # → specify if needed
main_frame = tk.Frame(self) # name it more descriptive
main_frame.pack(expand=True, fill="both") # let the canvas parent frame stretch out when resizing
# let the canvas expand to fill main_frame both in width and height
main_frame.rowconfigure(0, weight=1)
main_frame.columnconfigure(0, weight=1)
# scrollregion is also essential when using scrollbars → but you only need y in your case!
self.canvas = tk.Canvas(main_frame, scrollregion=f"0 0 0 {max_height}", width=600, height=400)
self.canvas.grid(row=0, column=0, sticky=tk.NSEW)
self.scroll = tk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.canvas.yview, cursor="arrow")
self.scroll.grid(row=0, column=1, sticky=tk.NS)
self.canvas.config(yscrollcommand=self.scroll.set)
###############################
# Saving User Info
user_info_frame = tk.LabelFrame(main_frame, text="User Information") # unnecessary to manage with .grid
first_name_label = tk.Label(user_info_frame, text="First Name")
first_name_label.grid(row=0, column=0)
last_name_label = tk.Label(user_info_frame, text="Last Name")
last_name_label.grid(row=0, column=1)
first_name_entry = tk.Entry(user_info_frame)
last_name_entry = tk.Entry(user_info_frame)
first_name_entry.grid(row=1, column=0)
last_name_entry.grid(row=1, column=1)
title_label = tk.Label(user_info_frame, text="Title")
title_combobox = ttk.Combobox(user_info_frame, values=["", "Mr.", "Ms.", "Dr."])
title_label.grid(row=0, column=2)
title_combobox.grid(row=1, column=2)
age_label = tk.Label(user_info_frame, text="Age")
age_spinbox = tk.Spinbox(user_info_frame, from_=18, to=110)
age_label.grid(row=2, column=0)
age_spinbox.grid(row=3, column=0)
nationality_label = tk.Label(user_info_frame, text="Nationality")
nationality_combobox = ttk.Combobox(user_info_frame,
values=["Africa", "Antarctica", "Asia", "Europe", "North America",
"Oceania",
"South America"])
nationality_label.grid(row=2, column=1)
nationality_combobox.grid(row=3, column=1)
for widget in user_info_frame.winfo_children():
widget.grid_configure(padx=10, pady=5, sticky="nsew") # add sticky to stretch widget to fill grid cell
# Saving Course Info
courses_frame = tk.LabelFrame(main_frame) # unnecessary to manage with .grid
registered_label = tk.Label(courses_frame, text="Registration Status")
reg_status_var = tk.StringVar(value="Not Registered")
registered_check = tk.Checkbutton(courses_frame, text="Currently Registered",
variable=reg_status_var, onvalue="Registered", offvalue="Not registered")
registered_label.grid(row=0, column=0)
registered_check.grid(row=1, column=0)
numcourses_label = tk.Label(courses_frame, text="# Completed Courses")
numcourses_spinbox = tk.Spinbox(courses_frame, from_=0, to='infinity')
numcourses_label.grid(row=0, column=1)
numcourses_spinbox.grid(row=1, column=1)
numsemesters_label = tk.Label(courses_frame, text="# Semesters")
numsemesters_spinbox = tk.Spinbox(courses_frame, from_=0, to="infinity")
numsemesters_label.grid(row=0, column=2)
numsemesters_spinbox.grid(row=1, column=2)
for widget in courses_frame.winfo_children():
widget.grid_configure(padx=10, pady=5, sticky="nsew")
# rowconfigure seems to make no sense except you want to stretch in height as well (but why then a scrollbar?)
# but giving all cols the same weight will let them stretch evenly
user_info_frame.columnconfigure(0, weight=1)
user_info_frame.columnconfigure(1, weight=1)
user_info_frame.columnconfigure(2, weight=1)
courses_frame.columnconfigure(0, weight=1)
courses_frame.columnconfigure(1, weight=1)
courses_frame.columnconfigure(2, weight=1)
# add the pady and padx in the position tuple & specify width for the frames by callback
self.padx = 20
self.canvas.create_window((self.padx, 10), anchor=tk.NW, window=user_info_frame, tags='frame')
self.canvas.create_window((self.padx, 160), anchor=tk.NW, window=courses_frame, tags='frame')
self.canvas.bind("<Configure>", self.onCanvasConfigure)
def onCanvasConfigure(self, e):
"""stretch canvas frames to match canvas width on resize (respecting the scrollbar and the padding)"""
self.canvas.itemconfig('frame', width=self.canvas.winfo_width() - self.padx - self.scroll.winfo_width())
if __name__ == '__main__':
app = GUI()
app.mainloop()