Python Pack() and Grid() Methods Together

python pack() and grid() methods together

You cannot use both pack and grid on widgets that have the same master. The first one will adjust the size of the widget. The other will see the change, and resize everything to fit it's own constraints. The first will see these changes and resize everything again to fit its constraints. The other will see the changes, and so on ad infinitum. They will be stuck in an eternal struggle for supremacy.

While it is technically possible if you really, really know what you're doing, for all intents and purposes you can't mix them in the same container. You can mix them all you want in your app as a whole, but for a given container (typically, a frame), you can use only one to manage the direct contents of the container.

A very common technique is to divide your GUI into pieces. In your case you have a bottom statusbar, and a top "main" area. So, pack the statusbar along the bottom and create a frame that you pack above it for the main part of the GUI. Then, everything else has the main frame as its parent, and inside that frame you can use grid or pack or whatever you want.

Combining grid/pack Tkinter

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.

A solution to using both the .pack() and .grid() method in the same tkinter window?

If I created, say, 2 frames, could some widgets be arranged in one
frame using the .grid() method and other widgets (such as the canvas)
be arranged in the other frame using the .pack() method? Is this a
solution to using two different layout management methods in the same
window?

Yes, using grid in one frame and pack in another is a very common thing to do.

Grid and Pack in Main and Class?

It is a best practice to use both grid and pack within an application.

The only caveat is that you cannot mix them within a given frame. The choice of geometry manager (grid, pack, place) for widgets in a frame is completely independent of the geometry manger used in any other frame. Each has strengths and weaknesses, and you should use them accordingly.

Also -- and I see this mistake all the time -- a class that inherits from a widget should not call grid or pack or place on itself.

For example, this is incorrect:

class Example(tk.Frame):
def __init__(self, parent):
...
self.pack(...)

class Main(tk.Frame):
def __init__(self, parent):
...
this.example = Example(self)

Instead of the above, pack should be removed from Example, and Main should be redefined to call pack (or grid or place):

class Main(tk.Frame):
def __init__(self, parent):
...
this.example = Example(self)
this.example.pack(...)

The problem with the first (incorrect) example is that if you have a dozen frames inside of main along with some other widgets, if you decide to switch from pack to grid, you'll have to modify a dozen other classes. By having a parent responsible for arranging it's own children, when you make a layout change, you only have to change a single function.

Tkinter: pack()ing frames that use grid()

The error is telling you exactly what is wrong. You can't use grid on a widget in the root window when you've already used pack to manage a widget in the root window.

You wrote:

It then initializes a frame, self.mainframe, and within that frame initializes a grid()

No, that is not what your code is doing. It is not setting up a grid within the frame, it's attempting to use grid to add the widget to the root window.

First you have this line of code which uses pack on a widget in the root window:

self.pack(fill=BOTH,expand=1)

Later, you try to use grid for another window in the root window:

self.mainframe = Frame(root)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )

The above isn't setting up a grid within self.mainframe, it's using grid to add the widget to the root window.

You need to use one or the other, you can't use both for different windows that are both direct children of the root window.

In other words, you're doing this:

self.pack(fill=BOTH,expand=1)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )

but since both self and self.mainframe are a direct child of the root window, you can't do that. You need to either use pack for both:

self.pack(fill=BOTH,expand=1)
self.mainframe.pack(...)

... or grid for both:

self.grid(...)
self.mainframe.grid(column=0,row=0, sticky=(N,W,E,S) )

Using .pack() and .grid() at the same time with tkinter

You can't mix grid and pack with widgets that share the same parent.

Why? Because grid will try to lay widgets out, possibly growing or shrinking widgets according to various options. Next, pack will try to do the same according to its rules. This may require that it change widget widths or heights.

grid will see that widgets have changed size so it will try to rearrange the widgets accirding to its rules. pack will then notice that some widgets have changed size so it will rearrange the widgets according to its rules. grid will see that widgets have changed size so it will try to rearrange the widgets according to their rules. pack will then notice that some widgets have changed size so it will rearrange the widgets according to its rules. grid will see that ...

Differences in pack() and grid() tkinter

During the process of destroy() in pack() it appears that Frames are appended to root, and in grid() it appears they are inserted.

Not exactly. In both cases they are added to the frame according to their options. In the case of pack, the default placement option (eg: side) is to place the widget at the top of the available space in the parent/master.

In the case of grid the default is to use the first unoccupied row after all other widgets in the same parent/master. If not specified, the column number will be zero.

Best practices dictate that you should always specify at least the side option with pack, and the row and column option with grid so that there is never any confusion about where the widget is intended to be placed.

I have also run into the problem of the entries being of type(') instead of tkinter.Entry

That question doesn't make any sense. The objects created by Entry will always be of type <class 'tkinter.Entry'>, and the method get will always work when used on an object of that type.

I'm having issues converting python .pack() to .grid()

You can use both pack and grid within the same tkinter program. Just as long as you don't mix methods that has the same parent widget.

You can see an example below with a program that mixes pack and grid:

from tkinter import *
import time
from tkinter import ttk

#~~~~~~~~~~~~~~Defining the window~~~~~~~~~~~~~

root=Tk()
root.title("Parent window")
root.geometry('1600x800+0+0')
root.configure(bg='#FFFFFF')
#~~~~~~~~~~~~~~window~partition~~~~~~~~~~~~~~~~

top=Frame(root, width=1600, height=100, bg='blue', relief=SUNKEN)
top.pack(side=TOP)

# Here I create a new parent frame, to contain the widgets we wish to
# use .grid() for instead of .pack()
gridframe = Frame(root, width=1600, height=700)
gridframe.pack(side=TOP)

w1=Frame(gridframe, width=800, height=700, bg='purple', relief=SUNKEN)
w1.grid(row=0, column=0)

w2=Frame(gridframe, width=300, height=700, bg='green', relief=SUNKEN)
w2.grid(row=0, column=1)

w3=Frame(gridframe, width=35, height=700, bg='orange', relief=SUNKEN)
w3.grid(row=0, column=2)

w4=Frame(gridframe, width=100, height=700, bg='pink', relief=SUNKEN)
w4.grid(row=0, column=3)

root.mainloop()

To replicate the result you got with the pack function, please try something like the following. I am using padx to add the whitespace between the widgets so it matches what you saw when packing

w1=Frame(gridframe, width=800, height=700, bg='purple', relief=SUNKEN)
w1.grid(row=0, column=0)

w2=Frame(gridframe, width=35, height=700, bg='orange', relief=SUNKEN)
w2.grid(row=0, column=1)

w3=Frame(gridframe, width=100, height=700, bg='pink', relief=SUNKEN)
w3.grid(row=0, column=2)

w4=Frame(gridframe, width=300, height=700, bg='green', relief=SUNKEN)
w4.grid(row=0, column=3, padx=1600-800-35-100-300)

In what cases Tkinter's grid() cannot be mixed with pack()?

In what cases Tkinter's grid() cannot be mixed with pack()?

In all cases, you cannot use both grid and pack for widgets that have a common master. Within a master, all direct children must use the same geometry manager. Within an application as a whole, you can mix pack and grid all you want as long as you follow that one rule that you can't use them both for widgets that have the same parent.

  1. I must have a Frame to fill the root window, otherwise there'd be no hope to fill the extra space in the window, however I tweak widgets alone.

This is not correct. You can easily fill all of the space in the root window without using a frame.


  1. For all the child widgets sitting inside a common parent Frame, I must use either pack() or grid() but not both.

That is correct. The third option is to use place, though it's rarely the best choice.


  1. When using grid() in a Frame, it's mandatory to specify Frame.grid_rowconfigure() and .grid_columnconfigure() with non-zero weight arguments. Otherwise, nothing would show up.

That is not true -- configuring rows and columns to have a non-zero weight isn't mandatory. It's usually a best practice, but it's not required in order for widgets to show up. The weight only applies to how grid manages extra space. Any widgets with a non-zero size should appear whether you use weights or not.


  1. It's thus possible to have the main Frame using pack(), but its immediate child Frames all using grid()

Correct.


  1. By a careful weight design, I could fill a horizontal space in a parent Frame with a child Frame full of widgets laid out horizontally, e.g., all widgets use grid(sticky='nsew'), and the child Frame uses pack(side='top', fill='both', expand=True).

That is correct.



Related Topics



Leave a reply



Submit