Combining grid/pack Tkinter

user2063 picture user2063 · Jun 29, 2012 · Viewed 16.2k times · Source

I know there have been many questions on grid and pack in the past but I just don't understand how to combine the two as I'm having difficulties expanding my 'table' in both directions (row/column).

Buttons I wish to keep the same size but always stay at the bottom of the window. The 'table' however I wish to expand automatically with resizing the window but can't seem to make it work. Changing 'win1' to pack worked in the sense that it stays central but that's it.

How can I achieve the same effects such as sticky etc with pack as I know I'll need to change the terminology.

Code is as follows (showing basic frames and several widgets, not complete code):

root = Tk()  

win1 = Frame(root)
win1.pack()
win1.grid_columnconfigure(0, weight=1)
win1.grid_rowconfigure(1, weight=1)

frame_table = ttk.Frame(win1, style="Black.TLabel", relief='sunken', borderwidth=1)
frame_table.pack(row=2, column=0, padx=1, pady=1, sticky= "nsew")
frame_table.grid_columnconfigure(0, weight=1)
frame_table.grid_rowconfigure(1, weight=1)
text_table1 = Label(frame_table, text='Number1', bg='white', borderwidth=0)
text_table1.grid(row=1, column=0, sticky="nsew", padx=1, pady=1)
empty1 = Label(frame_table, bg='white', borderwidth=0)
empty1.grid(row=2, column=0, sticky="nsew", padx=1, pady=1)
text_table2 = Label(frame_table, text='Number2', bg='white', borderwidth=0, width=12)
text_table2.grid(row=1, column=1, sticky="nsew", padx=1, pady=1)
empty2 = Label(frame_table, bg='white', borderwidth=0)
empty2.grid(row=2, column=1, sticky="nsew", padx=1, pady=1)

frame_but = ttk.Frame(win1)
frame_but.grid(sticky=S, padx=1, pady=1)
frame_but.grid_columnconfigure(0, weight=1)
frame_but.grid_rowconfigure(1, weight=1)
but1 = ttk.Button(frame_but, text='Start', command=Start)
but1.grid(row=3, column=0, padx=2, pady=1, sticky="S")

Answer

Bryan Oakley picture Bryan Oakley · Jun 29, 2012

Your first problem is that the main frame, win1 is packed with no options. The default is for it to not fill the part of its container that it is in. Thus, no matter what you do to the inner widgets, the whole thing will stack anchored to the top portion of the window. The first thing you should do, then, is tell win1 to fill the whole window (assuming that's actually what you want it to do):

win1.pack(side="top", fill="both", expand=True)

That will cause this frame to properly expand and shrink when you resize the window.

The second problem is that you're giving row 0 in win a weight of 1, but you are putting frame_table in row 3 which has a default weight of 0. I don't know if that's intentional or not, but that is what keeps the labels and entry widgets stuck to the bottom of the screen, because the empty row 0 of win1 is expanding and shrinking to take up the extra space.

How to learn to lay out your widgets

Proper resize behavior is pretty easy to get right, but it's fairly hard to learn how to get it right. My recommendation is, get some paper and a pencil. Draw out the main regions of your application -- the areas that each have different properties. For example, a row along the bottom that should stay at the bottom (status bar, or row of buttons perhaps). Maybe something at the top (toolbar, for example) that should stay at the top, etc. Typically there will be only one region that is expandable, though that expandable region may itself be divided into two or more regions.

In this case I'm guessing you have two regions: a table, and a row of buttons. Drawing this out is easy. Next, create a frame for each region, and only a frame for each region. Give them separate background colors, and place them in the window using grid or pack, whichever one gives you the resize behavior you want. pack is great if you have a simple layout (every region is sticked either top-to-bottom or left-to-right), grid is great if you truly have a grid. Work with just this, tweaking options until you get the behavior you want for the main regions. The different colors will help you see which areas are resizing and which are not.

Once you have the main regions working exactly right, you can then start to focus on the inner portions. Get out that pencil and paper again, and do the same with each of these sub-regions. Draw out the inner regions, and figure out which ones will grow within their container and which ones will not. Maybe there's only one main sub-region so you can skip this part. Finally, create frames if you have sub-regions, again giving them different colors so you can see what is resizing. Tweak the settings until everything resizes just the way you want. Lather, rinse, repeat.

Finally, you will not be able to sub-divide your window any more. Usually there are only a couple of regions so this process is quick. Once you have the different regions of your program all resizing how you want, it's time to add the actual widgets. Once you've done that you can go back and remove the color from the frames.

It's simple, but it requires a methodical approach. Just throwing a bunch of widgets into a frame and trying random things to get it to work is not the right approach. Be methodical, lay out your design on paper, transfer to frames with distinct colors, and then add your real widgets and add the final polish.