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:
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
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.
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.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), "paint", callback=self.paintGlyphsCallback ) # done creating the dialog - open the window self.w.open() 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) return # 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 return # if the program gets here: we have selected glyph(s)! # loop over all selected glyphs for glyphName in f.selection: # print glyph name print(glyphName) # done! (print a blank line) print() def paintGlyphsCallback(self, sender): '''Paint all selected glyphs.''' f = CurrentFont() if f is None: print('please open a font first!') return if not len(f.selection): print('please select one or more glyphs first!') return 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 f[glyphName].performUndo() # open the dialog ToolDemo()
In RoboFont 1.8, use