This example shows a very simple tool with a dialog.

The dialog contains two buttons, and each button triggers an action:

  • the fist button prints the names of the selected glyphs
  • the second button paints the glyph cells of the selected glyphs

This example is useful to show some common coding patterns:

  1. Callbacks

    A callback is a function that is called by another function which takes the first function as a parameter. Usually, a callback is a function that is called when something happens. That something can be called an event in programmer-speak. (source)

    In our example, the event is the clicking of a button. When that event is triggered, the dialog will run the specified function (the callback).

    Callbacks are used extensively in vanilla.

  2. Defensive programming

    The tool does something to the selected glyphs in the current font. But what if no glyph is selected, or there is no font open? A well-written tool should handle such situations elegantly, avoiding errors and providing informative messages to the user.

    Notice how, in the code below, the program exits the function if these two conditions – a font is open, and at least one glyph is selected – are not met.

  3. Prepare Undo & Perform Undo

    If a tool makes changes to user data, it should also make it possible for the user to revert these changes if necessary.

    In RoboFont, this is accomplished by recording the state of the data before and after the change is made – look for glyph.prepareUndo() and glyph.performUndo() in the code below.

from vanilla import *

class ToolDemo(object):

    def __init__(self):
        # first, create a floating window
        # (different than Window, FloatingWindow stays of top of other windows)
        self.w = FloatingWindow((123, 70), "myTool")

        # second, define some variables to use in the UI layout
        x, y = 10, 10
        padding = 10
        buttonHeight = 20

        # add a button for printing the selected glyphs
        self.w.printButton = Button(
                (x, y, -padding, buttonHeight), # position & size
                "print", # button label
                callback=self.printGlyphsCallback # button callback

        # update the y-position before placing the next button
        y += buttonHeight + padding

        # add another button for paiting the selected glyphs
        self.w.paintButton = Button(
                (x, y, -padding, buttonHeight),

        # done creating the dialog - open the window

    def printGlyphsCallback(self, sender):
        '''Print the names of all selected glyphs.'''

        # get the current font
        f = CurrentFont()

        # if there is no current font:
        if f is None:
            # print a message to the Output Window
            print('please open a font first!')
            # and exit the function (rest of the code will not be executed)

        # if the program gets here: we have a font!

        # if no glyphs are selected:
        if not len(f.selection):
            # print a message
            print('please select one or more glyphs first!')
            # and exit the function

        # if the program gets here: we have selected glyph(s)!

        # loop over all selected glyphs
        for glyphName in f.selection:
            # print glyph name

        # done! (print a blank line)

    def paintGlyphsCallback(self, sender):
        '''Paint all selected glyphs.'''

        f = CurrentFont()

        if f is None:
            print('please open a font first!')

        if not len(f.selection):
            print('please select one or more glyphs first!')

        for glyphName in f.selection:
            # save undo state
            f[glyphName].prepareUndo('painting glyph')
            # paint glyph
            f[glyphName].markColor = 1, 0, 0, 0.35
            # end undo state

# open the dialog

In RoboFont 1.8, use glyph.mark instead of glyph.markColor.

Last edited on 17/10/2017