Icon Theme Api

Preface:

This document outlines how Editra's icon theme system works and how create a custom theme for Editra. The theme system has been designed to provide an interface that emphasizes ease of creation, distribution, and usage. This means that creating a theme requires little to no programming, distribution is only a single file, and installation/usage is completely automated from Editra's interface.

The theme system described in this document is available for all versions of Editra > 0.2.0




Themes in Editra

Themes are part of Editra's plugin framework in order to provide an easy to customize and easy to install object. Typically programs that are theme-able require downloading and untarring dozens of files/directories and copying them to some configuration folder for the program to make use of them. Editra simplifies this whole process by making themes be a single binary file that requires no unpacking, and the whole installation system is also automated through the PluginManager. This means that themes are easily installed/updated/uninstalled with very little effort required from the user.

Since themes are part of the plugin framework the layout for creating a theme is very similar to that of a plugin. The layout described below outlines a typical setup for creating a theme and the components that are required. The description of actions and layout in this document will follow what is used by the current themes (Tango, Nuovo) and will allow you to create a complete theme without requiring any programming, if you wish to make customizations above and beyond what is shown here that is also possible.

Folders are indicated by [] and files are written plainly with each level of indentation indicating a child directory

[mytheme]
        |
        setup.py
        [mytheme]
                |
                __init__.py
                [pixmaps]
                        |
                        [menu]
                        [mime]
                        [toolbar]

With this layout now in mind we will walk step by step through what each of the items in the layout is and how to put it all together.




The Setup Script

Included below is the full source for a setup.py file that can be used with a theme that is laid out as described in the above section. All that requires is changing all instances of MyTheme/mytheme to the name of what you want to call your theme and change and fill out any other of the entries about the plugin and yourself.

# -*- coding: utf-8 -*-
# Setup script to build the MyTheme Theme. To build the plugin
# just run 'python setup.py bdist_egg' and an egg will be built and put into 
# a directory called dist in the same folder as this script.
"""
 
"""
__author__ = "Your Name"
 
import sys
try:
    from setuptools import setup
except ImportError:
    print "You must have setup tools installed in order to build this plugin"
    setup = None
 
if setup is not None:
    setup(
        name='MyTheme',
        version='0.1',
        description=__doc__,
        author=__author__,
        author_email="your@email.com",
        license="GPLv2",
        url="http://editra.org",
        platforms=["Linux", "OS X", "Windows"],
        packages=['mytheme'],
        package_data={'mytheme' : ['pixmaps/menu/*.png', 'pixmaps/toolbar/*.png',
                                   'pixmaps/mime/*.png',]},
        entry_points='''
        [Editra.plugins]
        MyTheme = mytheme:MyThemeTheme
        '''
        )


The Theme File

The theme file is a simple python script called __init__.py. This file is responsible for providing the icons from your theme to Editra. How the theme file is written is completely up to you but included below are the required elements of the interface as well as a complete theme file that can be used that only requires changing the name of theme in the file.

Theme Interface:

class ThemeI(plugin.Interface):
    """Interface for defining an icon theme in Editra
    When a icon theme is active Editra's ArtProvider will ask the active
    theme that implements this interface to give it a bitmap. The requests
    for bitmaps will be numerical ID values that come from ed_glob. These
    ID's are associated with different objects in the interface. The names
    are descriptive of the object they represent, for reference however
    see the implementation of the two main themes (Tango and Nuovo).
 
    @see: L{ed_glob.py}
    @see: L{syntax.synglob.py}
 
    """
    def GetName(self):
        """Return the name of this theme. This is used to identify the
        theme when the provider looks for resources based on user preferences
 
        @return: name string
 
        """
 
    def GetMenuBitmap(self, bmp_id):
        """Get the menu bitmap associated with the object id
        If this theme does not have a resource to provide for this
        object return a wx.NullBitmap.
 
        @return: 16x16 pixel bitmap
 
        """
        return wx.NullBitmap
 
    def GetFileBitmap(self, bmp_id):
        """Get the filetype bitmap associated with the object id, the
        valid object ids are defined in the module syntax.synglob and
        are used to request images for menu's and page tabs. The theme
        implimenting this interface should at least be able to
        provide an image for plain text files and return that for any
        unmapped types.
 
        If this theme does not have a resource to provide for this
        object return a wx.NullBitmap.
 
        @return: 16x16 pixel bitmap
 
        """
        return wx.NullBitmap
 
    def GetToolbarBitmap(self, bmp_id):
        """Get the toolbar bitmap associated with the object id. The
        toolbar icons must be returned as a 32x32 pixel bitmap any
        scaling that is needed will be handled by the art provider that
        requests the resource.
 
        If this theme does not have a resource to provide for this
        object return a wx.NullBitmap.
 
        @return: 32x32 pixel bitmap
 
        """
        return wx.NullBitmap


A Complete Theme File

Included below is a complete theme file that can be used if your theme uses the default icon file names defined in ed_theme.py (more on this later). The only change that is again needed is to change all instances of MyTheme to match the name of your theme.

###############################################################################
# Name: __init__.py                                                           #
# Purpose: Provides the MyTheme based icon theme for Editra                   #
# Author: Cody Precord <cprecord@editra.org>                                  #
# Copyright: (c) 2007 Cody Precord <cprecord@editra.org>                      #
# Licence: wxWindows Licence                                                  #
###############################################################################
"""MyTheme icon theme for Editra"""
__author__ = "Cody Precord"
__version__ = "0.1"
 
import wx
import os
import cStringIO
import ed_glob
import plugin
import ed_theme
import syntax.synglob as synglob
import syntax.syntax as syntax
 
#-----------------------------------------------------------------------------#
 
# Paths
MENU_PATH = os.path.join('pixmaps', 'menu') + os.sep
MIME_PATH = os.path.join('pixmaps', 'mime') + os.sep
TOOL_PATH = os.path.join('pixmaps', 'toolbar') + os.sep
 
#-----------------------------------------------------------------------------#
 
class MyThemeTheme(plugin.Plugin):
    """Represents the MyTheme Icon theme for Editra"""
    plugin.Implements(ed_theme.ThemeI)
 
    def __LoadBitmapData(self, path):
        """Loads image data into a bitmap, returns None if there is a failure"""
        try:
            data = __loader__.get_data(os.path.join(__path__[0], path))
        except IOError:
            pass
        else:
            bmp = wx.ImageFromStream(cStringIO.StringIO(data), wx.BITMAP_TYPE_PNG)
            return bmp.ConvertToBitmap()
 
    def GetName(self):
        return u'MyTheme'
 
    def GetMenuBitmap(self, bmp_id):
        if ed_theme.ART.has_key(bmp_id):
            path = MENU_PATH + ed_theme.ART[bmp_id]
            bmp = self.__LoadBitmapData(path)
            if bmp is not None:
                return bmp
        else:
            return self.GetFileBitmap(bmp_id)
 
        return wx.NullBitmap
 
    def GetFileBitmap(self, bmp_id):
        bmp = None
        if ed_theme.MIME_ART.has_key(bmp_id):
            path = MIME_PATH + ed_theme.MIME_ART[bmp_id]
            bmp = self.__LoadBitmapData(path)
            if bmp is not None:
                return bmp
 
        if bmp is None and bmp_id in syntax.SyntaxIds():
            # Fail back to plain text bitmap
            bkup = MIME_PATH + ed_theme.MIME_ART[synglob.ID_LANG_TXT]
            bmp = self.__LoadBitmapData(bkup)
            if bmp is not None:
                return bmp
 
        return wx.NullBitmap
 
    def GetToolbarBitmap(self, bmp_id):
        if ed_theme.ART.has_key(bmp_id):
            path = TOOL_PATH + ed_theme.ART[bmp_id]
            bmp = self.__LoadBitmapData(path)
            if bmp is not None:
                return bmp
 
        return wx.NullBitmap



The Icon Files

The icon files are looked up upon request by the theme file. The theme file shown in this document uses the default icon file names that are defined in ed_theme.py. These names can be added to and/or changed at runtime by your theme file but using the default names is the easiest method. Included below is a list of the default items that are available from ed_theme to reference when naming the icon files.


Menu and Toolbar Resources:

ART = { ed_glob.ID_ABOUT  : u'about.png',           # Used for About menu item
        ed_glob.ID_ADD_BM : u'bmark_add.png',       # Add Bookmark menu item
        ed_glob.ID_BIN_FILE : u'bin_file.png',      # Execute command/binary files icon
        ed_glob.ID_CDROM  : u'cdrom.png', 
        ed_glob.ID_CONTACT : u'mail.png',
        ed_glob.ID_COPY   : u'copy.png',            # Copy menu icon
        ed_glob.ID_COMPUTER : u'computer.png',
        ed_glob.ID_CUT    : u'cut.png',
        ed_glob.ID_DELETE : u'delete.png',
        ed_glob.ID_DOCPROP : u'doc_props.png',      # Prefdialog document button
        ed_glob.ID_DOWN   : u'down.png',
        ed_glob.ID_EXIT   : u'quit.png',
        ed_glob.ID_FILE   : u'file.png',
        ed_glob.ID_FIND   : u'find.png',
        ed_glob.ID_FIND_REPLACE : u'findr.png',
        ed_glob.ID_FLOPPY : u'floppy.png',
        ed_glob.ID_FOLDER : u'folder.png',
        ed_glob.ID_FONT   : u'font.png',
        ed_glob.ID_HARDDISK : u'harddisk.png',
        ed_glob.ID_HOMEPAGE : u'web.png',
        ed_glob.ID_HTML_GEN : u'html_gen.png',
        ed_glob.ID_KWHELPER : u'kw_help.png',
        ed_glob.ID_NEW    : u'new.png',
        ed_glob.ID_NEW_WINDOW: u'newwin.png',
        ed_glob.ID_NEXT_MARK : u'bmark_next.png',
        ed_glob.ID_OPEN    : u'open.png',
        ed_glob.ID_PACKAGE : u'package.png',
        ed_glob.ID_PASTE   : u'paste.png',
        ed_glob.ID_PLUGMGR : u'plugin.png',         # image to represent plugins
        ed_glob.ID_PRE_MARK : u'bmark_pre.png',
        ed_glob.ID_PREF    : u'pref.png',
        ed_glob.ID_PRINT   : u'print.png',
        ed_glob.ID_PRINT_PRE : u'printpre.png',
        ed_glob.ID_REDO    : u'redo.png',
        ed_glob.ID_RTF_GEN : u'rtf_gen.png',
        ed_glob.ID_SAVE    : u'save.png',
        ed_glob.ID_SAVEAS  : u'saveas.png',
        ed_glob.ID_STYLE_EDIT : u'style_edit.png',
        ed_glob.ID_TEX_GEN : u'tex_gen.png',
        ed_glob.ID_THEME  : u'theme.png',
        ed_glob.ID_UNDO   : u'undo.png',
        ed_glob.ID_UP     : u'up.png',
        ed_glob.ID_USB    : u'usb.png',
        ed_glob.ID_WEB    : u'web.png',             # Used for downloading/web related actions
        ed_glob.ID_ZOOM_IN : u'zoomi.png',
        ed_glob.ID_ZOOM_OUT : u'zoomo.png',
        ed_glob.ID_ZOOM_NORMAL : u'zoomd.png'
}

Filetype Resources:

MIME_ART = { synglob.ID_LANG_BASH : u'shell.png',
             synglob.ID_LANG_BOURNE : u'shell.png',
             synglob.ID_LANG_C : u'c.png',
             synglob.ID_LANG_CPP : u'cpp.png',
             synglob.ID_LANG_CSH : u'shell.png',
             synglob.ID_LANG_CSS : u'css.png',
             synglob.ID_LANG_HTML : u'html.png',
             synglob.ID_LANG_JAVA : u'java.png',
             synglob.ID_LANG_KSH : u'shell.png',
             synglob.ID_LANG_LATEX : u'tex.png',
             synglob.ID_LANG_MAKE : u'makefile.png',
             synglob.ID_LANG_PERL : u'perl.png',
             synglob.ID_LANG_PHP : u'php.png',
             synglob.ID_LANG_PYTHON : u'python.png',
             synglob.ID_LANG_RUBY : u'ruby.png',
             synglob.ID_LANG_TCL : u'tcl.png',
             synglob.ID_LANG_TEX : u'tex.png',
             synglob.ID_LANG_TXT : u'text.png',
             synglob.ID_LANG_XML : u'xml.png'
 }

As can be seen there are allot of resources and these are only a list of the most common ones, as mapping icons to any of the ID_* values defined in ed_glob.py is possible with the theme system and will enable it to automatically associate icons where necessary. The good news is that for a theme to be usable you only need to define and provide a small subset of items shown above.




This is a list of the required icon mappings that a theme needs to provide in order to keep the editor working smoothly.


Toolbar Icons:

Ideally a theme should define all of these toolbar icons. All toolbar icons must be 32x32 pixels in size and reside in the toolbar folder.

ed_glob.ID_COPY    : u'copy.png'        # Copy Text
ed_glob.ID_CUT     : u'cut.png'         # Cut Text
ed_glob.ID_DOCPROP : u'doc_props.png'   # Preferences Dialog Document button
ed_glob.ID_FIND    : u'find.png'        # Find button
ed_glob.ID_FIND_REPLACE : u'findr.png'  # Find and Replace button
ed_glob.ID_NEW     : u'new.png'         # New Document button
ed_glob.ID_OPEN    : u'open.png'        # Open Document (usually a folder opening image)
ed_glob.ID_PACKAGE : u'package.png'     # PluginManager Install button
ed_glob.ID_PASTE   : u'paste.png'       # Paste Text
ed_glob.ID_PREF    : u'pref.png'        # Pref Dialog General/PluginManager Config button
ed_glob.ID_PRINT   : u'print.png'       # Print button
ed_glob.ID_REDO    : u'redo.png'        # Redo button
ed_glob.ID_SAVE    : u'save.png'        # Save button
ed_glob.ID_THEME   : u'theme.png'       # Pref Dialog Appearance button
ed_glob.ID_UNDO    : u'undo.png'        # Undo button
ed_glob.ID_WEB     : u'web.png'         # Download/Update button

Menu Icons:

No menu icons are actually required but here is a list of some of the important ones that a good theme should provide as a minimum. All menu icons must be 16x16 pixels in size and reside in the menu folder.

ed_glob.ID_BIN_FILE : u'bin_file.png'   # Represent binary files
ed_glob.ID_CDROM    : u'cdrom.png'      # Cdrom image
ed_glob.ID_COMPUTER : u'computer.png'   # Computer image
ed_glob.ID_DOWN     : u'down.png'       # Down arrow
ed_glob.ID_FILE     : u'file.png'       # Generic file icon
ed_glob.ID_FLOPPY   : u'floppy.png'     # Floppy disk
ed_glob.ID_FOLDER   : u'folder.png'     # Generic folder icon
ed_glob.ID_HARDDISK : u'harddisk.png'   # Hardisk image
ed_glob.ID_OPEN     : u'open.png'       # Open folder icon
ed_glob.ID_PLUGMGR  : u'plugin.png'     # Plugin image
ed_glob.ID_PREF     : u'pref.png'       # Prefrences/config icon (used in many places)
ed_glob.ID_UP       : u'up.png'         # Up arrow
ed_glob.ID_USB      : u'usb.png'        # Removable device icon
ed_glob.ID_WEB      : u'web.png'        # Represent web related items (usually a globe)

Mime Icons:

Just as with the menu icons none of these are actually required but a good theme should be able to at least provide an icon to represent a plain text file as it is used as the default when a more specific one cannot be found. These icons must be 16x16 pixels in size and reside in the mime folder.

synglob.ID_LANG_TXT : u'text.png' # Plain Text File

Links: