Tkinter: grid or pack inside a grid? Tkinter: grid or pack inside a grid? tkinter tkinter

Tkinter: grid or pack inside a grid?


The key to doing layout is to be methodical, and to use the right toolfor the job. It also means that sometimes you need to be creative.That means using grid when laying things out in agrid, and using pack when laying things out top-to-bottom orleft-to-right.

The other key is to group your layout code together. It's much, mucheasier to visualize and modify the layout when it's all in one blockof code.

In your case you seem to have three or four distinct areas, depending on how you count. If you want to use grid, it will be easiest tocombine "menu upper" and "menu lower" into a frame, and treat thatwhole frame as a table cell. It looks like you're already doing that,which is good.

So, let's start with those main areas:

self.menu_left.grid(row=0, column=0, rowspan=2, sticky="nsew")self.some_title_frame.grid(row=0, column=1, sticky="ew")self.canvas_area.grid(row=1, column=1, sticky="nsew") # you don't have a status bar in the example code, but if you# did, this is where you would put it# self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")

Any time you use grid, you need to give at least one row and onecolumn a positive weight so that tkinter knows where to use anyunallocated space. Usually there is one widget that is the "main"widget. In this case it's the canvas. You want to make sure that therow and column for the canvas has a weight of 1 (one):

self.root.grid_rowconfigure(1, weight=1)self.root.grid_columnconfigure(1, weight=1)

note: using pack instead of grid would save you two lines of code, since pack doesn't require you to set weights the way grid does.

Next, we need to solve the problem of the menu areas. By default, frames shrink to fit their contents, which is why adding the labelbroke your layout. You weren't telling tkinter what to do with extra space, so the frames shrunk to fit, and extra space went unused.

Since you want "menu_upper" and "menu_lower" toeach share 50% of that area, pack is the simplest solution. You canuse grid, but it requires more lines of code to add the row and column weights.

self.menu_left_upper.pack(side="top", fill="both", expand=True)self.menu_left_lower.pack(side="top", fill="both", expand=True)

Here is a functioning version, with statusbar. Notice how it behaves exactly as it should when you resize the window:

import Tkinter as tkclass Example():    def __init__(self):        self.root = tk.Tk()        self.root.title("some application")        # menu left        self.menu_left = tk.Frame(self.root, width=150, bg="#ababab")        self.menu_left_upper = tk.Frame(self.menu_left, width=150, height=150, bg="red")        self.menu_left_lower = tk.Frame(self.menu_left, width=150, bg="blue")        self.test = tk.Label(self.menu_left_upper, text="test")        self.test.pack()        self.menu_left_upper.pack(side="top", fill="both", expand=True)        self.menu_left_lower.pack(side="top", fill="both", expand=True)        # right area        self.some_title_frame = tk.Frame(self.root, bg="#dfdfdf")        self.some_title = tk.Label(self.some_title_frame, text="some title", bg="#dfdfdf")        self.some_title.pack()        self.canvas_area = tk.Canvas(self.root, width=500, height=400, background="#ffffff")        self.canvas_area.grid(row=1, column=1)        # status bar        self.status_frame = tk.Frame(self.root)        self.status = tk.Label(self.status_frame, text="this is the status bar")        self.status.pack(fill="both", expand=True)        self.menu_left.grid(row=0, column=0, rowspan=2, sticky="nsew")        self.some_title_frame.grid(row=0, column=1, sticky="ew")        self.canvas_area.grid(row=1, column=1, sticky="nsew")         self.status_frame.grid(row=2, column=0, columnspan=2, sticky="ew")        self.root.grid_rowconfigure(1, weight=1)        self.root.grid_columnconfigure(1, weight=1)        self.root.mainloop()Example()

On an unrelated note: I would strongly encourage you to not remove the ability for the user to resize the window. They know better than you what their requirements are. If you use grid and pack properly, the GUI will resize perfectly.


Adding the following code right before self.root.mainloop() achieves what you're looking for

self.some_status = tk.Label(self.root, text="status bar", bg="#dfdfdf")self.some_status.grid(row=3, column=0, columnspan=2, sticky="we")


By putting in the line:

menu_left_upper.grid_propagate(False) 

In between your menu_left_upper Frame and menu_left_upper.mainloop()

This works as:

By default, a container widget expands or collapses to be just big enough to hold its contents. Thus, when you call pack, it causes the frame to shrink. This feature is called geometry propagation.

For the vast majority of applications, this is the behavior you want. For those rare times when you want to explicitly set the size of a container you can turn this feature off. To turn it off, call either pack_propagate or grid_propagate on the container (depending on whether you're using grid or pack on that container), giving it a value of False.

See link to another question where this came from

To get your status bar just implement another frame and grid method:

status_bar_frame = Frame(root, bg="#dfdfdf")status_bar_frame.grid(row=3, column=0, columnspan=2, sticky="we")status_bar = Label(status_bar_frame, text="status bar", bg="#dfdfdf")status_bar.pack()

Then your plan works.Hope it helps :)

PS. Also why all the self attributes?

EDIT:TO work you need to do:

menu_left_upper = Frame(menu_left, width=225, height=225, bg="red")menu_left_upper.grid_propagate(False)menu_left_upper.grid(row=0, column=0)# this label breaks the design   test = Label(menu_left_upper, text="test", bg='red')test.grid()

My result