[index] [home]

Tweet

Tk's "grid" geometry for the impatient

This page walks through a basic layout using the Tk "grid" geometry-manager. For more information, see grid's manpage or better, a tutorial at TkDocs.com.



Introduction

In a nutshell, "grid" (or any other geometry manager) handles visual layout of Tk-widgets. It does this based on a a grid (duh), much like HTML's "table" mechanism.

This page is basically a reminder for myself - I don't do a lot of GUI-stuff, but when I do, the overhead of finding out how it worked again tends to be quite high.

To run each of these examples, save them to a file, say "example.tcl", then run

    wish example.tcl

For each example, lines of interest are indicated with "-->" in the left-hand margin.

Each example-code is followed by a screenshot of what it would actually look like when being run. I used Tcl/Tk 8.6.5 throughout this text, on a Linux system, using Herbstluftwm as window-manager.

Cell- and widget-size

This section covers a single widget positioned in a cell, and shows the effects of "-weight" and "-sticky" to expand a cell's size and expand a widget, respectively.

A simple label-widget

A very basic example shows a single label with a red background. (A label is a container for static text or an image.)

        ttk::label .a -background red -text "My Label"

        grid .a

As can be seen, the widget is first created, and then "gridded" (added to the "grid" geometry-manager).

Expanding a widget's cell

By default, a widget is centered in its surrounding cell.

So.. why is the label still displayed at the top left..?

Because in this case, the surrounding cell is only as big as the label itself.

We can specify that the surrounding cell takes up all of the remaining space in its parent grid, by using "columnconfigure" or "rowconfigure" commands to set the "-weight" option.

The "-weight" option gives a weight to a cell's width or height. When dividing the width or height in the surrounding grid among cells, the cell receives a proportional fraction of that total size, or the minimum size to hold its widget(s) in case the "-weight" value for that cell was 0 (which is the default).

        ttk::label .a -background red -text "My Label"

    --> grid columnconfigure . 0 -weight 1

        grid .a

(Other values for "-weight" are possible, to have a cell take multiple "space-units" of its surrounding grid.)

As can be seen, the cell now takes up all the remaining horizontal space in its parent grid, but not all vertical space. There is only 1 cell in this grid, and therefore, this cell is as wide as the grid, but not as high.

By also making the cell's height take up all the remaining space in its surrounding grid, the widget is aligned at the center of the surrounding grid:

        ttk::label .a -background red -text "My Label"

        grid columnconfigure . 0 -weight 1
    --> grid rowconfigure    . 0 -weight 1

        grid .a

Expanding a widget to fill its cell

The label is still only as big as necessary to fit the text "My Label".

To make the label expand to its cell's size, the "-sticky" option can be used when gridding the widget:

        ttk::label .a -background red -text "My Label"

        grid columnconfigure . 0 -weight 1
        grid rowconfigure    . 0 -weight 1

    --> grid .a -sticky news

The "sticky" option specifies which sides of the cell the widget is to stick to. In this case, we specified all sides (north, east, west, and south), so the widget expands on all sides.

Bonus clue: centering text in a label-widget

As a final modification, the widget's text can be centered using the "-anchor" option:

    --> ttk::label .a -background red -text "My Label" -anchor center

        grid columnconfigure . 0 -weight 1
        grid rowconfigure    . 0 -weight 1

        grid .a -sticky news

Nested multi-cell grids

Whereas the previous section dealt only with a 1-cell grid, 2 nested 2x2 grids are used hereafter.

A basic 2x2-grid

A basic configuration can look as follows:

        ttk::label .a -background red
        ttk::label .b -background green
        ttk::label .c -background blue
        ttk::label .d -background black

        grid .a .b -sticky news
        grid .c .d -sticky news

        grid columnconfigure . 0 -weight 1
        grid rowconfigure    . 0 -weight 1
        grid columnconfigure . 1 -weight 1
        grid rowconfigure    . 1 -weight 1

Each widget expands to fill its cell. The total width as well as height in the surrounding grid is divided equally among its cells (since each cell has the same "-weight" value).

Note that you can grid multiple widgets using 1 command. Options given then apply to all of those widgets.

When gridding multiple widgets at once, row- and column-numbers are assigned in a logical way. Apart from this implicit row- and column-assignment, you can grid widgets one by one, and explicitly use "-row" and/or "-column" options. (See the corresponding text at the TkDocs.com site.)

A grid within a grid

Next, the black label (".d") is given 4 children (".d.p" ... ".d.s"). These sub-widgets automatically receive their own 2x2 grid.

In the following example, these new labels are expanded to fill their respective cells (using "-sticky"), and their cells are expanded to divide their surrounding grid evenly in width and height (using "-weight"):

        ttk::label .a -background red
        ttk::label .b -background green
        ttk::label .c -background blue
        ttk::label .d -background black

    --> ttk::label .d.p -background yellow
    --> ttk::label .d.q -background cyan
    --> ttk::label .d.r -background purple
    --> ttk::label .d.s -background brown

        grid .a .b -sticky news
        grid .c .d -sticky news

    --> grid .d.p .d.q -sticky news
    --> grid .d.r .d.s -sticky news

        grid columnconfigure . 0 -weight 1
        grid rowconfigure    . 0 -weight 1
        grid columnconfigure . 1 -weight 1
        grid rowconfigure    . 1 -weight 1

    --> grid columnconfigure .d 0 -weight 1
    --> grid rowconfigure    .d 0 -weight 1
    --> grid columnconfigure .d 1 -weight 1
    --> grid rowconfigure    .d 1 -weight 1

All these cells and widgets fit snugly together. This is not always desired.

Adding space between cell-boundary and widget

We can give the brown lower-right sub-widget (".d.s") an orange child-widget (".d.s.foo"), and specify to leave some space between the surrounding cell and the new widget, using the "-padx" and "-pady" options:

        ttk::label .a -background red
        ttk::label .b -background green
        ttk::label .c -background blue
        ttk::label .d -background black

        ttk::label .d.p -background yellow
        ttk::label .d.q -background cyan
        ttk::label .d.r -background purple
        ttk::label .d.s -background brown

    --> ttk::label .d.s.foo -background orange

        grid .a .b -sticky news
        grid .c .d -sticky news

        grid .d.p .d.q -sticky news
        grid .d.r .d.s -sticky news

    --> grid .d.s.foo -sticky news -padx 40 -pady 20

        grid columnconfigure . 0 -weight 1
        grid rowconfigure    . 0 -weight 1
        grid columnconfigure . 1 -weight 1
        grid rowconfigure    . 1 -weight 1

        grid columnconfigure .d 0 -weight 1
        grid rowconfigure    .d 0 -weight 1
        grid columnconfigure .d 1 -weight 1
        grid rowconfigure    .d 1 -weight 1

    --> grid columnconfigure .d.s 0 -weight 1
    --> grid rowconfigure    .d.s 0 -weight 1

At this point, there are 3 grids:

  1. the top-level 2x2 grid, with the red widget at its upper left corner
  2. a second-level 2x2 grid, with the yellow widget at its upper left corner
  3. a third-level 1x1 grid, only containing the orange widget

Note that the surrounding space is present (with brown colour), but the result might be a bit unexpected - it seems as if the margin has been added after the space in the surrounding grid has been distributed evenly. Thus, the cell-with-margin around the orange widget is bigger than the other cells in the same grid.

I don't know how to prevent this, and moreover, I think it's seldom a practical problem.

Merging columns and rows

To keep the analogy with HTML's "table"-mechanism, we can merge cells and columns.

In the next example, the cyan cell (".d.q") from the previous picture is removed, and in its place, its left-hand neighbour (the yellow cell ".d.p") is specified to span 2 columns, using the "-columnspan" option:

        ttk::label .a -background red
        ttk::label .b -background green
        ttk::label .c -background blue
        ttk::label .d -background black

        ttk::label .d.p -background yellow
        ttk::label .d.r -background purple
        ttk::label .d.s -background brown

        ttk::label .d.s.foo -background orange

        grid .a .b -sticky news
        grid .c .d -sticky news

    --> grid .d.p -sticky news -columnspan 2
        grid .d.r .d.s -sticky news

        grid .d.s.foo -sticky news -padx 40 -pady 20

        grid columnconfigure . 0 -weight 1
        grid rowconfigure    . 0 -weight 1
        grid columnconfigure . 1 -weight 1
        grid rowconfigure    . 1 -weight 1

        grid columnconfigure .d 0 -weight 1
        grid rowconfigure    .d 0 -weight 1
        grid columnconfigure .d 1 -weight 1
        grid rowconfigure    .d 1 -weight 1

        grid columnconfigure .d.s 0 -weight 1
        grid rowconfigure    .d.s 0 -weight 1

The same can be done for rows, of course.

(Re)configuring multiple rows/columns at once

For convenience, where "0" and "1" were used as indices to "columnconfigure" and "rowconfigure" to specify column/row respectively, the keyword "all" can be used to mean, to apply a set of options to all columns or rows at once:

        ttk::label .a -background red
        ttk::label .b -background green
        ttk::label .c -background blue
        ttk::label .d -background black

        ttk::label .d.p -background yellow
        ttk::label .d.r -background purple
        ttk::label .d.s -background brown

        ttk::label .d.s.foo -background orange

        grid .a .b -sticky news
        grid .c .d -sticky news

        grid .d.p -sticky news -columnspan 2
        grid .d.r .d.s -sticky news

        grid .d.s.foo -sticky news -padx 40 -pady 20

    --> grid columnconfigure . all -weight 1
    --> grid rowconfigure    . all -weight 1

    --> grid columnconfigure .d all -weight 1
    --> grid rowconfigure    .d all -weight 1

    --> grid columnconfigure .d.s all -weight 1
    --> grid rowconfigure    .d.s all -weight 1

That's all!


Delivered to you by Vim, GNU Make, MultiMarkdown, bozohttpd, NetBSD, and 1 human.