Home / Library / Tkinter / Introduction / Widget styling

Widget styling

There are two widget sets in Tkinter and they are styled in completely different ways. This is the single most confusing thing about the toolkit.

The old widgets

Classic tk widgets take appearance options directly:

import tkinter as tk

tk.Label(root, text="Hello", background="white", foreground="navy",
         font=("Helvetica", 14, "bold"), padx=10, pady=6).pack()

Simple, and it works the same everywhere. It also looks nothing like the operating system, which is why ttk exists.

The themed widgets

ttk widgets get their appearance from a style, not from options on the widget. Passing background= to a ttk.Label raises an error:

from tkinter import ttk

style = ttk.Style()
style.configure("Title.TLabel", foreground="#3c5a77", font=("Helvetica", 18, "bold"))

ttk.Label(root, text="Hello", style="Title.TLabel").pack()

Style names follow a convention: TLabel, TButton, TEntry, TFrame and so on. Putting a prefix before it, as in Title.TLabel, makes a named variant that inherits everything else from the base.

Changing every widget of a kind

style.configure("TButton", padding=8)

Configure the base name with no prefix and every button in the application picks it up.

States

configure sets the normal appearance. map sets what changes when the widget is disabled, active, pressed or focused:

style.map("TButton",
          foreground=[("disabled", "#999999"), ("pressed", "white")],
          background=[("pressed", "#33608f"), ("active", "#4a7fb5")])

Themes

print(style.theme_names())
style.theme_use("clam")

Which themes exist depends on the platform. clam, alt, default and classic are everywhere; Windows and macOS add native ones. clam is the one that responds best to styling, so it is a common choice when you want a consistent look across platforms rather than a native one.

Some options simply do not exist in some themes. The native macOS button, for instance, ignores most colour settings, because the operating system draws it. Switching to clam is the usual answer when styling appears to do nothing.

What cannot be themed

A few widgets exist only in classic tk: Text, Canvas, Listbox, Menu. Mixing the two sets is normal, but it means a themed application still has to style those four the old way, and matching the colours by hand is part of the job.

A workable pattern

style = ttk.Style()
style.theme_use("clam")
style.configure(".", font=("Helvetica", 11))
style.configure("TButton", padding=(12, 6))
style.configure("Heading.TLabel", font=("Helvetica", 16, "bold"))

The dot on its own is the root style: everything inherits from it, so that is where application wide defaults go.

Related