Previous | Tutorial index | | Next: none

Tutorial 10: The Themed Tkinter (Ttk) Module – Differences from Classic Tk

Learning Objectives

1. Introduction – Why Ttk Exists

If you have built a GUI using classic Tkinter widgets, you may have noticed they look... dated. The classic widgets have a distinct 1990s appearance—grey, chunky 3D bevels, and a fixed look that does not adapt well to modern operating systems.

Enter Ttk (Themed Tkinter). Introduced in Tk 8.5 and available in Python's tkinter.ttk module, Ttk provides a set of widgets that leverage the platform's native theming engine. On Windows 10/11, Ttk widgets look like native Windows controls; on macOS, they adopt Aqua styling; on Linux (with GTK), they blend in with the desktop environment.

But Ttk is not just about good looks—it introduces a new styling API, state management, and new widgets that are not available in classic Tk. This tutorial will guide you through everything you need to transition from classic to themed Tkinter.

2. What Is Ttk? – Getting Started

2.1 Importing Ttk

The standard practice is to import both classic Tkinter and Ttk side‑by‑side:

import tkinter as tk # Classic widgets from tkinter import ttk # Themed widgets

You can also do import tkinter.ttk as ttk, but the above is more common.

2.2 The Ttk Widget Philosophy

2.3 Basic Ttk Example

import tkinter as tk from tkinter import ttk root = tk.Tk() root.title("Ttk Example") # Classic label tk.Label(root, text="Classic Label", bg="yellow").pack(pady=5) # Themed label ttk.Label(root, text="Themed Label").pack(pady=5) root.mainloop()

Notice the difference: the Ttk label has a clean, flat appearance that matches your operating system, while the classic label has a grey background (unless you specify bg).

3. Key Difference 1: Appearance and Themes

3.1 What Is a Theme?

A theme is a collection of styles that define the appearance of all Ttk widgets. Themes are platform‑specific and can be switched at runtime.

3.2 Listing Available Themes

print(ttk.Style().theme_names())

Common themes:

3.3 Switching Themes

style = ttk.Style() style.theme_use('clam') # Switch to the 'clam' theme

3.4 The Power of Theme Switching

You can change the entire look of your application with one line:

def toggle_theme(): current = style.theme_use() if current == 'clam': style.theme_use('vista') else: style.theme_use('clam')

This is a huge advantage over classic Tk, where you would have to manually reconfigure every widget's colours.

4. Key Difference 2: Styling with ttk.Style

4.1 The Problem with Classic Styling

Classic Tk allows you to set colours and fonts directly:

tk.Button(root, text="Click", bg="red", fg="white", font=("Arial", 12))

Ttk does NOT allow this. You cannot pass bg, fg, font, or relief to a Ttk widget's constructor. If you try, it will be silently ignored (or raise an error in some cases). Instead, you must use the ttk.Style class.

4.2 The ttk.Style Class – Your Styling Toolkit

The Style class manages the appearance of all Ttk widgets. Here are its core methods:

Method Description
configure(style_name, **options) Sets style options for a given widget class (e.g., "TButton").
map(style_name, **options) Defines dynamic style changes based on widget states (e.g., hover, pressed).
layout(style_name, layout_spec) Defines the internal layout of a widget (advanced).
theme_names() Returns a tuple of available themes.
theme_use(theme_name) Sets the current theme.

4.3 Widget Style Names

Each Ttk widget has a style name that you use to configure it. The naming convention is "T" + widget class name (with the first letter capitalised):

Widget Style Name
ttk.Button "TButton"
ttk.Label "TLabel"
ttk.Entry "TEntry"
ttk.Frame "TFrame"
ttk.LabelFrame "TLabelframe"
ttk.Checkbutton "TCheckbutton"
ttk.Radiobutton "TRadiobutton"
ttk.Combobox "TCombobox"
ttk.Progressbar "TProgressbar"
ttk.Notebook "TNotebook"
ttk.Treeview "Treeview" (note: no leading "T")

4.4 Configuring a Style – Basic Example

style = ttk.Style() style.configure("TButton", font=("Helvetica", 12), foreground="blue", background="yellow")

Important: The options you can set depend on the theme and the widget. Common options include:

4.5 Custom Style Names – Creating Your Own Widget Classes

You can create custom style names that inherit from a base style. This is useful for having different styles for different buttons.

# Create a custom style called "Success.TButton" that inherits from "TButton" style.configure("Success.TButton", foreground="green", font=("Arial", 10, "bold")) style.configure("Danger.TButton", foreground="red") btn1 = ttk.Button(root, text="Save", style="Success.TButton") btn2 = ttk.Button(root, text="Delete", style="Danger.TButton")

4.6 Dynamic Styling with .map()

The .map() method allows you to change styles based on the widget's state (e.g., hover, pressed, disabled). This is how modern UIs provide visual feedback.

Syntax:

style.map(style_name, **state_specs)

state_specs is a dictionary where keys are options (like foreground, background) and values are lists of (state, value) tuples.

Example: Button hover effect

style.map("TButton", foreground=[('pressed', 'red'), ('active', 'blue')], background=[('active', 'lightgrey')], relief=[('pressed', 'sunken'), ('!pressed', 'raised')])

4.7 Customising the Notebook Tabs

The ttk.Notebook widget (tabs) has its own style components:

style.configure("TNotebook.Tab", font=("Arial", 10, "bold"), padding=[10, 5]) style.map("TNotebook.Tab", background=[('selected', 'lightblue'), ('active', 'lightyellow')])

4.8 When to Use .configure() vs .map()

5. Key Difference 3: The Widget Set – 12 Corresponding + 6 New

Ttk provides 18 widgets in total:

5.1 The 12 Corresponding Widgets

Ttk Widget Classic Equivalent Notes
ttk.Button tk.Button Themed, no bg/fg support.
ttk.Label tk.Label Themed, no direct colour options.
ttk.Entry tk.Entry Themed.
ttk.Frame tk.Frame Themed.
ttk.LabelFrame tk.LabelFrame Themed.
ttk.Checkbutton tk.Checkbutton Themed.
ttk.Radiobutton tk.Radiobutton Themed.
ttk.Scale tk.Scale Themed.
ttk.Scrollbar tk.Scrollbar Themed.
ttk.Listbox No direct equivalent – see Treeview. Ttk does not have a direct Listbox; use Treeview for lists.
ttk.Menu No direct equivalent Use classic tk.Menu with Ttk.
ttk.PanedWindow No direct equivalent Use classic tk.PanedWindow or the one in Ttk (some themes support it).

5.2 The 6 Brand New Ttk Widgets (Deep Dive)

These are the real power‑ups of Ttk:

5.2.1 Combobox – Dropdown with Editing

A Combobox combines an Entry with a dropdown list. The user can either type a value or select from the list.

combo = ttk.Combobox(root, values=["Apple", "Banana", "Cherry"], state="readonly") combo.pack() combo.set("Banana") # Set default selected = combo.get()

5.2.2 Notebook – Tabbed Interface

A Notebook creates a tabbed container where each tab holds a different frame.

notebook = ttk.Notebook(root) tab1 = ttk.Frame(notebook) tab2 = ttk.Frame(notebook) notebook.add(tab1, text="Tab 1") notebook.add(tab2, text="Tab 2") notebook.pack(fill=tk.BOTH, expand=True) # Add content to tabs ttk.Label(tab1, text="Content of Tab 1").pack() ttk.Label(tab2, text="Content of Tab 2").pack() # Select a tab programmatically notebook.select(tab1) # or notebook.select(0) for index

5.2.3 Progressbar – Visual Progress Indicator

A Progressbar shows the progress of a long‑running operation.

progress = ttk.Progressbar(root, orient=tk.HORIZONTAL, length=200, mode='determinate') progress.pack() # Update progress progress['value'] = 50 # 0 to 100 # Indeterminate mode (for unknown duration) progress.config(mode='indeterminate') progress.start(50) # Starts animation (step every 50ms) progress.stop() # Stops animation

5.2.4 Separator – A Visual Divider

A Separator is a simple horizontal or vertical line used to group UI elements.

ttk.Separator(root, orient=tk.HORIZONTAL).pack(fill=tk.X, padx=10, pady=10)

5.2.5 Sizegrip – Resizing Handle

A Sizegrip is a small triangular handle (usually at the bottom‑right corner) that allows the user to resize the window.

ttk.Sizegrip(root).pack(side=tk.BOTTOM, anchor=tk.SE)

Note: The window must be resizable (root.resizable(True, True)) for it to work.

5.2.6 Treeview – Multi‑Column List / Tree

A Treeview is a powerful widget that can display hierarchical data (like a file explorer) or a simple multi‑column table (like a spreadsheet). It replaces Listbox and Canvas in some cases.

Basic Table Example:

tree = ttk.Treeview(root, columns=('ID', 'Name', 'Age'), show='headings') tree.heading('ID', text='ID') tree.heading('Name', text='Name') tree.heading('Age', text='Age') tree.insert('', tk.END, values=(1, 'Alice', 30)) tree.insert('', tk.END, values=(2, 'Bob', 25)) tree.pack() # Get selected item def on_select(event): selected = tree.selection() if selected: item = tree.item(selected[0]) print(item['values']) tree.bind('<<TreeviewSelect>>', on_select)

Tree Example (Hierarchical):

tree = ttk.Treeview(root) tree.insert('', tk.END, text='Fruits', iid='fruits') tree.insert('fruits', tk.END, text='Apple') tree.insert('fruits', tk.END, text='Banana') tree.pack()

Scrolling a Treeview:

scroll = ttk.Scrollbar(root, orient=tk.VERTICAL, command=tree.yview) tree.configure(yscrollcommand=scroll.set) scroll.pack(side=tk.RIGHT, fill=tk.Y)

6. Key Difference 4: State Management

6.1 The state() Method

Ttk widgets have a .state() method that allows you to set or query the widget's state. States are strings like 'disabled', 'pressed', 'selected', 'active', 'focus', etc.

Setting states:

button.state(['disabled']) # Disable the button button.state(['!disabled']) # Enable the button button.state(['pressed']) # Simulate pressing

Querying states:

if 'disabled' in button.state(): print("Button is disabled")

6.2 The instate() Method

.instate(states, callback) checks if the widget is in a specific state and optionally calls a callback.

def on_button_state_change(): print("Button is now pressed") button.instate(['pressed'], on_button_state_change)

6.3 Using States with .map()

As shown earlier, .map() uses states to change appearance dynamically.

7. Using Ttk Together with Classic Tk – The Hybrid Approach

You can mix classic Tk widgets and Ttk widgets in the same application. They are fully compatible because both are built on the same Tk framework.

Best practice:

Example of a Hybrid Layout

import tkinter as tk from tkinter import ttk root = tk.Tk() root.title("Hybrid App") # Ttk widgets for modern look main_frame = ttk.Frame(root, padding=10) main_frame.pack(fill=tk.BOTH, expand=True) ttk.Label(main_frame, text="Name:").grid(row=0, column=0, sticky='w') ttk.Entry(main_frame).grid(row=0, column=1, sticky='ew') # Classic Canvas for drawing canvas = tk.Canvas(main_frame, width=200, height=100, bg='white') canvas.grid(row=1, column=0, columnspan=2, pady=10) canvas.create_oval(50, 25, 150, 75, fill='red') ttk.Button(main_frame, text="Submit").grid(row=2, column=0, columnspan=2) root.mainloop()

Warning: Do not mix geometry managers (pack, grid, place) in the same container, regardless of whether they hold Ttk or classic widgets. The golden rule still applies.

8. Choosing the Right Tool – Decision Guide

Scenario Recommendation
Building a modern desktop application for end‑users. Use Ttk for all supported widgets.
Need precise colour control (e.g., a custom colour‑coded dashboard). Use classic Tk for those specific widgets, or use Ttk with Style if the theme supports it.
Need drawing (lines, shapes, images). Use classic Canvas.
Need rich text editing. Use classic Text.
Need a simple list. Use ttk.Treeview (or tk.Listbox if you prefer).
Need drop‑down menus (Menu bar). Use tk.Menu (Ttk has no replacement).
Need a tabbed interface. Use ttk.Notebook – it's excellent.
Need a progress bar. Use ttk.Progressbar.
Need a multi‑column table. Use ttk.Treeview.
Need pop‑up windows. Use tk.Toplevel (no Ttk equivalent).

9. Complete Example: Modern Contact Manager using Ttk

Let's combine everything into a single, cohesive application that showcases Ttk's power.

import tkinter as tk from tkinter import ttk, messagebox class ContactManager: def __init__(self, root): self.root = root self.root.title("Contact Manager") self.root.geometry("600x400") # Style self.style = ttk.Style() self.style.theme_use('clam') self.style.configure("TNotebook.Tab", padding=[10, 5]) self.style.map("TButton", foreground=[('active', 'blue')]) # Main notebook self.notebook = ttk.Notebook(root) self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) # Tab 1: Contact List self.list_tab = ttk.Frame(self.notebook) self.notebook.add(self.list_tab, text="Contacts") self.build_list_tab() # Tab 2: Add Contact self.add_tab = ttk.Frame(self.notebook) self.notebook.add(self.add_tab, text="Add Contact") self.build_add_tab() # Bind tab change event self.notebook.bind("<<NotebookTabChanged>>", self.on_tab_change) def build_list_tab(self): # Treeview for contacts columns = ('ID', 'Name', 'Phone', 'Email') self.tree = ttk.Treeview(self.list_tab, columns=columns, show='headings') for col in columns: self.tree.heading(col, text=col) self.tree.column(col, width=100) self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) # Scrollbar scroll = ttk.Scrollbar(self.list_tab, orient=tk.VERTICAL, command=self.tree.yview) self.tree.configure(yscrollcommand=scroll.set) scroll.pack(side=tk.RIGHT, fill=tk.Y) # Load sample data sample = [(1, 'Alice', '123-456', 'alice@mail.com'), (2, 'Bob', '789-012', 'bob@mail.com')] for item in sample: self.tree.insert('', tk.END, values=item) # Bind double-click to view details self.tree.bind("<Double-1>", self.view_contact) def build_add_tab(self): # Form using grid fields = ['Name', 'Phone', 'Email'] self.entries = {} for i, field in enumerate(fields): ttk.Label(self.add_tab, text=f"{field}:").grid(row=i, column=0, sticky='e', padx=5, pady=5) entry = ttk.Entry(self.add_tab, width=30) entry.grid(row=i, column=1, padx=5, pady=5) self.entries[field.lower()] = entry # Buttons btn_frame = ttk.Frame(self.add_tab) btn_frame.grid(row=len(fields), column=0, columnspan=2, pady=10) ttk.Button(btn_frame, text="Save", command=self.save_contact).pack(side=tk.LEFT, padx=5) ttk.Button(btn_frame, text="Clear", command=self.clear_form).pack(side=tk.LEFT, padx=5) def save_contact(self): name = self.entries['name'].get() phone = self.entries['phone'].get() email = self.entries['email'].get() if not name: messagebox.showerror("Error", "Name is required!") return # Insert into tree last_id = len(self.tree.get_children()) + 1 self.tree.insert('', tk.END, values=(last_id, name, phone, email)) messagebox.showinfo("Success", "Contact added!") self.clear_form() def clear_form(self): for entry in self.entries.values(): entry.delete(0, tk.END) def view_contact(self, event): selected = self.tree.selection() if selected: values = self.tree.item(selected[0])['values'] messagebox.showinfo("Contact Details", f"ID: {values[0]}\nName: {values[1]}\nPhone: {values[2]}\nEmail: {values[3]}") def on_tab_change(self, event): # Refresh when switching to list tab if self.notebook.index(self.notebook.select()) == 0: # Could reload data here pass if __name__ == "__main__": root = tk.Tk() app = ContactManager(root) root.mainloop()

10. Check Your Understanding (Quizzes)

Quiz 1: Multiple Choice

  1. Which import statement is used to access the themed Tkinter widgets?
AnswerB – `from tkinter import ttk` is the standard.
  1. Which method is used to change the appearance of all Ttk buttons globally?
AnswerB – `style.configure()` is correct.
  1. Which widget would you use to create a tabbed interface?
AnswerB – `ttk.Notebook` provides tabs.
  1. Which Ttk widget is the best replacement for a tk.Listbox when you need multiple columns?
AnswerB – `ttk.Treeview` supports multiple columns.
  1. How do you make a Ttk button appear "pressed" programmatically?
AnswerB – `.state(['pressed'])` sets the state.

Quiz 2: True or False

  1. True / False: Ttk widgets support the bg and fg options directly in their constructors.
AnswerFalse – Ttk does not support direct `bg`/`fg`; you must use `Style`.
  1. True / False: The .map() method on a ttk.Style object is used to define styles that change based on the widget's state.
AnswerTrue – `.map()` is for state‑dependent styles.
  1. True / False: You cannot mix classic Tk widgets and Ttk widgets in the same window.
AnswerFalse – You can mix them freely.
  1. True / False: The ttk.Progressbar has both 'determinate' and 'indeterminate' modes.
AnswerTrue – Both modes are supported.
  1. True / False: The ttk.Sizegrip allows the user to resize the window when placed at the bottom‑right corner.
AnswerTrue – That is exactly its purpose.

Quiz 3: Fill in the Blanks

  1. To switch the current theme, you use style.______('clam').
Answertheme_use
  1. The Ttk widget for a dropdown list with a text entry field is ttk.________.
AnswerCombobox
  1. The method to add a new tab to a ttk.Notebook is .______(child, text="...").
Answeradd
  1. To get the selected item from a ttk.Treeview, you use .______().
Answerselection
  1. The state that represents the mouse hovering over a widget is '______'.
Answeractive

11. Hands‑On Lab (In‑Class Exercises)

Exercise 1: Theme Switcher

Create a window with a ttk.Combobox listing all available themes, a ttk.Button labelled "Apply", and a selection of Ttk widgets (Button, Label, Entry, Progressbar). When "Apply" is clicked, change the theme to the selected one and show a messagebox confirming the change.

Sample Solution Outline - `style = ttk.Style()`; `combo = ttk.Combobox(values=style.theme_names())`. - `apply_theme()`: `style.theme_use(combo.get())`; show messagebox.

Exercise 2: Tabbed Dashboard

Build a ttk.Notebook with three tabs:

Sample Solution Outline - Create notebook, add frames for each tab. - Tab1: progress bar, start button that schedules increments with `after`. - Tab2: treeview with columns and sample data. - Tab3: combobox with colours, entry, and submit button.

Exercise 3: Styling Challenge

Create a custom style for TButton that:

Sample Solution ```python style.configure("Custom.TButton", font=("Arial", 14, "bold"), foreground="green") style.map("Custom.TButton", foreground=[('active', 'blue'), ('pressed', 'red')]) btn1 = ttk.Button(root, text="Button 1", style="Custom.TButton") btn2 = ttk.Button(root, text="Button 2", style="Custom.TButton") btn3 = ttk.Button(root, text="Button 3", style="Custom.TButton") ```

Exercise 4: Hybrid Application

Build a small drawing tool that uses:

Sample Solution Outline - Use Ttk frames and buttons, classic Canvas. - Bind mouse events to draw. - Separator between canvas and button panel.

12. Homework Assignment

Objective

Demonstrate your mastery of Ttk by building a modern task management application that leverages themes, styling, and new Ttk widgets.

Part A: Task Manager Application (25 points)

Build a task manager with the following features:

Core Requirements:

Styling Requirements:

Theme Switching:

Persistence (Bonus +5):

Sample Solution Outline - Main window with notebook, optionmenu for themes. - Treeview with columns, insert sample or loaded data. - Progressbar updates based on count of done tasks. - Buttons for mark done, delete. - Add task tab with entry, combobox, checkbutton, save/clear. - Styling as specified. - Theme switcher uses `style.theme_use`. - Persistence: on close, dump treeview data to JSON; on start, load.

Part B: Code Analysis – Fix the Styling Bug (10 points)

The following code attempts to style a Ttk button but contains three logical errors. Identify each error, explain the problem, and provide the corrected code.

import tkinter as tk from tkinter import ttk root = tk.Tk() style = ttk.Style() style.theme_use('clam') # Error 1: Setting bg directly on Ttk button btn = ttk.Button(root, text="Click Me", bg="yellow", fg="blue") btn.pack() # Error 2: Incorrect style name style.configure("TButton", font=("Arial", 12), background="yellow") style.map("Custom.TButton", foreground=[('active', 'red')]) # Error 3: Trying to map a style that doesn't exist for the button root.mainloop()
Answers 1. Passing `bg` and `fg` to Ttk button – Ttk does not accept these; they are ignored. Use `style.configure` instead. 2. `style.configure("TButton", ...)` – this changes the global button style, but the button created uses default style; that's fine, but if we want a custom style, we should use a different name. 3. `style.map("Custom.TButton", ...)` – this maps a style that has not been configured; the button does not use "Custom.TButton" style. Need to set `style="Custom.TButton"` on the button, and also configure that style.

Corrected code:

import tkinter as tk from tkinter import ttk root = tk.Tk() style = ttk.Style() style.theme_use('clam') style.configure("Custom.TButton", font=("Arial", 12), background="yellow") # background may be ignored style.map("Custom.TButton", foreground=[('active', 'red')]) btn = ttk.Button(root, text="Click Me", style="Custom.TButton") btn.pack() root.mainloop()

Part C: Research – ttk.Style.layout() (5 points)

Research the .layout() method of the ttk.Style class. Write a short explanation (5‑7 sentences) of what it does, why it is considered advanced, and provide a simple example that changes the layout of a TButton (e.g., moving the label to the right of the image). Hint: Look up the default layout structure.

Sample Answer The `.layout()` method allows you to define the internal structure (the "layout") of a widget, specifying which sub‑elements (like `'Button.padding'`, `'Button.label'`, `'Button.focus'`) are used and how they are positioned relative to each other. It is considered advanced because it requires knowledge of the widget's internal element names and the Ttk layout engine. For example, to move the label to the right of the image in a button, you would redefine the layout to place `'Button.image'` to the left of `'Button.label'`. The default layout for `TButton` can be inspected with `style.layout('TButton')`, and you can modify it by passing a layout specification.

Part D: Reflection – The Ttk Advantage (5 points)

Write a short essay (6‑8 sentences) explaining why Ttk is considered a significant improvement over classic Tkinter widgets. Address:

Sample Answer Ttk represents a major improvement over classic Tkinter because it provides a consistent, native look across different operating systems, making applications feel professional and integrated. By separating appearance (via `ttk.Style`) from the widget's functionality, Ttk allows developers to change the entire look of an application with a single theme change, without touching individual widgets. This decoupling also makes it easier to maintain and customise the UI. Furthermore, Ttk introduces new widgets like `Notebook`, `Treeview`, and `Progressbar` that are not available in classic Tk, greatly expanding the toolkit's capabilities. Finally, the state‑based styling via `.map()` enables dynamic visual feedback, such as hover and pressed states, which are essential for modern user interfaces. Overall, Ttk empowers developers to build more polished and cross‑platform consistent applications with less effort.

Part E: Extra Challenge – Treeview with Context Menu (Bonus +5)

In the "All Tasks" tab, add a right‑click context menu (tk.Menu) on the Treeview that provides options: "Mark as Done", "Delete Task", and "View Details". The "View Details" option should open a Toplevel window showing all details of the selected task in a formatted way.

Sample Solution Outline - Bind `` to show a popup menu. - The menu items call functions that operate on the currently selected tree item. - "View Details" creates a Toplevel with labels showing the task's values.

13. Summary of Key Terms (Glossary)

Term Definition
Ttk (Themed Tkinter) A submodule of Tkinter that provides themed, platform‑native widgets.
Theme A collection of styles that define the appearance of all Ttk widgets.
ttk.Style A class used to configure, map, and define custom styles for Ttk widgets.
Style Name The identifier for a widget's style (e.g., "TButton", "Success.TButton").
.configure() Method on Style that sets the base appearance of a widget class.
.map() Method on Style that defines appearance changes based on widget states.
State A condition of a widget (e.g., 'disabled', 'active', 'pressed').
Combobox A Ttk widget combining an Entry and a dropdown list.
Notebook A Ttk widget for tabbed containers.
Progressbar A Ttk widget for showing progress (determinate or indeterminate).
Treeview A Ttk widget for displaying hierarchical or tabular data.
Hybrid Application An application that uses both classic Tk widgets and Ttk widgets.

14. Further Resources for Self‑Study

This tutorial is designed to take approximately 3 hours of study, lab work, and homework. Mastering Ttk is the final step in becoming a proficient Tkinter developer—your applications will now look professional, modern, and native on every platform.

Previous | Tutorial index | | Next: none