The Theme System
Theme Properties
A property descriptor class called ThemeProperty
is provided for defining theme attributes. When such an attribute is
read, its value is automatically looked up in the current theme
hierarchy starting at theme.root (see the Theme Lookup below for details of the theme lookup process).
Because
looking up a theme attribute can be an expensive process, it is only
done the first time the attribute is read for a particular instance of
the class, and the value cached in an instance attribute having the
same name with "_" prepended. Subsequent access to the theme property
will return the cached value. This means that changing the theme will
not affect the appearance of any widgets that have already been
created. Since themes are intended for establishing the overall
appearance of the application, this should not be a serious problem.
If
you assign to a theme property, the cached value is replaced by the
value you assign. This means you can override the value provided by the
theme by assigning a value to the property just like a normal attribute.
If
you subclass a class that defines a theme property, and define an
initial value for it as a class attribute, it becomes a normal
attribute and is no longer a theme property for that class or its
subclasses (unless a subclass re-defines it as a theme property again).
This can be useful if you are subclassing a widget and don't want to go
to the trouble of setting up a theme for your subclass – you can just
override its theme properties directly in the class.
Fonts as Theme Properties
Theme
properties representing fonts are handled in a special way. To avoid
loading fonts that won't be used, instead of holding a font object, the
theme hierarchy holds a tuple specifying the size and filename of the
font.
A subclass of ThemeProperty called FontProperty
is provided for handling such properties. On first access, it loads the
font and returns the font object as the value of the property.Theme Lookup
Values for theme properties are looked up in a hierarchy of Theme objects, beginning with theme.root.
The root Theme object contains two kinds of attributes: default values
for theme properties, and other Theme objects containing values
pertaining to specific classes.
A value for a particular class C is looked up as follows. First, theme.root
is looked in for a Theme object for class C, and that Theme object is
looked in for a value for the attribute in question. If a value is
found, it is returned. Otherwise, the process is repeated for each of
C's base classes, in method resolution order. If a value is still not
found for the attribute, theme.root itself is looked in for a default value. (If it's not there either, an AttributeError results.)
As
a further refinement, a Theme object can be based on another Theme
object. When looking in a Theme object for an attribute, if not found
it will be looked for in the base theme, if any, and so forth.
As an example, here is an excerpt from theme.py showing how the default theme hierarchy for the Button class is set up:
root = Theme('root')
root.font = (15, "Vera.ttf")
root.fg_color = (255, 255, 255)
root.Button = Theme('Button')
root.Button.font = (18, "VeraBd.ttf")
root.Button.fg_color = (255, 255, 0)
framed = Theme('framed')
framed.border_width = 1
framed.margin = 3
root.TextField = Theme('TextField', base = framed)
Here
is a diagram showing the search path when looking up a theme property
for class Button, whose base classes are BaseButton, Label and Widget.
The
theme objects shown in grey don't exist in the default theme hierarchy,
but if an application were to add them, they would be searched in the
order shown.
Here is the search order for the TextField class: