2025, Oct 06 19:00

Demystifying ttk.Widget vs tkinter.Widget: how inheritance and fully qualified names really work

Learn why ttk.Widget inherits from tkinter.Widget without circularity. We explain the base class, namespaces, and how to subclass right in Python's tkinter/ttk

Reading the tkinter source can be confusing at first glance, especially when two classes share the same name. A common stumbling block is seeing ttk define class Widget(tkinter.Widget) and thinking it somehow extends itself. It doesn’t. Here’s what actually happens and why it’s designed that way.

Where the confusion comes from

The ttk module imports the tkinter package and then declares a Widget that subclasses tkinter.Widget. If you scan only the ttk module, it may look like there’s no other Widget in sight and that ttk.Widget is inheriting from itself. The key detail is that tkinter itself defines a base Widget class inside its package’s __init__.py, and ttk builds on top of that.

Minimal code that raises the question

The structure that triggers the confusion can be reduced to the following pattern:

import tkinter as ui

class Panel(ui.Widget):
    """Base class for themed widgets."""
    pass

At a glance it might look circular. It isn’t, because ui.Widget refers to a class that already lives in the tkinter package.

What’s actually going on

Inside the tkinter package, a base widget class is defined in __init__.py. Conceptually, it looks like this:

class BaseItem(CoreBase, PackMixin, PlaceMixin, GridMixin):
    pass

Its purpose is described as:

Internal class. Base class for a widget which can be positioned with the geometry managers Pack, Place or Grid.

The ttk layer then subclasses that base class. In other words, ttk.Widget inherits from tkinter.Widget, not from itself. This is why ttk is typically imported via from tkinter import ttk: ttk is built on top of tkinter and extends its widgets, particularly their styling capabilities.

The intended inheritance

The inheritance relationship in ttk can be sketched like this:

import tkinter as ui

class StyledPanel(ui.Widget):
    """Base class for themed widgets."""

    def __init__(self, parent, kind, opts=None):
        parent = init_container(parent)
        ui.Widget.__init__(self, parent, kind, kw=opts)

The logic mirrors the original: import the tkinter package, derive from its Widget, normalize the parent, then invoke the base class initializer.

Solution: read the fully qualified name

There is no bug to fix here—only a naming overlap to recognize. The key is the fully qualified name. tkinter.Widget refers to the class defined in the tkinter package’s __init__.py. ttk.Widget is a distinct class that subclasses it. They share a name but live in different modules, so there is no circular inheritance.

Why this matters

Understanding this layering helps when navigating the standard library and when subclassing yourself. You can create your own class that derives from tkinter.Widget, from ttk.Widget, or from neither, depending on what behavior and styling foundations you need. It also underlines a practical readability lesson: reusing the same class name across modules is valid, but it can slow down code comprehension if you’re not paying attention to module qualifiers.

Takeaways

When you see class X(Y.Z) in the tkinter and ttk code, read the qualifier carefully. ttk builds on tkinter by subclassing its base Widget from inside the tkinter package. The design is intentional and common in layered libraries. If you adopt similar patterns in your own code, prefer clear, fully qualified references, and consider distinct class names to make hierarchies easier to follow.

The article is based on a question from StackOverflow by Darien Marks and an answer by JRiggles.