Every GUI application is made of a collection of widgets. These widgets are the buttons, text entry fields and menus of the application. In order for the widgets to display in any meaningful arrangement in the window, you need to use some sort of layout management code. In Tk, this management code is called the packer. The packer will arrange all the child widgets around the edges of a parent widget and, using the packer, you can display the widgets in your GUI application in a desired arrangement.
What Does the Packer Do?
Widgets in a GUI application follow a hierarchical parent/child relationship. This means that every widget, save for the application window itself, has a parent widget. The application window is referred to as the root widget and all other widgets are children of the root widget. Other widgets, such as the frame widget, can act as parent widgets, too. In fact, the frame widget acts as a parent widget to better control how the widgets are packed by the packer.
The packer is responsible for arranging the child widgets inside of their parent widgets. Each widget has an ordered list of its child widgets as well as information about to which side of the parent the widgets wish to be placed. The packer processes this list and decides on the position and size of each child widget. Because widgets have no position or size before they are packed, they cannot be displayed or used before they are packed.
How Does the Packer Pack?
When the packer processes each child widget, it has an empty space in the parent widget called the "cavity." The cavity is a rectangular space into which no widgets have yet been packed. The packer packs the child widget on whichever side of the cavity it has requested, allocating for it a rectangular space called a "parcel." If the requested side is left or right, the widget's vertical size will be expanded to match the parcel's size. If the requested position is top or bottom, the widget's horizontal size will be expanded to match the parcel's size.
The frame widget must be used in order to further group widgets inside a parent widget that has both horizontally and vertically packed children. A frame widget is a container for other widgets. Widgets may be packed horizontally within it and then the frame itself is packed vertically. To the packer, though, the frame is just a single widget--it doesn't know there are more widgets inside.
Anchoring, Expanding and Filling
There are three major options to further define how widgets should be packed if there is extra space in the parcel they're given. Anchoring defines which edge of the parcel the widget should touch. The directions north, south, east and west are used here instead of the packing directions top, bottom, left and right. Corners can also be defined using northeast, southwest, etc. By default, widgets are centered in their parcel without touching any edge.
The expand flag tells the packer that the widget should expand in all directions to fit its parcel size. This is useful for expanding buttons to fill the space they're given, instead of simply sizing the button to match its text. The fill option is much like expand, except it gives greater control over how the widget should expand. You can expand the widget to fill the space in the horizontal direction, vertical direction or in both directions. Using the both option is functionally equivalent to the expand flag.
The final element to the packer is padding. Padding is simply empty space left around the widgets. Each widget has padding and internal padding. Padding will make the packer allocate a larger parcel for the widget. Internal padding will change the size of the widget within the parcel. Padding is useful for most widgets. It leaves a little space around the edges so the application doesn't seem so cluttered. Internal padding is useful for widgets like frames, so the child widgets don't crowd along its edges.
Using the Packer
In the following example, there are two groups of check boxes and radio buttons. Each group is contained by a frame widget and within the frame widgets, the TkCheckBox or TkRadioButton widgets are packed horizontally from the top edge. Within the root widget (the application window), the two frame widgets are packed horizontally from the left edge.
Inside each frame widget, the check box or radio button widgets are anchored to the left edge. This is because the text of each check box or radio button is a different length. The default anchor is to center the widget in its parcel, but if this is left as the default, the check boxes won't line up. Thus, they must be anchored to the west edge of the frame.
The frames themselves have some padding so the window isn't so cramped. On the top and bottom, a padding of 10 pixels is used. To the left of the left frame, a padding of 10 pixels is also used. To the right of the left widget, however, there's a padding of 5 pixels so the right frame can also use a padding of 5 pixels, making the padding consistent for the entire window.
#!/usr/bin/env ruby require 'tk' root = TkRoot.new do title "Packer Demo" end # Frame for radio buttons rad_frame = TkFrame.new(root) do pack 'side' => 'left', 'padx' => [10, 5], 'pady' => 10 end $rad_value = nil rad_buttons = %w[ One Two Three ] rad_buttons.map do|r| TkRadioButton.new(rad_frame) do variable $rad_value value r text r pack 'side' => 'top', 'anchor' => 'w' end end # Frame for the check boxes cb_frame = TkFrame.new(root) do pack 'side' => 'left', 'padx' => [5, 10], 'pady' => 10 end cb_buttons = %w[ Red Green Blue ] cb_buttons.map do|c| TkCheckButton.new(cb_frame) do text c pack 'side' => 'top', 'anchor' => 'w' end end Tk.mainloop