The Move to GTK 3 and PyGObject
PyGTK bound GTK 2, and GTK 3 was the toolkit's next major version - the client-side decorations, the improved rendering, the multi-monitor and the touch support - with a new binding generation to match: PyGObject (the gi introspection bindings) replacing PyGTK. The migration from import gtk to from gi.repository import Gtk is systematic: the class names move to the Gtk namespace, the signals and the main loop carry over unchanged, and a defined set of APIs changed (the removed gtk.misc, the get_style to the CSS, the stock deprecation). This chapter maps the migration - the import and the namespace, what carried over, the specific API changes, the gtk.pygtk compatibility layer, and the decision of when an app migrates.
The Import and the Namespace
# the PyGTK (GTK 2) form - what this tutorial has used:
import gtk
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
button = gtk.Button(label="Quit")
window.connect("delete-event", on_delete)
gtk.main()
# the PyGObject (GTK 3) form - the migration target:
# the gi (gobject-introspection) repository, the Gtk module
from gi.repository import Gtk
window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL)
button = Gtk.Button(label="Quit")
window.connect("delete-event", on_delete)
Gtk.main()
# the mapping is systematic:
# import gtk -> from gi.repository import Gtk
# gtk.Window -> Gtk.Window
# gtk.Button, gtk.Label, ... -> Gtk.Button, Gtk.Label, ...
# gtk.STOCK_OPEN -> Gtk.STOCK_OPEN (deprecated
# in GTK 3, see below)
# gtk.main / gtk.main_quit -> Gtk.main / Gtk.main_quit
# the signals, the connect, the
# response ids, the main loop -> UNCHANGED
# the version pin - the app declares the GTK version it binds:
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
# the require_version is BEFORE the import: it selects the
# typelib (the 3.0 bindings) the import resolves against
The migration's first step is the import: import gtk (PyGTK, the GTK 2 binding) becomes from gi.repository import Gtk (PyGObject, the introspection binding), with the gi.require_version("Gtk", "3.0") before the import to select the GTK 3 typelib. The class mapping is systematic and mechanical: every gtk.ClassName is a Gtk.ClassName - the Window, the Button, the Label, the containers, the dialogs - and the constants move with them (the WINDOW_TOPLEVEL to the Gtk.WindowType.TOPLEVEL enum form, the response ids to the Gtk.ResponseType). What carries over unchanged is the architecture the whole tutorial built on: the signals and the connect, the main loop (Gtk.main/Gtk.main_quit), the delete-event contract, the response ids, the idle_add marshaling (now GLib.idle_add). The migration is the namespace move plus a defined set of API changes, not a rewrite of the app's structure - which is the point of the chapter: the PyGTK app's architecture is the PyGObject app's architecture, and the diff is the spelling.
What Carried Over Unchanged
from gi.repository import Gtk
# UNCHANGED across the migration - the architecture:
# 1. the widget tree and the containers (the box, the grid,
# the notebook, the frame, the scrolled window) - the
# same classes, the same pack/attach API
# 2. the signals: the connect, the handler signature
# (widget first, then the args), the user_data, the
# handler id, the disconnect, the stop_emission
# 3. the event contract: the -event signals, the GdkEvent,
# the True/False return (handled / not handled)
# 4. the main loop: Gtk.main / Gtk.main_quit, the
# GLib.idle_add / GLib.timeout_add marshaling
# 5. the dialogs: the Dialog base, the add_button, the
# response signal, the run/destroy pattern
# 6. the tree model: the ListStore/TreeStore, the TreeView,
# the renderers, the selection - the same API
# 7. the drawing: the Cairo context, the expose handler,
# the queue_draw - the same vocabulary
# the glib namespace: the PyGTK "gobject.idle_add" is
# the PyGObject "GLib.idle_add" - the module renamed,
# the function the same
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import GLib
GLib.idle_add(update, arg) # the same marshal
The list is the reassurance: the migration changes the spelling, not the architecture. The widget tree and the containers carry over with the same classes and the same pack/attach API. The signal system is identical - the connect, the handler signature (the widget first, then the arguments, then the user data), the handler id and the disconnect, the stop_emission, and the -event signals with their GdkEvent payload and the True/False return contract. The main loop is the same (Gtk.main/Gtk.main_quit), and the marshaling is the same functions in the renamed module (gobject.idle_add to GLib.idle_add). The dialogs, the tree model and view, and the Cairo drawing all carry over with the same API. The consequence is the one the chapter holds: a PyGTK app's structure - the skeleton, the signals, the main loop, the containers, the models - is valid PyGObject structure, and the migration is the mechanical namespace move plus the API changes in the next section, not a redesign of the application.
The API Changes: What Actually Moved
# the defined set of changes in the GTK 2 -> GTK 3 migration:
# 1. the gtk.misc and the gtk.Requisition: the size request
# moved to the widget's requisition; the gtk_misc functions
# (gtk_misc_parse_string, etc.) are gone
# 2. the stock items: DEPRECATED in GTK 3 (removed in 3.10
# from the default theme's rendering) - the stock id still
# works in early 3.x but the app should move to the icon
# name + the explicit label:
# Gtk.Button(stock_id=Gtk.STOCK_OPEN) (the 2.x form)
# ->
# b = Gtk.Button(label="Open")
# b.set_image(Gtk.Image.new_from_icon_name(
# "document-open", Gtk.IconSize.BUTTON))
# 3. the get_style / the gtkrc: the widget styling moved to
# CSS - the modify_bg / modify_font are deprecated, the
# GtkCssProvider is the GTK 3 styling:
# provider = Gtk.CssProvider()
# provider.load_from_data(b"bytes { background: #f0f0f0; }")
# Gtk.StyleContext.add_provider_for_screen(
# Gdk.Screen.get_default(), provider,
# Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
# 4. the gdk.Window: the widget.window is now a GdkWindow
# with the cairo via get_window().get_surface() in places
# 5. the client-side decorations: the header bar (the
# GtkHeaderBar) is the GTK 3 window chrome - the title
# and the buttons in the content, the CSD option
The changes are the defined set, and they are where the mechanical migration meets the deliberate decisions. The gtk.misc and its Requisition helpers are gone (the size request is the widget's requisition). The stock items are deprecated in GTK 3 (the rendering removed from the default theme in 3.10) - the stock id works in the early 3.x, but the migration target is the icon name plus the explicit label (the set_image with the new_from_icon_name plus the set_label), which is the icons chapter's named-lookup pattern made the standard. The styling moved to CSS - the modify_bg/modify_font are deprecated, and the GtkCssProvider (the load_from_data, the add_provider_for_screen with the priority) is the GTK 3 resource layer, replacing the gtkrc the icons chapter described. The GdkWindow access changed in places (the get_window for the surface), and the client-side decorations (the GtkHeaderBar) are the GTK 3's window chrome option. The pattern across the changes: the removed or deprecated API has a named replacement (the icon name for the stock, the CSS for the gtkrc), and the migration is the move to the replacement, which is usually the cleaner form the toolkit was moving toward.
The Compatibility Layer and the Decision
# the gtk.pygtk compatibility: GTK 3's pygobject did NOT
# provide the "import gtk" name - the migration is explicit
# (there is no drop-in "import gtk" for GTK 3; the gi import
# is the form)
# the decision: when does a PyGTK app migrate?
# - the app is maintained and the target desktops moved to
# GTK 3 (the GNOME 3 era): migrate - the GTK 2 libraries
# are leaving the distros, and the app's toolkit support
# ends with them
# - the app is stable, unmaintained, and the GTK 2 libraries
# are present on its targets: it can STAY on PyGTK/GTK 2
# (the 2.24 series is still shipped on many systems) -
# the migration is a project, not an emergency
# - the new app: it starts on PyGObject/GTK 3 (or GTK 4) -
# there is no reason to start a new app on PyGTK
# the migration is incremental for the large app: the import
# and the namespace in one pass (the mechanical), the stock
# and the styling in the next (the deliberate), the CSD
# (the header bar) as the option the app adopts or not
The decision is the one the chapter closes on. There is no drop-in import gtk for GTK 3 - the gi import is the form, and the migration is explicit. The question is when: the maintained app on the GTK 3 desktops migrates, because the GTK 2 libraries are leaving the distributions and the app's toolkit support ends with them; the stable, unmaintained app on targets that still ship GTK 2 can stay on PyGTK (the 2.24 series is still present on many systems), where the migration is a project rather than an emergency; and the new app starts on PyGObject (GTK 3 or the GTK 4 the next chapter's stack points to), because there is no reason to start new on the maintenance binding. The migration is incremental for the large app: the import and the namespace in the mechanical pass, the stock-to-icon and the gtkrc-to-CSS in the deliberate pass, and the client-side decorations as the option the app adopts. The through-line is the one the whole tutorial holds: the architecture is stable across the migration, and the binding change is the spelling - which is what makes the PyGTK app's investment in the GTK model carry into the modern stack.
Summary
The move to GTK 3 and PyGObject is the binding generation after PyGTK: the import is from gi.repository import Gtk (the gi.require_version("Gtk", "3.0") before it), the class mapping is the systematic gtk.Class to Gtk.Class with the constants in the enum form, and the architecture carries over unchanged - the widget tree and the containers, the signals and the connect and the -event contract, the main loop and the GLib.idle_add marshaling, the dialogs, the tree model, the Cairo drawing. The API changes are the defined set: the gtk.misc gone, the stock items deprecated (the icon name plus the label is the target), the gtkrc styling replaced by the GtkCssProvider, the GdkWindow access shifted, and the GtkHeaderBar as the client-side-decoration option. The decision is the maintained app on the GTK 3 desktops migrates (the GTK 2 libraries are leaving), the stable app on GTK 2 targets can stay, and the new app starts on PyGObject - and the through-line is that the migration is the spelling, not the structure, so the PyGTK app's GTK-model investment carries into the modern stack.