Now that we know how to use a few basic widgets, we need to know how to customize their appearance and behavior. In the most general sense, you do this by subclassing each widget you want to customize. For example, if you were making a Pong GUI, you would probably end up defining some sort of retro/arcade style in classes like these:
class PongLabel(glooey.Label): ... class PongButton(glooey.Button): ...
There are several advantages to stylizing widgets by subclassing them like this. First, it’s very easy to create multiple variants of the same widget type. This comes in handy because most GUIs need several different kinds of button, header and body text, heavier and lighter borders or frames, etc. Second, subclassing does a great job of making the simple customizations easy (because superclasses can anticipate and facilitate common tweaks) and the complex ones possible (because subclasses can override any aspect of their base class).
This tutorial focuses on the simplest and easiest customizations, which typically amount to changing the appearance of existing widgets. The Composing widgets and Making widgets from scratch tutorials will cover more powerful ways to customize widgets.
These tutorials will use the Wesnoth theme as an example. Wesnoth is a popular open-source, fantasy-themed, turn-based strategy game. Although Wesnoth itself does not use glooey, recapitulating parts of its user interface is a good real-world demonstration of what glooey can do.
The easiest way to stylize a widget is to override its “custom” attributes.
These are class-wide attributes — conventionally prefixed with
— that control basic properties like colors, images, fonts, etc. The label
widget is a good example:
custom_alignment are all attributes defined in the Label class (or one of
its superclasses) for the explicit purpose of being overridden in subclasses.
The Label class has a lot more of these attributes too, check the
documentation for a complete list.
The background widget is another good example. The more idiomatic way to write the demo from the Especially useful widgets tutorial is like this:
It’s also common to stylize widgets with other widgets. For example, you can stylize the button widget by providing different widgets for the label and the various mouse rollover states. The syntax for doing this is analogous to the “custom” attributes defined above: just override a class variable with the widget class you want to use. The trick is that you can also create that widget class in place (i.e. by defining an inner class). This provides a very succinct way to customize composite widgets:
WesnothButton class in this example has four widget classes being
Down. The first is used to display whatever is in the foreground of
the button. Typically this is either a
Label or an
Image, but it could be
anything (e.g. an
HBox that puts a label next to an image). The remaining
overridden classes are used to display different background images in the
different rollover states.
This example shows off both ways of using widgets to define style. The
Foreground attribute is directly set to an existing widget class.
WesnothLabel widget we created earlier in this tutorial already has the
style we want for this button, so it’s nice that we can simply reuse it here.
Down attributes are
overridden by new widget classes defined in place. These background images
aren’t useful outside the button, so it’s nice that we don’t have to define
This is the first time we’ve seen the
Image widget, but hopefully it’s not
too hard to understand. It just displays the image specified by
custom_image. It’s perhaps more common to derive
Background (which allows the button
to grow and shrink with the text), but we use
Image here because the Wesnoth
theme has fixed-size buttons.
Because specifying rollover images for buttons is a very common task, the
Button class actually provides a more succinct way to accomplish it:
class WesnothButton(glooey.Button): Foreground = WesnothLabel Background = glooey.Image custom_base_image = pyglet.resource.image('base.png') custom_over_image = pyglet.resource.image('over.png') custom_down_image = pyglet.resource.image('down.png')
Background class is used for all the rollover states. If you
want, you can override
Background for specific rollover states by
Background class is
here we change it to
custom_base_image attribute is equivalent to the
custom_image attribute in the background widget for the
Base state, which in this example is
Image. This is a little bit
magical: the button basically finds any attributes matching
custom_base_*, renames them to
custom_*, then provides them to the
Base background class. In this way, any custom attributes provided
by any of the background classes are mirrored in the button class itself.