Although Toga presents a single interface to the end user, there are three internal layers that make up every widget. They are:
The Interface layer
The Implementation layer
The Native layer
The interface layer is the public, documented interface for each widget. Following Toga’s design philosophy, these widgets reflect high-level design concepts, rather than specific common widgets. It forms the public API for creating apps, windows, widgets, and so on.
The interface layer is responsible for validation of any API inputs, and storage of any persistent values retained by a widget. That storage may be supplemented or replaced by storage on the underlying native widget (or widgets), depending on the capabilities of that widget.
The interface layer is also responsible for storing style and layout-related attributes of the widget.
The interface layer is defined in the
The implementation layer is the platform-specific representation of each
widget. Each platform that Toga supports has its own implementation layer,
named after the widget toolkit that the implementation layer is wrapping –
toga-cocoa for macOS (Cocoa being the name of the underlying macOS widget
toga-gtk for Linux (using the GTK+ toolkit); and so on. The
implementation provides a private, internal API that the interface layer can
use to create the widgets described by the interface layer.
The API exposed by the implementation layer is different to that exposed by the interface layer and is not intended for end-user consumption. It is a utility API, servicing the requirements of the interface layer.
Every widget in the implementation layer corresponds to exactly one widget in the interface layer. However, the reverse will not always be true. Some widgets defined by the interface layer are not available on all platforms.
An interface widget obtains its implementation when it is constructed, using
the platform factory. Each platform provides a factory implementation. When a
Toga application starts, it guesses its platform based on the value of
sys.platform, and uses that factory to create implementation-layer widgets.
If you have an interface layer widget, the implementation widget can be
obtained using the
_impl attribute of that widget.
The lowest layer of Toga is the native layer. The native layer represents the widgets required by the widget toolkit of your system. These are accessed using whatever bridging library or Python-native API is available on the implementation platform. This layer is usually provided by system-level APIs, not by Toga itself.
Most implementation widgets will have a single native widget. However, when a platform doesn’t expose a single widget that meets the requirements of the Toga interface specification, the implementation layer will use multiple native widgets to provide the required functionality.
In this case, the implementation must provide a single “container” widget that represents the overall geometry of the combined native widgets. This widget is called the “primary” native widget. When there’s only one native widget, the native widget is the primary native widget.
If you have an implementation widget, the interface widget can be obtained
interface attribute, and the primary native widget using the
If you have a native widget, the interface widget can be obtained using the
interface attribute, and the implementation widget using the
Here’s how Toga’s three-layer API works on the Button widget.
toga.Buttonis defined in
core/src/toga/widgets/button.py. This defines the public interface for the Button widget, describing (amongst other things) that there is an
on_clickevent handler on a Button. It expects that there will be an implementation, but doesn’t care which implementation is provided.
toga-gtk.widgets.Buttonis defined in
gtk/src/toga-gtk/widgets/button.py. This defines the Button at the implementation layer. It describes how to create a button on GTK, and how to connect the GTK
clickedsignal to the
Gtk.Buttonis the native GTK-Python widget API that implements buttons on GTK.
This three layered approach allows us to change the implementation of
without changing the public API that end-users rely upon. For example, we
could switch out
to provide a macOS implementation of the Button without altering the API that
end-users use to construct buttons.
The layered approach is especially useful with more complex widgets. Every platform provides a Button widget, but other widgets are more complex. For example, macOS doesn’t provide a native DetailedList view, so it must be constructed out of a scroll view, a table view, and a collection of other pieces. The three layered architecture hides this complexity - the API exposed to developers is a single (interface layer) widget; the complexity of the implementation only matters to the maintainers of Toga.
Lastly, the layered approach provides a testing benefit. In addition to the Cocoa, GTK, and other platform implementations, there is a “dummy” implementation. This implementation satisfies all the API requirements of a Toga implementation layer, but without actually performing any graphical operations. This dummy API can be used to test code using the Toga interface layer.