0

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!!

1
  • 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. Commented May 7, 2024 at 1:17

1 Answer 1

0

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

Sign up to request clarification or add additional context in comments.

1 Comment

Fanatics! Thank you SO MUCH Jan_B!!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.