Styles and Themes

The themed aspect of the modern Tk widgets is one of the most powerful and exciting aspects of the newer widget set. Yet, it's also one of the most confusing.

This chapter explains how to use existing styles (which control how widgets like buttons look) and themes (which are a collection of styles that define how all the widgets in your application look). Changing themes can give your application an entirely different look.

If you're looking to create custom styles and themes, the following chapter will guide you through the details.

screen shot
Applying different themes.

Note that it's not just colors that have changed, but the actual shape of individual widgets. Styles and themes are extremely flexible.

Why?

However, before you get carried away, very few applications will benefit from switching themes like this. Some games or educational programs might be exceptions. Using the standard Tk theme for a given platform will display widgets the way people expect to see them, particularly if they're running macOS and Windows.

On Linux systems, there's far less standardization of look and feel. Users expect (and are more comfortable with) some variability and "coolness." Because different widget sets (typically GTK and QT) are used by window managers, control panels, and other system utilities, Tk can't seamlessly blend in with the current settings on any particular system. Most of the Linux screenshots in this tutorial use Tk's alt theme. Despite users being accustomed to variability, there are limits to what most users will accept. A prime example is the styling of core widgets in Tk's classic widget set, matching circa-1992 OSF/Motif.

Styles and themes, used in a more targeted manner and with significant restraint, can have a role to play in modern applications. This chapter explains why and when you might want to use them and how to go about doing so. We'll begin by drawing a parallel between Tk's styles and themes and another realm of software development.

Understanding styles and themes

If you're familiar with web development, you know about cascading stylesheets (CSS). There are two ways it can be used to customize the appearance of an element in your HTML page. One way is to add a bunch of style attributes (fonts, colors, borders, etc.) directly to an element in your HTML code via the style attribute. For example:

<label style="color:red; font-size:14pt; font-weight:bold; background-color:yellow;">Meltdown imminent!</label>

The other way to use CSS is to attach a class to each widget via the class attribute. The details of how elements of that class appear are provided elsewhere, often in a separate CSS file. You can attach the same class to many elements, and they will all have the same appearance. You don't need to repeat the full details for every element. More importantly, you separate the logical content of your site (HTML) from its appearance (CSS).

<label class="danger">Meltdown imminent!</label>
...
<style type="text/css">
label.danger {color:red; font-size:14pt; font-weight:bold; background-color:yellow;}
</style>

Back to Tk.

  • In the classic Tk widgets, all appearance customizations require specifying each detail on individual widgets, akin to always using the style HTML attribute.
  • In the themed Tk widgets, all appearance customizations are made via attaching a style to a widget, akin to using the class HTML attribute. Separately, you define how widgets with that style will appear, akin to writing CSS.
  • Unlike with HTML, you can't freely mix and match. You can't customize some themed entries or buttons with styles and others by directly changing appearance options.

Yes, there are a few exceptions, like labels where you can customize the font and colors through both styles and configuration options.

Applying a style

To use a style means to apply that style to an individual widget. All you need is the style's name and the widget to apply it to. Setting the style can be done at creation time:

b = ttk.Button(parent, text='Hello', style='Fun.TButton')
ttk::button .b -text "Hello" -style "Fun.TButton"
b = Tk::Tile::Button.new(parent) {text "Hello"; style "Fun.TButton"}
$b = $parent->new_ttk__button(-text => "Hello", -style => "Fun.TButton");

A widget's style can also be changed later with the style configuration option:

b['style'] = 'Emergency.TButton'
.b configure -style "Emergency.TButton"
b['style'] = "Emergency.TButton"
$b->configure(-style => "Emergency.TButton");

Benefits

So why use styles and themes in Tk? They take the fine-grained details of appearance decisions away from individual instances of widgets.

That makes for cleaner code and less repetition. If you have 20 entry widgets in your application, you don't need to repeat the exact appearance details every time you create one (or write a wrapper function). Instead, you assign them a style.

Themes collect a series of styles, putting all appearance decisions in one place. And because styles for a button and styles for other widgets can share common elements, collecting them in themes promotes consistency and improves reuse.

Styles also have many benefits for widget authors. Widgets can delegate most appearance decisions to styles. A widget author no longer has to hardcode logic to the effect of "when the state is disabled, consult the 'disabledforeground' configuration option and use that for the foreground color." Not only did that make coding widgets longer (and more repetitive), but it also restricted how a widget could be changed based on its state. If the widget author omitted logic to change the font when the state changed, you were out of luck as an application developer using the widget.

Using styles, widget authors don't need to provide code for every possible appearance option. That not only simplifies the widget but paradoxically ensures that a wider range of appearances can be set, including those the widget author may not have anticipated.

Using existing themes

Before delving into the weightier matters of tastefully and selectively modifying and applying styles to improve the usability of your application and cleanliness of your code, let's deal with the fun bits: using existing themes to completely reskin your application.

Themes are identified by a name. You can obtain the names of all available themes:

>>> s = ttk.Style()
>>> s.theme_names()
('aqua', 'step', 'clam', 'alt', 'default', 'classic')

Tkinter encapsulates all style manipulations in the ttk.Style class. We'll therefore need an instance of that class for this and other operations.

% ttk::style theme names
aqua clam alt default classic
>> Tk::Tile::Style.theme_names
=> ["aqua", "step", "clam", "alt", "default", "classic"]
Perl> Tkx::ttk__style_theme_names()
aqua clam alt default classic

screen shot
Built-in themes.

Besides the built-in themes (alt, default, clam, and classic), macOS includes a theme named aqua to match the system-wide style, while Windows includes themes named vista, winxpnative, and winnative.

Only one theme can be active at a time. To obtain the name of the theme currently in use, use the following:

>>> s.theme_use()
'aqua'
% ttk::style theme use
aqua
>> Tk.tk_call('ttk::style', 'theme', 'use')
=> "aqua"

As of Ruby/Tk 0.2.0 (current version as of 2020), the Tk::Tile::Style.theme_use API doesn't allow querying the current style.

Perl> Tkx::ttk__style_theme_use()
aqua

This API, which was originally targeted for Tk 8.6, was back-ported to Tk 8.5.9. If you're using an earlier version of Tk getting this info is a bit trickier.

Switching to a new theme can be done with:

s.theme_use('themename')
ttk::style theme use themename
Tk::Tile::Style.theme_use "themename"
Tkx::ttk__style_theme_use("themename");

What does this actually do? Obviously, it sets the current theme to the indicated theme. Doing this, therefore, replaces all the currently available styles with the set of styles defined by the theme. Finally, it refreshes all widgets, so they take on the appearance described by the new theme.

Third-party themes

With a bit of looking around, you can find some existing add-on themes available for download. A good starting point is https://wiki.tcl-lang.org/page/List+of+ttk+Themes.

Though themes can be defined in any language that Tk supports, most that you will find are written in Tcl. How can you install them so that they are available to use in your application?

For Tkinter users, you'll find an increasing number of themes on PyPi and elsewhere designed to be directly incorporated into your applications. Some of these are small wrappers around the Tcl-based themes below, making them easier to use. Some are full-fledged themes in their own right, created using Python and Tkinter, without making use of Tcl code. The book version of this tutorial, Modern Tkinter, includes bonus material describing some of these, including a powerful and highly configurable theme called ttkbootstrap, which adds not only new visuals but also several entirely new widgets you can use in your Tkinter applications.

As an example, let's use the "awdark" theme, available from https://sourceforge.net/projects/tcl-awthemes/. Download and unzip the awthemes-*.zip file somewhere. You'll notice it contains a bunch of .tcl files, a subdirectory i containing more directory with images used by the theme, etc.

One of the files is named pkgIndex.tcl. This identifies it as a Tcl package, which is similar to a module in other languages. If we look inside, you'll see a bunch of lines like package ifneeded awdark 7.7. Here, awdark is the name of the package, and 7.7 is its version number. It's not unusual, as in this case, for a single pkgIndex.tcl file to provide several packages.

To use it, we need to tell Tcl where to find the package (via adding its directory to Tcl's auto_path) and the name of the package to use.

root.tk.call('lappend', 'auto_path', '/full/path/to/awthemes-9.3.1')
root.tk.call('package', 'require', 'awdark')
lappend auto_path "/full/path/to/awthemes-9.3.1"
package require awdark
Tk.tk_call('lappend', 'auto_path', '/full/path/to/awthemes-9.3.1')
Tk.tk_call('package', 'require', 'awdark')
Tkx::eval("lappend auto_path \"/full/path/to/awthemes-9.3.1\"");
Tkx::eval("package require awdark");

If the theme is instead implemented as a single Tcl source file, without a pkgIndex.tcl, you can make it available like this:

root.tk.call('source', '/full/path/to/themefile.tcl')
source "/full/path/to/themefile.tcl"
Tk.tk_call('source', '/full/path/to/themefile.tcl')
Tkx::eval("source \"/full/path/to/themefile.tcl\"");

You should now be able to use the theme in your own application, just as you would a built-in theme.