TkDocs Tutorial - Organizing Complex Interfaces (original) (raw)

This tutorial will quickly get you up and running with the latest Tk from Python, Tcl, Ruby, and Perl on macOS, Windows, or Linux. It provides all the essentials about core Tk concepts, the various widgets, layout, events and more that you need for your application.

If you have a complex user interface, you'll need to find ways to organize it that don't overwhelm your users. There are several different approaches to doing this. Both general-purpose and platform-specific human interface guidelines are good resources when designing your user interface.

When we talk about complexity in this chapter, we don't mean the underlying technical complexity of how the program is implemented. Instead, we mean how it's presented to users. A user interface can be pulled together from many different modules, built from hundreds of widgets combined in a deeply nested hierarchy, but that doesn't mean users need to perceive it as complex.

Multiple windows

One benefit of using multiple windows in an application can be to simplify the user interface. Done well, it can require users to focus only on the contents of one window at a time to complete a task. Forcing them to focus on or switch between several windows can also have the opposite effect. Similarly, showing only the widgets relevant to the current task (i.e., via grid) can help simplify the user interface.

White space

If you do need to display a large number of widgets onscreen at the same time, think about how to organize them visually. We've seen how grid makes it easy to align widgets with each other. White space is another useful aid. Place related widgets close to each other (possibly with an explanatory label immediately above) and separate them from other widgets by white space. This helps users organize the user interface in their own minds.

The recommended amount of white space around different widgets, between groups of widgets, around borders, etc., is highly platform-specific. While you can do an adequate job without worrying about exact pixel numbers, you'll need to tune this for each platform if you want a highly polished user interface.

Separator

A second approach to grouping widgets in one display is to place a thin horizontal or vertical rule between groups of widgets; often, this can be more space-efficient than using white space, which may be relevant for a tight display. Tk provides a simple separator widget for this purpose.

screen shot
Separator widgets.

Separators are created using the **ttk.Separator** class:

s = ttk.Separator(parent, orient=HORIZONTAL)

Separators are created using the **ttk::separator** command:

ttk::separator .s -orient horizontal

Separators are created using the **Tk::Tile::Separator** class:

s = Tk::Tile::Separator.new(parent) { orient 'horizontal' }

Separators are created using the **new_ttk__separator** method, a.k.a. **Tkx::ttk__separator**:

The orient option may be specified as either horizontal or vertical.

Label Frames

A labelframe widget, also commonly known as a group box, provides another way to group related components. It acts like a normal ttk::frame, in that it contains other widgets that you grid inside it. However, it is visually set off from the rest of the user interface. You can optionally provide a text label to be displayed outside the labelframe.

screen shot
Labelframe widgets.

Labelframes are created using the **ttk.Labelframe** class:

lf = ttk.Labelframe(parent, text='Label')

Labelframes are created using the **ttk::labelframe** command:

ttk::labelframe .lf -text "Label"

Labelframes are created using the **Tk::Tile::Labelframe** class:

lf = Tk::Tile::Labelframe.new(parent) { text 'Label' }

Labelframes are created using the **new_ttk__labelframe** method, a.k.a. **Tkx::ttk__labelframe**:

Paned Windows

A panedwindow widget lets you stack two or more resizable widgets above and below each other (or to the left and right). Users can adjust their relative heights (or widths) by dragging a sash located between the panes. Typically the widgets you're adding to a panedwindow will be frames containing many other widgets.

screen shot
Panedwindow widgets (shown here managing several labelframes).

Panedwindows are created using the **ttk.Panedwindow** class:

p = ttk.Panedwindow(parent, orient=VERTICAL)
# two panes, each of which would get widgets gridded into it:
f1 = ttk.Labelframe(p, text='Pane1', width=100, height=100)
f2 = ttk.Labelframe(p, text='Pane2', width=100, height=100)   
p.add(f1)
p.add(f2)

Panedwindows are created using the **ttk::panedwindow** command:

ttk::panedwindow .p -orient vertical
# two panes, each of which would get widgets gridded into it:
ttk::labelframe .p.f1 -text Pane1 -width 100 -height 100
ttk::labelframe .p.f2 -text Pane2 -width 100 -height 100
.p add .p.f1
.p add .p.f2

Panedwindows are created using the **Tk::Tile::Paned** class:

p = Tk::Tile::Paned.new(parent) { orient 'vertical' }
# two panes, each of which would get widgets gridded into it:
f1 = Tk::Tile::Labelframe.new(p) {text 'Pane1'; width 100; height 100;}
f2 = Tk::Tile::Labelframe.new(p) {text 'Pane2'; width 100; height 100;}
p.add f1, nil
p.add f2, nil

The extra nil parameter to add can be replaced with a hash of pane-specific options, which usually aren't needed.

Panedwindows are created using the **new_ttk__panedwindow** method, a.k.a. **Tkx::ttk__panedwindow**:

# two panes, each of which would get widgets gridded into it: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mn>1</mn><mo>=</mo></mrow><annotation encoding="application/x-tex">f1 = </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mord">1</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span></span></span></span>p->new_ttk__labelframe(-text => "Panel", -width => 100, -height => 100); <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>f</mi><mn>2</mn><mo>=</mo></mrow><annotation encoding="application/x-tex">f2 = </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mord">2</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span></span></span></span>p->new_ttk__labelframe(-text => "Pane2", -width => 100, -height => 100);  <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mo>−</mo><mo>&gt;</mo><mi>a</mi><mi>d</mi><mi>d</mi><mo stretchy="false">(</mo></mrow><annotation encoding="application/x-tex">p-&gt;add(</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">p</span><span class="mord">−</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal">dd</span><span class="mopen">(</span></span></span></span>f1); <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mo>−</mo><mo>&gt;</mo><mi>a</mi><mi>d</mi><mi>d</mi><mo stretchy="false">(</mo></mrow><annotation encoding="application/x-tex">p-&gt;add(</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7778em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">p</span><span class="mord">−</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">&gt;</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">a</span><span class="mord mathnormal">dd</span><span class="mopen">(</span></span></span></span>f2);

A panedwindow is either vertical (its panes are stacked vertically on top of each other) or horizontal. Importantly, each pane you add to the panedwindow must be a_direct child_ of the panedwindow itself.

Calling the add method adds a new pane at the end of the list of panes. Theinsert _position_ _subwindow_ method allows you to place the pane at the given position in the list of panes (0..n-1). If the pane is already managed by the panedwindow, it will be moved to the new position. You can use the forget _subwindow_ to remove a pane from the panedwindow (you can also pass a position instead of a subwindow).

You can assign relative weights to each pane so that if the overall panedwindow resizes, certain panes will be allocated more space than others. As well, you can adjust the position of each sash between items in the panedwindow. See the command reference for details.

Notebook

A notebook widget uses the metaphor of a tabbed notebook to let users switch between one of several pages using an index tab. Unlike with paned windows, users only see a single page (akin to a pane) at a time.

screen shot
Notebook widgets.

Notebooks are created using the **ttk.Notebook** class:

n = ttk.Notebook(parent)
f1 = ttk.Frame(n)   # first page, which would get widgets gridded into it
f2 = ttk.Frame(n)   # second page
n.add(f1, text='One')
n.add(f2, text='Two')

Notebooks are created using the **ttk::notebook** command:

ttk::notebook .n
ttk::frame .n.f1; # first page, which would get widgets gridded into it 
ttk::frame .n.f2; # second page
.n add .n.f1 -text "One"
.n add .n.f2 -text "Two"

Notebooks are created using the **Tk::Tile::Notebook** class:

n = Tk::Tile::Notebook.new(parent)
f1 = Tk::Tile::Frame.new(n); # first page, which would get widgets gridded into it
f2 = Tk::Tile::Frame.new(n); # second page
n.add f1, :text => 'One'
n.add f2, :text => 'Two'

Notebooks are created using the **new_ttk__notebook** method, a.k.a. **Tkx::ttk__notebook**:

The operations on tabbed notebooks are similar to those on panedwindows. Each page is typically a frame and again must be a direct child (subwindow) of the notebook itself. Add a new page and its associated tab after the last tab with the add _subwindow_ _?option_ _value...?_ method. The text tab option sets the label on the tab; also useful is the statetab option, which can have the value normal, disabled (not selectable), orhidden.

To insert a tab at somewhere other than the end of the list, use the insert _position_ _subwindow_ _?option_ _value...?_, and to remove a given tab, use the forget method, passing it either the position (0..n-1) or the tab's subwindow. You can retrieve the list of all subwindows contained in the notebook via the tabs method.

To retrieve the currently selected subwindow, call the select method, and change the selected tab by passing either the tab's position or the subwindow itself as a parameter.

To change a tab option (like the text label on the tab or its state), you can use the tab(_tabid_, _option_=_value_)method (where _tabid_ is again the tab's position or subwindow); omit the =_value_ to return the current value of the option.

To retrieve the selected subwindow, call the select method, and change the selected tab by passing it either the tab's position or the subwindow itself as a parameter.

To change a tab option (like the text label on the tab or its state), you can use the tab _tabid_ _option_ _value_method (where _tabid_ is again the tab's position or subwindow); omit the value to return the current value of the option.

To retrieve the subwindow that is currently selected, call the selected method, and change the selected tab by calling the select method, passing it either the tab's position or the subwindow itself as a parameter.

To change a tab option (like the text label on the tab or its state), you can use the itemconfigure _tabid_, _:option => value_ method (where _tabid_ is again the tab's position or subwindow); use the itemcget _tabid_, _:option_ to return the current value of the option.

The itemconfigure and itemcget methods exist alongside the tabconfigure and tabcgetmethods, which are more true to the API; however the tabcget method currently has a bug in it.

To retrieve the subwindow that is currently selected, call the select method, and change the selected tab by passing it either the tab's position or the subwindow itself as a parameter.

To change a tab option (like the text label on the tab or its state), you can use the tab _tabid_ _option_ _value_method (where _tabid_ is again the tab's position or subwindow); omit the _value_ to return the current value of the option.

Notebook widgets generate a <<NotebookTabChanged>> virtual event whenever a new tab is selected.

Again, there are a variety of less frequently used options and commands detailed in thecommand reference.

Spotted a mistake? Couldn't find what you were looking for? Suggestions? Let me know!
If you've found this tutorial useful, please check out Modern Tkinter.