0
votes

I need my interface to look like this:

Desired outcome

Each color is a seperate frame, and within each frame I defined some labels.

from tkinter import *

root = Tk()
root.title("Library program")
root.geometry('{}x{}'.format(900, 450))
root.resizable(width=False, height=False)

#define main containers
topFrame = Frame(root, width=450, height=50, pady=3)
bottomFrame = Frame(root, width=450, height=50)

#main container layouts
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)

topFrame.grid(row=0, sticky="nsew")
bottomFrame.grid(row=1, sticky="nsew")

#define additional containers
bottomFrame.grid_rowconfigure(0, weight=1)
bottomFrame.grid_columnconfigure(1, weight=1)

bottomLeft = Frame(bottomFrame, width=225, bg="red")
bottomMidLeft = Frame(bottomFrame, width=225, bg="blue")
bottomMidRight = Frame(bottomFrame, width=225, bg="yellow")
bottomRight = Frame(bottomFrame, width=225, bg="green")

bottomLeft.pack_propagate(0)
bottomMidLeft.pack_propagate(0)
bottomMidRight.pack_propagate(0)
bottomRight.pack_propagate(0)

bottomLeft.grid(row=0, column=0, sticky = "nsew")
bottomMidLeft.grid(row=0, column=1, sticky = "nsew")
bottomMidRight.grid(row=0, column=2, sticky = "nsew")
bottomRight.grid(row=0, column=3, sticky = "nsew")

#top container widgets
titleLabel = Label(topFrame, text="Jakub's Library Program", font="Calibri 20")
titleLabel.pack()

#bottom container widgets
leftLabel = Label(bottomLeft, text="Book Search", font="Calibri")
midLeftLabel = Label(bottomMidLeft, text="Book Checkout", font="Calibri")
midRightLabel = Label(bottomMidRight, text="Return Books", font="Calibri")
rightLabel = Label(bottomRight, text="Popular Books", font="Calibri")

leftLabel.grid(row=0, column=0)
midLeftLabel.pack()
midRightLabel.pack()
rightLabel.pack()

bookTitleLabel = Label(bottomLeft, text="Book title", font="Calibri 12")
bookTitleEntry = Entry(bottomLeft)

bookTitleLabel.grid(row=1, column=0)
bookTitleEntry.grid(row=1, column=1)

root.mainloop()

But since I will need labels and entries to be placed like in the red frame, I use grid() instead of pack() to place the labels in each frame.

leftLabel.grid(row=0, column=0)
midLeftLabel.grid(row=0, column=0)
midRightLabel.grid(row=0, column=0)
rightLabel.grid(row=0, column=0)

Now my interface looks like this: Not good

It has no issue when the bottom left label is displayed using grid(), but as soon as the remaining labels are displayed using grid it throws off the size of the frames for some reason.

1
Have you done any research on this? There are lots of questions on this site related to grid. Most of them are related to not configuring weights for rows and/or columns properly. - Bryan Oakley
@BryanOakley I have, but I don't even understand what the hell the program has changed to be able to change the size of a frame based on the grid of a label. Especially since I used grid_propagate(0). - user6439666
You don't call grid_propagate(0) anywhere in the code you posted. - Bryan Oakley
@BryanOakley I meant pack_propagate(0) my apologies. - user6439666
That's rarely a good idea (using pack_propagate). What is your ultimate goal? Is your goal to have four equally sized columns? They aren't 100% equal in the first screenshot, so it's not entirely clear what you're trying to do. - Bryan Oakley

1 Answers

0
votes

If your goal is to create four equal-sized columns, grid makes that very easy. You need to make sure each column has the same non-zero weight, and you need to give every column the same value for the uniform option.

You should also remove the calls to pack_propagate. Turning off geometry propagation is rarely the right thing to do. It's an OK solution if you want to be required to do a bunch of math to make sure everything fits, but it's easier to let tkinter do all of the work to make everything fit.

Remove this block:

bottomLeft.pack_propagate(0)
bottomMidLeft.pack_propagate(0)
bottomMidRight.pack_propagate(0)
bottomRight.pack_propagate(0)

Add this line of code:

bottomFrame.grid_columnconfigure((0,1,2,3), uniform="equal", weight=1)

Note: this will cause your entry widget to be clipped. That is because you're a) forcing the window to be a specific size, b) diving that size into four equal columns, c) using a default size for the entry widget, which is too big to fit given your other design choices.

A better approach is to not force the window to a specific size. Instead, let the contents determine the size of the window. You can focus on the best size for the interior widgets, and let tkinter do all the work of deciding how big the window needs to be.

In either case (forced window size or not), there's no need to give the frames a width since grid will resize them to fit.

I also recommend removing root.resizable(width=False, height=False). It's a very user-unfriendly thing to remove the ability for the user to adjust the window size.