Python Series Part 16: Understanding Tkinter’s Grid System

Jarret B

Well-Known Member
Staff member
Joined
May 22, 2017
Messages
454
Reaction score
519
Credits
19,511
In this article, we’ll cover the final method for placing objects in a window - the ‘grid’ method. The grid method works like a spreadsheet, using rows and columns to position widgets. Each “cell” can hold an object, and cells can be expanded to span multiple rows or columns.

The method is like a spreadsheet. There are vertical and horizontal ‘cells’ which can contain an object. You can expand the ‘cells’ to contain multiple rows and/or columns.

The width and height of a cell will be based on the largest cell in that row or column. For example, if the largest ‘cell’ in a column is eleven characters wide, then all ‘cells’ will have a width of eleven characters. The rows will have a height of the largest ‘cell’ in the row.

The code is the same as using ‘place’ or ‘pack’, but with the keyword of ‘grid’:

Code:
root.grid()

Cell Index

By default, each row and column are numbered. Keep in mind that the top left cell has an index of ‘(0,0)’. The convention of the numbering is ‘(column, row)’. The numbering is by default consecutive, such as 0, 1, 2, 3, etc.

You can change the index numbering, but even then it is not perfect.

So, to configure the columns and rows, we use the code:

Code:
root.grid_rowconfigure(3)
root.grid_rowconfigure(5)
root.grid_rowconfigure(7)

Here, I am renumbering the first 3 rows to be indexed as 3, 5 and 7. The complete code is:

Code:
from tkinter import *

root = Tk()
root.title('Grid Method')
root.geometry("400x500")

root.grid_rowconfigure(3)
root.grid_rowconfigure(5)
root.grid_rowconfigure(7)
l1 = Label(text="Label1", bg='blue').grid(row=3, column=0)
l2 = Label(text="Label2", bg='red').grid(row=5, column=1)
l3 = Label(text="Label3", bg='green').grid(row=7, column=2)

root.mainloop()

The output is to be as expected, with the rows renumbered, as shown in Figure 1.

Figure 1.JPG

FIGURE 1

You can see that I placed the three labels on the rows 3, 5 and 7, changing them from the default. We did not change the columns, but could be by using ‘grid_columnconfigure’ instead of ‘grid_rowconfigure’.

We can set up a weight to signify size on these ‘cells’, but these are not to be taken as a literal number of ‘cells’ to be used. The amount given is relative to the screen size. Let me give an example:

Code:
root.grid_rowconfigure(3, weight=1)
root.grid_rowconfigure(5, weight=2)
root.grid_rowconfigure(7, weight=1)

Looking at this, you may think that row 3, the first row, has a ‘cell’ width of 1, as does row 7, while row 5 takes up two ‘cells’. This is not the case. Any rows or columns not specified are set as 0. So, all the rows take up four units. Row 3 then is 1 unit, so it takes up ¼ of the screen, the same is true of Row 7. Row 5 takes up ½ of the screen.

Any time you use the ‘weight’ parameter, it is a relative size to the screen.

Now, I mentioned that this method is not perfect. We configured Rows 3, 5 and 7. But what about Row 4? It is not configured, but still exists. So, where is it? Is it located after Row 3 or the row after Row 7, which really is the third row?

If we add in the code:

Code:
l4 = Label(text="Label4", bg='yellow').grid(row=4, column=3)

The label appears in the physical second row after Row 3. Since we do not configure Row 8 and if we use any row number after 7; it appears in the row after Row 7, the third row.

It may be best to not renumber any Rows or Columns, since this can cause strange results.

Creating a Grid from Scratch

If you should start a new grid in a new program and place objects on it, but not in consecutive rows and columns, you’ll get strange results. Remember that any unused and unconfigured ‘cells’ have a weight of zero.

To set up a grid to the dimensions you want, you can use the following code that sets up a grid with eight rows and four columns. You can change it as you need:

Code:
for row in range(0,7):
root.grid_rowconfigure(row, weight=1)
for col in range(0,3):
     root.grid_columnconfigure(col, weight=1)

There are eight rows indexed 0 to 7 and four columns indexed 0 to 3. We can set the weight of each row and column to ‘1’, meaning it only takes one cell space.

Now we have a new grid that is populated with empty cells and all are of default size.

Expanding a Cell

So, we know if a cell has a weight of ‘1’, it is a single sized cell. But what if we need to expand a single cell, or multiple cells, to be larger?

Let’s look at this a bit. Looking at Figure 2, we can see that there are two cells we want bigger. On cell is at (1,1) and needs to contain two columns, cell (1,1) and (1,2). There is also a cell at (2,4) that needs to contain two rows, cells (2,4) and (2,5).

Figure 2.JPG

FIGURE 2

The code is:

Code:
from tkinter import *
root = Tk()
root.title('Grid Method')
root.geometry("200x300")

for row in range(0,7):
     root.grid_rowconfigure(row, weight=1)

for col in range(0,3):
     root.grid_columnconfigure(col, weight=1)

l1 = Label(text="Label1", bg='blue').grid(column=1, row=1, columnspan=2)
l2 = Label(text="Label2", bg='blue').grid(column=2, row=4, rowspan=2)
root.mainloop()

This works for what we need it to do. The parameter ‘columnspan’ lets you expand a column as many cells as you need. The same is true with the parameter ‘rowspan’. Except for rows.

With the code, you cannot see the results. If you add:

Code:
l3 = Label(text="Label3",bg='green').grid(column=2, row=4)
l4 = Label(text="Label4", bg='purple').grid(column=1, row=1)

Here, we add another label to the cell that was expanded. In Figure 3, you can see that we move the original labels placed in the cell to the expanded portion and the new labels are in the original place.

Code:
l3 = Label(text="Label3",bg='green').grid(column=2, row=4)
l4 = Label(text="Label4", bg='purple').grid(column=1, row=1)

Figure 3.JPG

FIGURE 3

Keep in mind that expanded cells can contain multiple objects.

Padding Around an Object

You can see we place the objects right next to each other. You can add padding around the object so the objects do not ‘touch’.

The parameters are ‘padx’, horizontal padding, and ‘pady’, vertical padding. The value that is given by the parameter is in pixels. Let’s look at an example:

Code:
from tkinter import *
root = Tk()
root.title('Grid Pad Method')
root.geometry("300x400")

for row in range(0,7):
     root.grid_rowconfigure(row, weight=1)

for col in range(0,3):
     root.grid_columnconfigure(col, weight=1)

l1 = Label(text="Label1", bg='blue').grid(column=1,row=1)
l2 = Label(text="Label2", bg='red').grid(column=2,row=4)
l3 = Label(text="Label3",bg='green').grid(column=3,row=4)
l4 = Label(text="Label4", bg='purple').grid(column=1,row=2)
root.mainloop()

The output is shown in Figure 4.

Figure 4.JPG

FIGURE 4

Now, if on each label, if I add ‘,padx=10, pady=10’ this will add padding around the labels. Ultimately, this spreads the labels apart by 20 pixels, 10 pixels for each label. The result is in Figure 5.

Figure 5.JPG

FIGURE 5

Twenty pixels are not a lot, but it is noticeable.

Padding in an Object

Using ‘padx’ and ‘pady’ will place the padding around the object, but sometimes you may want the padding on an object.

If have the padding inside the object, you need to use ‘ipadx’ and ‘ipady’. Let’s look at an example:

Code:
from tkinter import *
root = Tk()
root.title('Grid IPad Method')
root.geometry("300x400")

for row in range(0,7):
     root.grid_rowconfigure(row, weight=1)

for col in range(0,3):
     root.grid_columnconfigure(col, weight=1)

l1 = Label(text="Label1", bg='blue').grid(column=1,row=1)
l2 = Label(text="Label2", bg='red').grid(column=2,row=4)
l3 = Label(text="Label3",bg='green').grid(column=3,row=4,ipadx=10,ipady=10)
l4 = Label(text="Label4", bg='purple').grid(column=1,row=2,ipadx=10,ipady=10)
root.mainloop()

Here, we are setting up 2 labels that are normal, ‘label1’ and ‘label2’. We can use ‘ipadx’ and ‘ipady’ on ‘label3’ and ‘label4’. You can see the output in Figure 6.

Figure 6.JPG

FIGURE 6

Sticky


We have seen this in ‘pack’ and ‘place’ as ‘anchor’. The placement using ‘grid’ is more detailed than in ‘pack’ and ‘place’. Let’s look at an output, shown in Figure 7.

Figure 7.JPG

FIGURE 7

You may have noticed in some of the previous figures that an object does not fill a complete cell. We can move an object around a ‘cell’ by using the compass directions:
  • n - North (top center)
  • s - South (bottom center)
  • e - East (right center)
  • w - West (left center)
  • ne - North East (top right corner)
  • nw - North West (top left corner)
  • se - South East (bottom right corner)
  • sw - South West (bottom left corner)
You are also allowed to stretch the object in multiple compass directions than those given. An example is ‘nwsw’. If you use any directions other than those listed above, you need to put the directions in single- or double-quotes.

Using ‘nwsw’ will cause the object to stretch from the top left to the bottom left of the cell.

Let’s look at Figure 7 a little closer. I list each compass direction. For comparison of the cells on the same lines, there are the labels listed as ‘0’, ‘1’ and ‘2’. These numbers also show which row they are on. The three ‘N’ directions are also in row 1. For ‘east’ and ‘west’, the labels are in row 1. The three ‘S’ labels are in row 2.

The label that has the text ‘NSWE’ is in row 3 and has the sticky parameter set to ‘NSWE’ so it stretches to the whole cell.

The code to create this is:

Code:
from tkinter import *
root = Tk()
root.title('Grid Sticky')
root.geometry("400x500")

for row in range(0,7):
     root.grid_rowconfigure(row, weight=1)

for col in range(0,3):
     root.grid_columnconfigure(col, weight=1)

l1 = Label(text="NW",bg='blue').grid(row=0,column=0,sticky=NW)
l2 = Label(text="North",bg='blue').grid(row=0, column=3,sticky=N)
l3 = Label(text="NE",bg='blue').grid(row=0, column=4,sticky=NE)
l4 = Label(text="West",bg='blue').grid(row=1, column=0,sticky=W)
l6 = Label(text="East",bg='blue').grid(row=1, column=4,sticky=E)
l7 = Label(text="SW",bg='blue').grid(row=2, column=0,sticky=SW)
l8 = Label(text="South",bg='blue').grid(row=2, column=3,sticky=S)
l9 = Label(text="SE",bg='blue').grid(row=2, column=4,sticky=SE)
l10 = Label(text="0",bg='blue').grid(row=0,column=2)
l11 = Label(text="1",bg='blue').grid(row=1,column=2)
l12 = Label(text="2",bg='blue').grid(row=2,column=2)
l13 = Label(text="NSWE",bg='green').grid(row=3,column=2,sticky='NSEW')
root.mainloop()

Conclusion

There is a lot to cover here, but most people may use this method. Some may use the ‘place’ method as well.

Get to know this method, unless you prefer ‘place’. Either way, most people prefer one method, or even a combination of methods.

You just need to understand a method for setting up windows the way you want them to look.
 
Last edited by a moderator:


Follow Linux.org

Members online

No members online now.

Top