Next / Previous / Contents

50.1. ttk layouts: Structuring a style

In general, the pieces of a widget are assembled using the idea of a cavity, an empty space that is to be filled with elements.

For example, in the classic theme, a button has four concentric elements. From the outside in, they are the focus highlight, border, padding, and label elements.

Each of these elements has a 'sticky' attribute that specifies how many of the four sides of the cavity it “sticks” to. For example, if an element has a sticky='ew' attribute, that means it must stretch in order to stick to the left (west) and right (east) sides of its cavity, but it does not have to stretch vertically.

Most of the built-in ttk styles use the idea of a layout to organize the different layers that make up a widget. Assuming that S is an instance of ttk.Style, to retrieve that style's layout use a method call of this form, where widgetClass is the name of the widget class.

S.layout(widgetClass)

Some widget classes don't have a layout; in those cases, this method call will raise a tk.TclError exception.

For the widget classes that have a layout, the returned value is a list of tuples (eltName, d). Within each tuple, eltName is the name of an element and d is a dictionary that describes the element.

This dictionary may have values for the following keys:

'sticky'

A string that defines how this element is to be positioned within its parent. This string may contain zero or more of the characters 'n', 's', 'e', and 'w', referring to the sides of the box with the same conventions as for anchors. For example, the value sticky='nsw' would stretch this element to adhere to the north, south, and west sides of the cavity within its parent element.

'side'

For elements with multiple children, this value defines how the element's children will be positioned inside it. Values may be 'left', 'right', 'top', or 'bottom.

'children'

If there are elements inside this element, this entry in the dictionary is the layout of the child elements using the same format as the top-level layout, that is, a list of two-element tuples (eltName, d).

Let's dissect the layout of the stock Button widget of the 'classic' theme in this conversational example.

>>> import ttk
>>> s = ttk.Style()
>>> s.theme_use('classic')
>>> b = ttk.Button(None, text='Yo')
>>> bClass = b.winfo_class()
>>> bClass
'TButton'
>>> layout = s.layout('TButton')
>>> layout
[('Button.highlight', {'children': [('Button.border', {'border':
'1', 'children': [('Button.padding', {'children': [('Button.label',
{'sticky': 'nswe'})], 'sticky': 'nswe'})], 'sticky': 'nswe'})],
'sticky': 'nswe'})]

All those parentheses, brackets, and braces make that structure a bit hard to understand. Here it is in outline form:

  • The outermost element is the focus highlight; it has style 'Button.highlight'. Its 'sticky' attribute is 'nswe', meaning it should expand in all four directions to fill its cavity.

  • The only child of the focus highlight is the border element, with style 'Button.border'. It has a 'border' width of 1 pixel, and its 'sticky' attribute also specifies that it adheres to all four sides of its cavity, which is defined by the inside of the highlight element.

  • Inside the border is a layer of padding, with style 'Button.padding'. Its sticky attribute also specifies that it fills its cavity.

  • Inside the padding layer is the text (or image, or both) that appears on the button. Its style is 'Button.label', with the usual sticky='nswe' attribute.

Each element has a dictionary of element options that affect the appearance of that element. The names of these options are all regular Tkinter options such as 'anchor', 'justify', 'background', or 'highlightthickness'.

To obtain the list of option names, use a method call of this form, where S is an instance of class ttk.Style:

S.element_options(styleName)

The result is a sequence of option strings, each preceded by a hyphen. Continuing our conversational above, where s is an instance of ttk.Style:

>>> d = s.element_options('Button.highlight')
>>> d
('-highlightcolor', '-highlightthickness')

To find out what attributes are associated with an element option, use a method call of this form:

s.lookup(layoutName, optName)

Continuing our example:

>>> s.lookup('Button.highlight', 'highlightthickness')
1
>>> s.lookup('Button.highlight', 'highlightcolor')
'#d9d9d9'
>>> print s.element_options('Button.label')
('-compound', '-space', '-text', '-font', '-foreground', '-underline',
'-width', '-anchor', '-justify', '-wraplength', '-embossed', '-image',
'-stipple', '-background')
>>> s.lookup('Button.label', 'foreground')
'black'