Icons, Themes and Resources
The visual identity of a GTK app comes from three layers: the icons (the named images the widgets display, resolved through the system icon theme), the theme (the widget style - the colors, the borders, the widget metrics - applied by the toolkit from the active theme), and the resources (the gtkrc style properties and the application's own resource overrides). The app's job is mostly to use the right icon names and to let the theme do the styling, with the resource layer as the escape for the deliberate override. This chapter covers the icon system (the IconTheme, the named lookup, the Stock items), the themes (the active theme, the gtkrc, the style properties), and the resource patterns (the application icon, the themed image, the override).
The Icon System: Named Lookup
import gtk
# the icon theme - the system's icon set, resolved by name:
theme = gtk.icon_theme_get_default()
# the named lookup - the app asks for a name, the theme
# resolves it to the actual image (at the requested size):
icon = theme.lookup_icon("document-open", 24,
gtk.ICON_LOOKUP_FORCE_SIZE)
print(icon) # the found icon, or None
# the existence check - the graceful fallback:
if theme.has_icon("my-custom-icon"):
img = gtk.Image.new_from_icon_name("my-custom-icon",
gtk.ICON_SIZE_LARGE_TOOLBAR)
else:
img = gtk.Image.new_from_icon_name("document-new",
gtk.ICON_SIZE_LARGE_TOOLBAR)
# the icon sizes: the named size constants
# ICON_SIZE_MENU (16), ICON_SIZE_SMALL_TOOLBAR (24),
# ICON_SIZE_LARGE_TOOLBAR (32), ICON_SIZE_DND (32),
# ICON_SIZE_DIALOG (48)
# the Image widget takes the name + the size: the theme
# resolves it at render time, so the icon follows the theme
The icon system is the named lookup: the app references an icon by its theme name (document-open, edit-copy, folder), and the IconTheme resolves the name to the actual image at the requested size, from the active icon theme (the distribution's icon set). The gtk.Image.new_from_icon_name(name, size) is the standard construction - the image widget holds the name and the size, and resolves the actual pixbuf at render time, which is what makes the icon follow the theme (a theme change re-resolves the same name to the new theme's image). The size is the named constant (the ICON_SIZE_MENU for the menu, the ICON_SIZE_LARGE_TOOLBAR for the toolbar), and the has_icon check is the graceful fallback - the custom icon that may not be in the theme, with the standard name as the substitute. The discipline is the named reference: the app never loads an image file for a standard action, it uses the theme name, and the theme is what makes the app look like the rest of the desktop. The IconTheme's lookup_icon is the lower-level form (the direct resolution, the ICON_LOOKUP_FORCE_SIZE flag), and the Image widget is the high-level form the app uses.
Stock Items: The Standard Actions
import gtk
# the stock items - the named standard actions with a
# canonical icon + label + (optionally) a stock accelerator:
# the stock id is the name; the toolkit provides the rest
button = gtk.Button()
button.set_image(gtk.Image.new_from_stock(
gtk.STOCK_OPEN, gtk.ICON_SIZE_BUTTON))
button.set_label("Open")
# the stock constants cover the common actions:
# STOCK_NEW, STOCK_OPEN, STOCK_SAVE, STOCK_SAVE_AS,
# STOCK_QUIT, STOCK_CUT, STOCK_COPY, STOCK_PASTE,
# STOCK_FIND, STOCK_PRINT, STOCK_APPLY, STOCK_CANCEL,
# STOCK_OK, STOCK_YES, STOCK_NO, STOCK_CLOSE, ...
# the stock in the menu and the toolbar (the menus chapter
# used the stock ToolButton): the stock id gives the item
# the icon and the label in one argument
item = gtk.ImageMenuItem(gtk.STOCK_OPEN)
item.connect("activate", on_open)
# the stock label helper: the formatted "Open" with the
# mnemonic and the accelerator the stock defines
# the stock set is the THEME'S canonical presentation of the
# standard actions - using it keeps the app in step with
# the desktop's vocabulary
The stock items are the named standard actions - the STOCK_OPEN, STOCK_SAVE, the clipboard set - each with the toolkit's canonical icon, label, and (where defined) the standard accelerator. The stock id is the one argument that gives the widget the icon and the label together: the Image.new_from_stock for the image, the ImageMenuItem(STOCK_OPEN) for the menu item, the ToolButton(stock_id=...) for the toolbar (the menus chapter's construction). The value is the consistency: the app's Open button uses the same icon and label the file manager's Open uses, because both reference the same stock id, and the theme is what renders it. The stock set is the desktop's shared vocabulary for the common actions, and using it is the low-effort, high-consistency path for those actions - the custom actions (the app-specific commands) are the ones that use the icon name plus the explicit label instead. The stock constants are the standard set, and the STOCK_ names are the stable references the app codes against.
Themes: The Widget Style
import gtk
# the active theme - the toolkit applies it to every widget:
# the colors, the borders, the widget metrics, the font
# the app does NOT set the theme's colors; the theme does
# the theme is chosen by the desktop (the GNOME settings),
# and the app inherits it - the widget style is the theme's
# the style properties - the app CAN read the theme's values
# (for the custom drawing that should match the theme):
style = widget.get_style()
print(style.bg[gtk.STATE_NORMAL]) # the background RGB
print(style.fg[gtk.STATE_NORMAL]) # the foreground
print(style.font) # the theme's font
# the custom widget that matches the theme: read the style's
# colors and use them in the Cairo draw, instead of hard-coding
def themed_expose(self, event):
cr = self.window.cairo_create()
st = self.get_style()
bg = st.bg[gtk.STATE_NORMAL]
cr.set_source_rgb(bg.red / 65535.0, bg.green / 65535.0,
bg.blue / 65535.0)
cr.paint()
return False
The theme is the widget style - the colors, the borders, the widget metrics, the font - that the toolkit applies to every widget from the active theme (the one the desktop is configured to use). The app's default relationship to the theme is to inherit it: the widgets are styled by the theme, and the app does not set the theme's colors, which is what makes the app look native on the desktop it runs on. The get_style() is the read side: the widget's GdkStyle exposes the theme's values for the states (the STATE_NORMAL, the STATE_ACTIVE, the STATE_SELECTED), and the custom widget that should match the theme reads those values and uses them in its Cairo draw instead of hard-coding the colors - the bg and the fg per state, the font for the text. The pattern is the one the custom-widget chapter's thermometer can adopt: the themed colors from the style, so the custom widget's palette follows the desktop's theme, and the theme change re-resolves the style the next draw reads. The theme is the layer the app mostly inherits and occasionally reads, and the get_style is the bridge for the custom drawing that should stay in step.
Resources: The gtkrc and the Overrides
import gtk
# the application icon - the window's icon (the taskbar,
# the window list): the icon NAME, resolved by the theme
window.set_icon_name("text-editor")
# the resource file - the gtkrc: the style overrides
# the desktop loads the theme's gtkrc; the app can load an
# additional one for its own overrides:
gtk.rc_parse("/path/to/app-gtkrc")
# the app-gtkrc content (the style syntax):
# style "app-font" { font_name = "Monospace 11" }
# widget "*editor*" style "app-font"
# style "app-padding" { xspacing = 4, yspacing = 4 }
# widget "*notepad*" style "app-padding"
# the programmatic style - the widget's style property:
# the font on a specific widget (the code form of the gtkrc):
widget.modify_font(pango.FontDescription("Monospace 11"))
# the color override on a specific widget:
widget.modify_bg(gtk.STATE_NORMAL, gdk.color_parse("#f0f0f0"))
# the resources are the ESCAPE layer: the theme is the default,
# the gtkrc and the modify calls are the deliberate overrides -
# used sparingly, for the app-specific styling the theme
# does not provide
The resource layer is the override: the gtkrc (the style file the desktop loads for the theme, and the app can load an additional one with rc_parse for its own rules) and the programmatic modify calls (the modify_font, the modify_bg on a specific widget). The gtkrc syntax is the style rules (the style block defining the properties, the widget rule applying the style to the matching widgets), and the modify calls are the per-widget code form. The window's set_icon_name is the application icon (the taskbar, the window list), resolved by the theme from the name. The discipline is the one the paragraph states: the theme is the default the app inherits, and the resources are the deliberate overrides - the app-specific font for the editor, the padding for the app's widgets - used sparingly, because an override that fights the theme is the app that looks out of place on the desktop. The layering is the theme (the desktop's style), the app's gtkrc (the app's rules), and the modify calls (the per-widget code), in that precedence order.
The Working Rules
- Reference icons by theme name, not file path. The
new_from_icon_nameresolves through the active theme at render time, so the icon follows the theme change - the file-path image is the exception (the app's own art), not the rule. - Use the stock ids for the standard actions. The
STOCK_OPEN, the clipboard set - the stock id is the one argument for the canonical icon and label, and it keeps the app in step with the desktop's vocabulary. - Inherit the theme; read it only for the custom drawing. The widgets are styled by the theme by default, and the
get_styleis the bridge for the custom widget that should match the theme's colors and font. - The resources are the escape, used sparingly. The
gtkrcand themodifycalls are the deliberate overrides for the app-specific styling - an override that fights the theme is the app that looks out of place. - Set the application icon by name. The
set_icon_nameon the window gives the taskbar and the window list the app's icon, resolved by the theme - the identity the user sees outside the app's own windows.
Summary
The visual identity comes from three layers. The icons are the named lookup - the app references the theme name (document-open, the stock id), the IconTheme resolves it at the requested size, and the Image.new_from_icon_name holds the name so the icon follows the theme; the stock items are the canonical standard actions (the one STOCK_ id for the icon and the label). The theme is the widget style the toolkit applies from the active desktop theme - the app inherits it by default, and the get_style is the read the custom drawing uses to match the theme's colors and font. The resources are the override layer - the gtkrc style file (the rc_parse, the style and widget rules) and the per-widget modify calls - used sparingly for the app-specific styling, because the theme is the default and the override that fights it is the app that looks out of place. The discipline across the three: name the icons, use the stock for the standard, inherit the theme, and override deliberately.