### Install via setup script Source: https://robofont.com/documentation/tutorials/python/external-modules Execute a setup.py script in the terminal to install a package. ```bash python /someFolder/myPackage/setup.py ``` -------------------------------- ### install(showMessages=True) Source: https://robofont.com/documentation/reference/api/mojo/mojo-extensions Installs the extension into the environment. ```APIDOC ### install(showMessages=True) Installs the extension. #### Parameters - **showMessages** (boolean) - Optional - Set to false to disable installation messages. ``` -------------------------------- ### Local library path example Source: https://robofont.com/documentation/how-tos/overriding-embedded-libraries Example output showing a library path located in a custom local directory, indicating an override. ```text ['/my/parent/folder/fontTools/Lib/fontTools'] ``` -------------------------------- ### Example glif file path output Source: https://robofont.com/documentation/tutorials/scripts-glyph An example of the file path string returned by the script. ```text /myFolder/myFont.ufo/glyphs/one.glif ``` -------------------------------- ### Install packages with pip Source: https://robofont.com/documentation/tutorials/python/external-modules Install Python packages from PyPI by name or specific version constraints. ```bash pip install SomePackage # latest version pip install SomePackage==1.0.4 # specific version pip install 'SomePackage>=1.0.4' # minimum version ``` -------------------------------- ### Install ttfautohint via Homebrew Source: https://robofont.com/documentation/how-tos/hinting-ttfautohint Command to install the ttfautohint library on macOS using Homebrew. ```bash $ brew install ttfautohint ``` -------------------------------- ### Smart Set query examples Source: https://robofont.com/documentation/how-tos/mojo/smart-sets Examples of predicate strings used to filter glyphs based on attributes like name, width, and status. ```text "Name == 'a'" "Name in {'a', 'b'}" "Empty == 'True'" "Name contains 'a' AND Width < 300" "Name matches '[A-z]'" "Width in {120, 240, 360}") ``` -------------------------------- ### Install package from git repository Source: https://robofont.com/documentation/tutorials/python/external-modules Install a package directly from a remote git repository archive. ```bash pip install https://github.com/user/repository/archive/branch.zip ``` -------------------------------- ### Embedded library path example Source: https://robofont.com/documentation/how-tos/overriding-embedded-libraries Example output showing a library path located within the RoboFont application bundle. ```text ['/Applications/RoboFont.app/Contents/Resources/lib/python3.6/fontTools'] ``` -------------------------------- ### Representation output examples Source: https://robofont.com/documentation/topics/defcon-representations Example output showing the time difference between initial computation and cached retrieval. ```text # >>> elapsed: 0.0012180805206298828 # >>> [('Component', (20, 0, 559, 680)), ('Component', (158, 720, 421, 889))] ``` ```text # >>> elapsed: 0.00012874603271484375 # >>> [('Component', (20, 0, 559, 680)), ('Component', (158, 720, 421, 889))] ``` -------------------------------- ### Jekyll server output Source: https://robofont.com/documentation/how-tos/previewing-documentation-locally Example of the terminal output generated when the Jekyll server starts successfully. ```text Configuration file: /_code/robofont_com/_config.yml Source: /_code/robofont_com Destination: /_code/robofont_com/_site Incremental build: enabled Generating... done in 6.02 seconds. Auto-regeneration: enabled for '/_code/robofont_com' Configuration file: /_code/robofont_com/_config.yml Server address: http://127.0.0.1:4000/ Server running... press ctrl-c to stop. ``` -------------------------------- ### Test module installation Source: https://robofont.com/documentation/tutorials/python/external-modules Attempt to import a module to verify it is correctly installed and accessible in the current environment. ```python import myModule ``` -------------------------------- ### testInstall(showProgress=True, **environmentOptions) Source: https://robofont.com/documentation/reference/api/fontParts/rfont Tests the installation of the font on the local machine with optional environment configurations. ```APIDOC ## testInstall(showProgress=True, **environmentOptions) ### Description Test install the font in the local machine. ### Parameters - **showProgress** (boolean) - Optional - Defaults to True. - **environmentOptions** (dict) - Optional - Configuration options including 'decompose', 'checkOutlines', 'autohint', 'releaseMode', 'glyphOrder', and 'layerName'. ``` -------------------------------- ### allExtensions() Source: https://robofont.com/documentation/reference/api/mojo/mojo-extensions Retrieves a list of all currently installed extensions. ```APIDOC ## allExtensions() ### Description Get a list of all installed extensions. ``` -------------------------------- ### Initialize a Git Repository Source: https://robofont.com/documentation/tutorials/using-git Run this command in the root folder of your project to start version control. ```bash git init ``` -------------------------------- ### loadAllExtensions() Source: https://robofont.com/documentation/reference/api/mojo/mojo-extensions Triggers the loading process for all installed extensions. ```APIDOC ## loadAllExtensions() ### Description Loads all installed extensions. This is used by RoboFont during startup. ``` -------------------------------- ### Install a tool in RoboFont Source: https://robofont.com/documentation/how-tos/interactive/simple-tool-example Use installTool to register a tool instance with the Glyph Editor toolbar. ```python from mojo.events import installTool installTool(MyTool()) ``` -------------------------------- ### Context management with the with statement Source: https://robofont.com/documentation/tutorials/python/control-flow The with statement simplifies setup and cleanup patterns using context managers. ```python with open('example.txt', 'r') as f: txt = f.read() ``` ```python # same as: f = open('example.txt', 'r') # setup try: txt = f.read() # action finally: f.close() # cleanup ``` -------------------------------- ### Console output example Source: https://robofont.com/documentation/tutorials/scripts-interpolation Expected console output after running the batch interpolation script. ```text generating instances... generating Light (0.25)... saving instances at /myFolder/MyFamily_Light.ufo... generating Regular (0.5)... saving instances at /myFolder/MyFamily_Regular.ufo... generating Bold (0.75)... saving instances at /myFolder/MyFamily_Bold.ufo... ...done. ``` -------------------------------- ### Open a URL in the Mojo Help Window Source: https://robofont.com/documentation/how-tos/mojo/help-window Initializes and displays a new HelpWindow instance pointing to the provided URL. ```python from mojo.UI import HelpWindow HelpWindow("http://robofont.com") ``` -------------------------------- ### Create a Window with Controls Source: https://robofont.com/documentation/reference/api/mojo/mojo-ui Demonstrates how to initialize a window and add controls by assigning them as attributes to the window instance. ```python from vanilla import Window, Button, TextBox class WindowDemo: def __init__(self): self.w = Window((200, 70), "Window Demo") self.w.myButton = Button((10, 10, -10, 20), "My Button") self.w.myTextBox = TextBox((10, 40, -10, 17), "My Text Box") self.w.open() WindowDemo() ``` -------------------------------- ### installNeededPackages(extensionName, packages) Source: https://robofont.com/documentation/reference/api/mojo/mojo-piptools Installs required Python packages for a specific extension. The function automatically checks if the package is already installed before attempting installation. ```APIDOC ## installNeededPackages(extensionName, packages) ### Description Installs packages needed by an extension. This function checks if the package is already installed and only performs the installation if it is missing. ### Arguments - **extensionName** (string) - The name of the extension. - **packages** (list) - A list of package dictionaries. ### Package Dictionary Structure - **packageName** (string) - Required. The name of the package on PIP. - **importName** (string) - Optional. The name of the module to be used during import if different from the package name. - **pipArgs** (list) - Optional. Additional arguments to be added to the installation command. ``` -------------------------------- ### StatusBar Implementation Example Source: https://robofont.com/documentation/reference/api/mojo/mojo-ui Demonstrates how to initialize a StatusBar and update its content using different display modes like fading and warnings. ```python from mojo.UI import StatusBar from vanilla import * class StatusBarExample(object): def __init__(self): self.w = Window((250, 300)) self.w.hello = Button((10, 10, -10, 22), "Hello", callback=self.helloCallback) self.w.helloFade = Button((10, 40, -10, 22), "Hello Fade", callback=self.helloFadeCallback) self.w.helloWarning = Button((10, 70, -10, 22), "Hello Warning", callback=self.helloWarningCallback) self.w.statusBar = StatusBar((0, -18, -0, -0)) self.w.open() def helloCallback(self, sender): self.w.statusBar.set(["hello"]) def helloFadeCallback(self, sender): self.w.statusBar.set(["hello Fade"], fadeOut=True) def helloWarningCallback(self, sender): self.w.statusBar.set(["hello Warning"], warning=True) StatusBarExample() ``` -------------------------------- ### View Notification Documentation Output Source: https://robofont.com/documentation/topics/observers Example output showing the notifications posted by the Contour object. ```text Help on class Contour in module defcon.objects.contour: class Contour(defcon.objects.base.BaseObject) | This object represents a contour and it contains a list of points. | | **This object posts the following notifications:** | | =============================== | Name | =============================== | Contour.Changed | Contour.WindingDirectionChanged | Contour.PointsChanged | Contour.IdentifierChanged | =============================== | | ... ``` -------------------------------- ### Manage selected guidelines Source: https://robofont.com/documentation/reference/api/fontParts/rfont Get or set selected guidelines using objects or indexes. ```python for guideline in font.selectedGuidelines: ... guideline.color = (1, 0, 0, 0.5) ``` ```python font.selectedGuidelines = someGuidelines ``` ```python font.selectedGuidelines = [0, 2] ``` -------------------------------- ### Creating a Window with Vanilla Source: https://robofont.com/documentation/topics/vanilla Demonstrates the basic structure of a Vanilla window containing a button and a text box. ```python from vanilla import * class WindowDemo: def __init__(self): self.w = Window((200, 70), "Window Demo") self.w.myButton = Button((10, 10, -10, 20), "My Button") self.w.myTextBox = TextBox((10, 40, -10, 17), "My Text Box") self.w.open() WindowDemo() ``` -------------------------------- ### mojo.UI.HelpWindow Source: https://robofont.com/documentation/how-tos/mojo/help-window Initializes and displays a new Help window pointing to the specified URL. ```APIDOC ## mojo.UI.HelpWindow(url) ### Description Opens a new Help window that acts as a simple HTML browser to display the content at the provided URL. ### Parameters - **url** (string) - Required - The web address to load in the Help window. ``` -------------------------------- ### Access pyftsubset help documentation Source: https://robofont.com/documentation/how-tos/generating-trial-fonts Displays the available command line options for the subsetter tool. ```bash pyftsubset --help ``` -------------------------------- ### Start the Jekyll local server Source: https://robofont.com/documentation/how-tos/previewing-documentation-locally Navigate to the repository directory and execute the serve command to initiate the local development environment. ```bash cd path/to/robofont_com/ bundle exec jekyll serve --incremental ``` -------------------------------- ### Create a welcome message start-up script Source: https://robofont.com/documentation/tutorials/setting-up-a-startup-script This script uses AppKit to synthesize speech, providing an audible welcome message when RoboFont launches. ```python import AppKit speaker = AppKit.NSSpeechSynthesizer.availableVoices()[7] voice = AppKit.NSSpeechSynthesizer.alloc().initWithVoice_(speaker) voice.startSpeakingString_("Welcome to RoboFont!") ``` -------------------------------- ### Location for .pth files Source: https://robofont.com/documentation/tutorials/python/external-modules Example path for saving a .pth file within the Python site-packages directory. ```text /Library/Python/3.9/site-packages/myModule.pth ``` -------------------------------- ### Define contour starting with off-curve point Source: https://robofont.com/documentation/reference/workspace/glyph-editor/contours Off-curve points lack a type attribute and can be placed at the start of a contour definition. ```xml ``` -------------------------------- ### Slice a list in Python Source: https://robofont.com/documentation/tutorials/python/lists Extract a sub-list using start and end indices. The start index is inclusive, while the end index is exclusive. ```python >>> list1 = [1, -20, 'hello', 4.347, 500, 'abcdefg'] >>> list1[2:4] # get a slice from 3nd to 5th items ``` ```python ['hello', 4.347] ``` -------------------------------- ### Initialize a CodeEditor control Source: https://robofont.com/documentation/reference/api/mojo/mojo-ui Demonstrates how to instantiate a CodeEditor within a vanilla Window and handle text entry callbacks. ```python from vanilla import Window from mojo.UI import CodeEditor class CodeEditorDemo(object): def __init__(self): self.w = Window((200, 200)) self.w.codeEditor = CodeEditor((10, 10, -10, -10), callback=self.codeEditorCallback, showLineNumbers=True) self.w.open() def codeEditorCallback(self, sender): print("text entry!", sender.get()) CodeEditorDemo() ``` -------------------------------- ### Initialize a Multi-Line View Source: https://robofont.com/documentation/how-tos/mojo/multiline-view Demonstrates how to embed a MultiLineView within a vanilla window and set its font and glyph content. ```python from mojo.UI import MultiLineView from vanilla import Window class MultiLineViewExample: def __init__(self, font): self.w = Window((600, 400), minSize=(300, 300)) self.w.lineView = MultiLineView( (0, 0, -0, -0), pointSize=30, selectionCallback=self.lineViewSelectionCallback) self.w.lineView.setFont(font) glyphs = [font[glyphName] for glyphName in font.glyphOrder] self.w.lineView.set(glyphs) self.w.open() def lineViewSelectionCallback(self, sender): print(sender.getSelectedGlyph()) MultiLineViewExample(CurrentFont()) ``` -------------------------------- ### hasTTFAutoHint Source: https://robofont.com/documentation/reference/api/mojo/mojo-compile Checks if ttfautohint is installed. ```APIDOC ## hasTTFAutoHint() ### Description Returns a boolean indicating if the ttfautohint tool is installed on the system. ``` -------------------------------- ### Initialize SpaceMatrix Controller Source: https://robofont.com/documentation/reference/api/mojo/mojo-ui Demonstrates setting up a MultiLineView with a SpaceMatrix to control glyph spacing parameters. ```python from mojo.UI import MultiLineView, SpaceMatrix from vanilla import Window, HorizontalLine class Controller: def __init__(self): self.w = Window((400, 400)) self.w.lineView = MultiLineView((0, 0, -0, -48), pointSize=72) self.w.hl = HorizontalLine((0, -(48+1), -0, 1)) self.w.matrix = SpaceMatrix((0, -50, -0, -0), callback=self._matrixCallback) f = CurrentFont() glyphs = [f["A"], f["a"]] self.w.lineView.setFont(f) self.w.lineView.set(glyphs) self.w.matrix.set(glyphs) self.w.open() def _matrixCallback(self, sender): self.w.lineView.update() Controller() ``` -------------------------------- ### Generate a font proof with DrawBot Source: https://robofont.com/documentation/how-tos/drawbot/proof-spacing This script installs the current font as a test version and renders character groups into a multi-page PDF. Ensure the font is installed locally via f.testInstall() before running the text rendering loop. ```python from string import ascii_lowercase, ascii_uppercase, digits, punctuation f = CurrentFont() fontName = '%s-%s' % (f.info.familyName, f.info.styleName) # install the font locally # f.testInstall() # settings MARGIN = 20 FONTSIZE = 14 LINEHEIGHT = 1.1 * FONTSIZE # make some test strings testStrings = [ ''.join(['%s%s%s ' % (c, c.join(list(ascii_lowercase)), c) for c in ascii_lowercase]), ''.join(['%s%s%s ' % (c, c.join(list(ascii_uppercase)), c) for c in ascii_uppercase]), ''.join(['%s%s%s ' % (c, c.join(list(punctuation)), c) for c in ascii_lowercase]), ''.join(['%s%s%s ' % (c, c.join(list(digits)), c) for c in digits]), ] # set text with test strings for txt in testStrings: # create new page newPage('A4Landscape') # set font if fontName in installedFonts(): font(fontName) else: print('font %s not installed' % fontName) # calculate pos/size x = y = MARGIN w = width() - MARGIN * 2 h = height() - MARGIN * 2 # set text sample fontSize(FONTSIZE) lineHeight(LINEHEIGHT) textBox(txt, (x, y, w, h)) ``` -------------------------------- ### Execute Hello World Expression Source: https://robofont.com/documentation/tutorials/python/terminal Demonstrates evaluating a string expression in the interactive shell. ```python >>> "hello world" ``` ```python 'hello world' ``` -------------------------------- ### Clone the repository Source: https://robofont.com/documentation/tutorials/editing-the-docs Initializes a local copy of the documentation repository for editing. ```bash cd /path/to/folder/ git clone git@gitlab.com:myusername/robofont_com.git ``` -------------------------------- ### installedFonts() Source: https://robofont.com/documentation/reference/api/mojo/mojo-drawingtools Returns a list of installed fonts. ```APIDOC ## installedFonts() ### Description Returns a list of all installed fonts. ``` -------------------------------- ### Importing Vanilla Source: https://robofont.com/documentation/topics/vanilla Initializes the Vanilla library for use in RoboFont scripts. ```python from vanilla import * ``` -------------------------------- ### glyph.width Source: https://robofont.com/documentation/reference/api/fontParts/rglyph Get or set the width of the glyph. ```APIDOC ## glyph.width ### Description The glyph's width as an integer or float. ### Usage ```python # Get width width = glyph.width # Set width glyph.width = 200 ``` ``` -------------------------------- ### Initialize a GlyphPreview in a Window Source: https://robofont.com/documentation/reference/api/mojo/mojo-glyphpreview Demonstrates how to embed a GlyphPreview component within a vanilla Window and set the glyph and selection points. ```python from vanilla import Window from mojo.glyphPreview import GlyphPreview class SimpleGlyphWindow: def __init__(self, glyph): self.w = Window((400, 400), glyph.name, minSize=(100, 100)) self.w.glyphPreview = GlyphPreview((0, 0, -0, -0)) self.w.glyphPreview.setGlyph(glyph) selection = [(p.x, p.y) for p in glyph.selectedPoints] self.w.glyphPreview.setSelection(selection) self.w.open() SimpleGlyphWindow(CurrentGlyph()) ``` -------------------------------- ### Accessing Vanilla Documentation Source: https://robofont.com/documentation/topics/vanilla Uses the built-in Python help function to display documentation for specific Vanilla objects. ```python from vanilla import Window help(Window) ``` -------------------------------- ### glyph.selected Source: https://robofont.com/documentation/reference/api/fontParts/rglyph Get or set the selection state of the glyph. ```APIDOC ## glyph.selected ### Description The object's selection state (boolean). ### Usage ```python # Get selection state is_selected = glyph.selected # Set selection state glyph.selected = True ``` ``` -------------------------------- ### Implement a Merz-based Glyph Visualizer with Mode Switching Source: https://robofont.com/documentation/topics/merz This example demonstrates a Subscriber that uses a MerzView to render glyph paths and baseline lines, with a button callback to toggle between light and dark color modes. ```python import ezui from mojo.subscriber import Subscriber, registerRoboFontSubscriber from mojo.roboFont import CurrentGlyph class GlyphVisualizer(Subscriber, ezui.WindowController): debug = True def build(self): self.glyph = CurrentGlyph() content = """ * VerticalStack @stack > * MerzView @merzView > (🌛) @button """ descriptionData = dict( merzView=dict( backgroundColor=(1, 1, 1, 1), delegate=self ), stack=dict( alignment="center" ), ) self.w = ezui.EZWindow( title="Glyph Visualizer", content=content, size=(self.glyph.width, self.glyph.font.info.unitsPerEm), descriptionData=descriptionData, controller=self ) merzView = self.w.getItem("merzView") container = merzView.getMerzContainer() # a layer for the glyph and the baseline self.backgroundLayer = container.appendBaseSublayer( size=(self.glyph.width, self.glyph.font.info.unitsPerEm), backgroundColor=(1, 1, 1, 1) ) self.glyphLayer = self.backgroundLayer.appendPathSublayer( position=(0, -self.glyph.font.info.descender) ) glyphPath = self.glyph.getRepresentation("merz.CGPath") self.glyphLayer.setPath(glyphPath) self.lineLayer = self.backgroundLayer.appendLineSublayer( startPoint=(0, -self.glyph.font.info.descender), endPoint=(self.glyph.width, -self.glyph.font.info.descender), strokeWidth=1, strokeColor=(1, 0, 0, 1) ) def started(self): self.w.open() def glyphEditorDidSetGlyph(self, info): self.glyph = info['glyph'] self.w.resize(self.glyph.width, self.glyph.font.info.unitsPerEm) glyphPath = self.glyph.getRepresentation("merz.CGPath") self.glyphLayer.setPath(glyphPath) self.backgroundLayer.setSize((self.glyph.width, self.glyph.font.info.unitsPerEm)) self.lineLayer.setEndPoint((0, -self.glyph.font.info.descender)) self.lineLayer.setEndPoint((self.glyph.width, -self.glyph.font.info.descender)) def _switchToDarkMode(self): self.glyphLayer.setFillColor((1, 1, 1, 1)) self.backgroundLayer.setBackgroundColor((0, 0, 0, 1)) def _switchToLightMode(self): self.glyphLayer.setFillColor((0, 0, 0, 1)) self.backgroundLayer.setBackgroundColor((1, 1, 1, 1)) def buttonCallback(self, sender): if sender.getTitle() == "🌛": self._switchToDarkMode() sender.setTitle('🌞') else: self._switchToLightMode() sender.setTitle('🌛') if __name__ == '__main__': registerRoboFontSubscriber(GlyphVisualizer) ``` -------------------------------- ### Test install a font via script Source: https://robofont.com/documentation/tutorials/using-test-install Use the testInstall method on a RFont object to make the font available system-wide. ```python myFont = CurrentFont() myFont.testInstall() ``` -------------------------------- ### Get a layer Source: https://robofont.com/documentation/reference/api/fontParts/rfont Retrieves a BaseLayer object by its name. ```python layer = font.getLayer("My Layer 2") ``` -------------------------------- ### selected Source: https://robofont.com/documentation/reference/api/fontParts/rbpoint Gets or sets the selection state of the object. ```APIDOC ## selected ### Description The object’s selection state. ### Example ```python obj.selected False obj.selected = True ``` ``` -------------------------------- ### getTestInstalledFonts() Source: https://robofont.com/documentation/reference/api/mojo/mojo-ui Retrieves a list of all fonts that are currently test installed. ```APIDOC ## getTestInstalledFonts() ### Description Returns all fonts which are test installed. ``` -------------------------------- ### Manage glyph height Source: https://robofont.com/documentation/reference/api/fontParts/rglyph Gets or sets the height of the glyph. ```python glyph.height 500 glyph.height = 200 ``` -------------------------------- ### Create an AccordionView with vanilla Source: https://robofont.com/documentation/how-tos/mojo/accordion-window Initializes a FloatingWindow containing an AccordionView with multiple panels defined by a list of dictionaries. ```python from mojo.UI import AccordionView from vanilla import * class AccordionViewExample: def __init__(self): self.w = FloatingWindow((200, 400)) self.firstPanel = TextEditor((10, 10, -10, -10)) self.secondPanel = List((0, 0, -0, -0), ["a", "b", "c"]) self.thirdPanel = Tabs((10, 10, -10, -10), ["1", "2", "3"]) self.fourthPanel = Group((0, 0, -0, -0)) self.fourthPanel.checkBox = CheckBox((10, 10, 100, 22), "CheckBox") self.fourthPanel.editText = EditText((10, 40, -10, 22)) descriptions = [ dict(label="first panel", view=self.firstPanel, size=200, collapsed=False, canResize=False), dict(label="second panel", view=self.secondPanel, minSize=100, size=140, collapsed=True, canResize=True), dict(label="third panel", view=self.thirdPanel, minSize=100, size=140, collapsed=True, canResize=False), dict(label="fourth panel", view=self.fourthPanel, size=140, collapsed=False, canResize=False) ] self.w.accordionView = AccordionView((0, 0, -0, -0), descriptions) self.w.open() AccordionViewExample() ``` -------------------------------- ### Create a Vanilla Button with Callback Source: https://robofont.com/documentation/topics/subscriber Demonstrates using a callback function with a Vanilla Button object to handle user interaction. ```python from vanilla import Window, Button class ButtonDemo: def __init__(self): self.w = Window((100, 40)) self.w.button = Button((10, 10, -10, 20), "A Button", callback=self.buttonCallback) self.w.open() def buttonCallback(self, sender): print("button hit!") ButtonDemo() ``` -------------------------------- ### get(groupName, default=None) Source: https://robofont.com/documentation/reference/api/fontParts/rgroups Retrieves the contents of a specific group. ```APIDOC ## get(groupName, default=None) ### Description Returns the contents of the named group as an immutable list of glyph names. Returns None if the group is not found. ### Parameters - **groupName** (string) - The name of the group to retrieve. - **default** (any) - The value to return if the group is not found. ### Usage ```python font.groups["myGroup"] ``` ``` -------------------------------- ### Implement Multiple Subscribers with WindowController Source: https://robofont.com/documentation/topics/subscriber A complete example demonstrating a tool palette that controls the appearance of glyph outlines across multiple editors using a custom subscriber event. ```python #!/usr/bin/env python3 # -------------------- # # Palette + Subscriber # # -------------------- # # -- Modules -- # import ezui from mojo.subscriber import Subscriber, WindowController, getRegisteredSubscriberEvents from mojo.subscriber import registerGlyphEditorSubscriber, unregisterGlyphEditorSubscriber from mojo.roboFont import OpenWindow from mojo.events import postEvent from mojo.subscriber import registerSubscriberEvent DEFAULT_KEY = 'com.developerName.SomeTool' class ToolPalette(WindowController): debug = True thickness = 5 def build(self): content = """ ----X---- 123 @slider """ descriptionData = dict( slider=dict( minValue=0, value=self.thickness, maxValue=25, stopOnTickMarks=True, tickMarks=6, continuous=False ) ) self.w = ezui.EZWindow( title="Tool", size=(200, "auto"), content=content, descriptionData=descriptionData, controller=self ) def started(self): self.w.open() Tool.controller = self registerGlyphEditorSubscriber(Tool) def destroy(self): unregisterGlyphEditorSubscriber(Tool) Tool.controller = None def sliderCallback(self, sender): self.thickness = sender.get() postEvent(f"{DEFAULT_KEY}.changed") class Tool(Subscriber): """ Tool can only ask for information to the palette """ debug = True controller = None def build(self): glyphEditor = self.getGlyphEditor() container = glyphEditor.extensionContainer(identifier=DEFAULT_KEY, location='background', clear=True) self.path = container.appendPathSublayer( fillColor=(0, 0, 0, 0), strokeColor=(1, 0, 0, 1), strokeWidth=self.controller.thickness if self.controller else 0, ) glyph = self.getGlyphEditor().getGlyph() self.path.setPath(glyph.getRepresentation("merz.CGPath")) def destroy(self): glyphEditor = self.getGlyphEditor() container = glyphEditor.extensionContainer(DEFAULT_KEY, location='background') container.clearSublayers() def glyphEditorGlyphDidChangeOutline(self, info): self.path.setPath(info['glyph'].getRepresentation("merz.CGPath")) def paletteDidChange(self, info): self.path.setStrokeWidth(self.controller.thickness) # -- Instructions -- # if __name__ == '__main__': eventName = f"{DEFAULT_KEY}.changed" # we register the subscriber event only if necessary if eventName not in getRegisteredSubscriberEvents(): registerSubscriberEvent( subscriberEventName=eventName, methodName="paletteDidChange", lowLevelEventNames=[eventName], dispatcher="roboFont", documentation="Send when the tool palette did change parameters.", delay=0, debug=True ) OpenWindow(ToolPalette) ``` -------------------------------- ### Register the subscriber Source: https://robofont.com/documentation/how-tos/subscriber/subscribing-to-current-glyph Registers the subscriber class to start receiving events. ```python registerCurrentGlyphSubscriber(CurrentGlyphWithUISubscriberDemo) ``` -------------------------------- ### Access and modify name Source: https://robofont.com/documentation/reference/api/fontParts/rglyph Get or set the glyph name as a string. ```python glyph.name "A" glyph.name = "A.alt" ``` -------------------------------- ### Window Initialization Source: https://robofont.com/documentation/reference/api/mojo/mojo-ui The Window class is initialized with a posSize tuple and various optional parameters to define its appearance and behavior. ```APIDOC ## Window(posSize, title, minSize, maxSize, textured, autosaveName, closable, miniaturizable, initiallyVisible, fullScreenMode, titleVisible, fullSizeContentView, screen) ### Description Creates a new window instance. Controls can be added to the window by setting them as attributes of the window instance. ### Parameters - **posSize** (tuple) - Required - (left, top, width, height) or (width, height) for automatic positioning. - **title** (string) - Optional - The title displayed in the window title bar. - **minSize** (tuple) - Optional - (width, height) minimum resize dimensions. - **maxSize** (tuple) - Optional - (width, height) maximum resize dimensions. - **textured** (boolean) - Optional - Whether the window has a textured appearance. - **autosaveName** (string) - Optional - Unique name for saving window state in preferences. - **closable** (boolean) - Optional - Whether the window has a close button. - **miniaturizable** (boolean) - Optional - Whether the window has a minimize button. - **initiallyVisible** (boolean) - Optional - Whether the window is visible upon creation (default: True). - **fullScreenMode** (string) - Optional - Full screen behavior: None, "primary", or "auxiliary". - **titleVisible** (boolean) - Optional - Whether the window title is displayed. - **fullSizeContentView** (boolean) - Optional - Whether the content view fills the entire window area. - **screen** (NSScreen) - Optional - The screen to draw the window on. ``` -------------------------------- ### Manage bottom margin Source: https://robofont.com/documentation/reference/api/fontParts/rglyph Gets or sets the bottom margin of the glyph. ```python glyph.bottomMargin 35 glyph.bottomMargin = 45 ``` -------------------------------- ### Get font path Source: https://robofont.com/documentation/reference/api/fontParts/rfont Retrieve the file path of the font object. ```python print font.path "/path/to/my/font.ufo" ``` -------------------------------- ### Initialize a Canvas Source: https://robofont.com/documentation/reference/api/mojo/mojo-canvas Demonstrates creating a window with a scrollable Canvas delegate for drawing operations. ```python from mojo.canvas import Canvas from mojo.drawingTools import * from vanilla import Window class ExampleWindow: def __init__(self): self.w = Window((400, 400), minSize=(200, 200)) self.w.canvas = Canvas((0, 0, -0, -0), delegate=self) self.w.open() def draw(self): rect(10, 10, 100, 100) ExampleWindow() ``` -------------------------------- ### get(pair, default=None) Source: https://robofont.com/documentation/reference/api/fontParts/rkerning Returns the value for a specific kerning pair. ```APIDOC ## get(pair, default=None) ### Description Returns the value for the kerning pair. Note that changes to the returned value are not reflected in the Kerning object; updates must be set explicitly. ### Parameters - **pair** (tuple) - Required - A tuple of two strings representing the kerning pair. - **default** (any) - Optional - The value to return if the pair is not found. ``` -------------------------------- ### Create a Glyph Preview Window Source: https://robofont.com/documentation/how-tos/mojo/glyph-preview Initializes a floating window containing a list of glyphs and a GlyphPreview area to display the selected glyph. ```python from vanilla import FloatingWindow, List from mojo.glyphPreview import GlyphPreview from mojo.roboFont import OpenWindow class GlyphPreviewExample: def __init__(self): # create a window self.w = FloatingWindow((400, 300), "Preview", minSize=(100, 100)) # add a list for all glyphs in font self.w.glyphs = List((10, 10, 100, -10), [], allowsMultipleSelection=False, selectionCallback=self.showPreviewCallback) # add a GlyphPreview to the window self.w.preview = GlyphPreview((100, 0, -0, -0)) # load glyphs from current font to list self.loadGlyphs(CurrentFont()) # open the window self.w.open() def loadGlyphs(self, font): if not font: print('to use this tool, please open a font first') return # load list of glyphs self.w.glyphs.set(font.glyphOrder) def setGlyph(self, glyph): # set the glyph in the GlyphPreview self.w.preview.setGlyph(glyph) def showPreviewCallback(self, sender): # get selected glyph selection = self.w.glyphs.getSelection() if not selection: return selectedGlyph = self.w.glyphs.get()[selection[0]] # set preview in preview f = CurrentFont() if f is not None: self.w.preview.setGlyph(f[selectedGlyph]) # open the window with OpenWindow, so it cannot be opened twice OpenWindow(GlyphPreviewExample) ``` -------------------------------- ### get(key, default=None) Source: https://robofont.com/documentation/reference/api/fontParts/rlib Retrieves the contents of a specific key from the Lib. ```APIDOC ## get(key, default=None) ### Description Returns the contents of the named key. Key is a string, and the returned values will either be a list of key contents or None if no key was found. ### Parameters - **key** (string) - Required - The name of the key to retrieve. - **default** (any) - Optional - The value to return if the key is not found. ``` -------------------------------- ### Get group contents Source: https://robofont.com/documentation/reference/api/fontParts/rgroups Retrieves the contents of a specific group by name. ```python font.groups["myGroup"] ("A", "B", "C") ``` -------------------------------- ### Run Vanilla test suite Source: https://robofont.com/documentation/topics/vanilla Executes the built-in test suite to display an overview of all available UI elements. ```python from vanilla.test.testAll import Test Test() ``` -------------------------------- ### Create a dictionary with items Source: https://robofont.com/documentation/tutorials/python/dictionaries Initialize a dictionary with predefined key/value pairs. ```python >>> myDict = { ... 'key 1' : 'some value', ... 'key 2' : 'another value', ... } ``` -------------------------------- ### testDeinstallFont(font) Source: https://robofont.com/documentation/reference/api/mojo/mojo-ui Deinstalls a specific font from the test installed fonts list. ```APIDOC ## testDeinstallFont(font) ### Description Deinstalls a provided font. ### Parameters - **font** (object) - Required - The font object to be deinstalled. ``` -------------------------------- ### Update pip Source: https://robofont.com/documentation/tutorials/python/external-modules Ensure the pip package installer is up-to-date using the terminal. ```bash pip install -U pip ``` -------------------------------- ### Create a Simplified Font Window with Python Source: https://robofont.com/documentation/tutorials/working-with-large-fonts Uses the Subscriber and WindowController patterns to display a list-based font overview, which is more performant for fonts with many glyphs. ```python from AppKit import NSURL, NSDocumentController from vanilla import Window, List from mojo.subscriber import Subscriber, WindowController from lib.doodleDocument import DoodleDocument from mojo.UI import OpenGlyphWindow, OpenSpaceCenter, OpenFontInfoSheet from mojo.roboFont import OpenFont class SimpleFontWindow(Subscriber, WindowController): def __init__(self, font): self._font = font self._canUpdateChangeCount = True super().__init__() def build(self): self.w = Window((250, 500), "SimpleFontWindow", minSize=(200, 300)) glyphs = sorted(font.keys()) self.w.glyphs = List((0, 0, -0, -0), glyphs, doubleClickCallback=self.openGlyph) toolbarItems = [ dict(itemIdentifier="spaceCenter", label="Space Center", imageNamed="toolbarSpaceCenterAlternate", callback=self.openSpaceCenter ), dict(itemIdentifier="fontInfo", label="Font Info", imageNamed="toolbarFontInfo", callback=self.openFontInfo ) ] self.w.addToolbar(toolbarIdentifier="SimpleToolbar", toolbarItems=toolbarItems) windowController = self.w.getNSWindowController() windowController.setShouldCloseDocument_(True) try: # RF >= 3.3 self._font.shallowDocument().addWindowController_(windowController) except AttributeError: if not font.path: return document = DoodleDocument.alloc().init() document.setFileURL_(NSURL.fileURLWithPath_(font.path)) dc = NSDocumentController.sharedDocumentController() dc.addDocument_(document) self._font.UIdocument().addWindowController_(windowController) self._font.addObserver(self, "fontChanged", "Font.Changed") def started(self): self.w.open() def openGlyph(self, sender): sel = sender.getSelection() if sel: i = sel[0] name = sender[i] self._canUpdateChangeCount = False OpenGlyphWindow(self._font[name]) self._canUpdateChangeCount = True def openSpaceCenter(self, sender): self._canUpdateChangeCount = False OpenSpaceCenter(self._font) self._canUpdateChangeCount = True def openFontInfo(self, sender): self._canUpdateChangeCount = False OpenFontInfoSheet(self._font, self.w) self._canUpdateChangeCount = True def fontDidChange(self, info): if self._canUpdateChangeCount: try: # RF >= 3.3 self._font.shallowDocument().updateChangeCount_(0) except Exception: self._font.UIdocument().updateChangeCount_(0) if __name__ == '__main__': fonts = OpenFont(showInterface=False) if not isinstance(fonts, list): fonts = [fonts] for font in fonts: SimpleFontWindow(font) ``` -------------------------------- ### ExtensionBundle Source: https://robofont.com/documentation/reference/api/mojo/mojo-extensions Class for reading, writing, and installing extensions based on the bundle specification. ```APIDOC ## class ExtensionBundle() ### Description An object to read, write and install extensions. ``` -------------------------------- ### Example Layers Structure Source: https://robofont.com/documentation/reference/workspace/layers-popover A representation of glyph attributes across different layers. ```text A (foreground) A (background) A (sketches) unicode: U+0041 unicode: None unicode: None color: red color: blue color: blue width: 400 width: 450 width: 350 ``` -------------------------------- ### Importing UI defaults Source: https://robofont.com/documentation/topics/building-extensions Use the mojo.UI module instead of lib.tools.defaults to ensure compatibility with the fontParts API. ```python # this has been deprecated: from lib.tools.defaults import getDefault # use this instead: from mojo.UI import getDefault ``` -------------------------------- ### Slice from the start of a list Source: https://robofont.com/documentation/tutorials/python/lists Omit the first index to slice from the beginning of the list. ```python >>> list1[:4] # same as: list1[0:4] ``` ```python [1, -20, 'hello', 4.347] ``` -------------------------------- ### moveTo(pt) Source: https://robofont.com/documentation/reference/api/mojo/mojo-drawingtools Moves the pen to a specific point to start a new contour. ```APIDOC ## moveTo(pt) ### Description Move to a point (x,y) to start a new contour. ### Parameters - **pt** (tuple) - Required - The target point (x, y). ``` -------------------------------- ### ShowHideWindow Class Source: https://robofont.com/documentation/reference/api/mojo/mojo-ui The ShowHideWindow class allows for the creation of windows. Controls are added as attributes to the window instance. The class supports configuration via parameters like posSize, title, minSize, maxSize, and visibility settings. ```APIDOC ## class ShowHideWindow() ### Description A window capable of containing controls. To add a control to a window, simply set it as an attribute of the window. ### Parameters - **posSize** (tuple) - Position and size of the window (left, top, width, height) or (width, height). - **title** (string) - The title to be set in the title bar of the window. - **minSize** (tuple) - Minimum size (width, height) the window can be resized to. - **maxSize** (tuple) - Maximum size (width, height) the window can be resized to. - **textured** (boolean) - Whether the window should have a textured appearance. - **autosaveName** (string) - Unique name for storing window position and size in preferences. - **closable** (boolean) - Whether the window has a close button. - **miniaturizable** (boolean) - Whether the window has a minimize button. - **initiallyVisible** (boolean) - Whether the window is initially visible. Default is True. - **fullScreenMode** (string) - Full screen mode: None, "primary", or "auxiliary". - **titleVisible** (boolean) - Whether the window title should be displayed. - **fullSizeContentView** (boolean) - Whether the content view should be the full size of the window. - **screen** (NSScreen) - The screen the window should be drawn to. ``` -------------------------------- ### Access and modify note Source: https://robofont.com/documentation/reference/api/fontParts/rglyph Get or set the glyph note as a string or None. ```python glyph.note "P.B. said this looks 'awesome.'" glyph.note = "P.B. said this looks 'AWESOME.'" ``` -------------------------------- ### Initialize a CanvasGroup Source: https://robofont.com/documentation/reference/api/mojo/mojo-canvas Demonstrates creating a window with a CanvasGroup delegate to handle drawing operations. ```python from mojo.canvas import CanvasGroup from mojo.drawingTools import * from vanilla import Window class ExampleWindow: def __init__(self): self.w = Window((400, 400), minSize=(200, 200)) self.w.canvas = CanvasGroup((0, 0, -0, -0), delegate=self) self.w.open() def draw(self): rect(10, 10, 100, 100) ExampleWindow() ``` -------------------------------- ### Manage selected layers Source: https://robofont.com/documentation/reference/api/fontParts/rfont Get or set the list of selected layer objects. ```python for layer in layer.selectedLayers: ... layer.color = (1, 0, 0, 0.5) ``` ```python layer.selectedLayers = someLayers ``` -------------------------------- ### Manage Glyph Order Source: https://robofont.com/documentation/reference/api/fontParts/rfont Get or set the preferred order of glyphs in the font. ```python font.glyphOrder ["C", "B", "A"] font.glyphOrder = ["A", "B", "C"] ``` -------------------------------- ### Create a Sheet in Mojo UI Source: https://robofont.com/documentation/reference/api/mojo/mojo-ui Demonstrates how to attach a sheet to a parent window using the Sheet class. Attributes must have unique names to be properly registered. ```python from vanilla import Window, Sheet, Button class SheetDemo: def __init__(self): self.w = Window((240, 140), "Sheet Demo") self.w.openSheet = Button((10, -30, -10, 20), "open sheet", callback=self.openSheetCallback) self.w.open() def openSheetCallback(self, sender): self.sheet = Sheet((160, 70), self.w) self.sheet.closeSheet = Button((10, -30, -10, 20), "close sheet", callback=self.closeSheetCallback) self.sheet.open() def closeSheetCallback(self, sender): self.sheet.close() del self.sheet SheetDemo() ``` -------------------------------- ### selectedGlyphs Source: https://robofont.com/documentation/reference/api/fontParts/rlayer Gets or sets the list of glyph objects currently selected in the layer. ```APIDOC ## selectedGlyphs ### Description A list of glyph objects selected in the layer. ### Usage ```python # Getting selected glyph objects for glyph in layer.selectedGlyphs: glyph.markColor = (1, 0, 0, 0.5) # Setting selected glyph objects layer.selectedGlyphs = someGlyphs ``` ``` -------------------------------- ### Run Jekyll server with incremental builds Source: https://robofont.com/documentation/how-tos/previewing-documentation-locally Use this command to start the local development server and rebuild only modified files for faster previewing. ```bash bundle exec jekyll serve --incremental ``` -------------------------------- ### selectedGlyphNames Source: https://robofont.com/documentation/reference/api/fontParts/rlayer Gets or sets the list of names of glyphs currently selected in the layer. ```APIDOC ## selectedGlyphNames ### Description A list of names of glyphs selected in the layer. ### Usage ```python # Getting selected glyph names for name in layer.selectedGlyphNames: print(name) # Setting selected glyph names layer.selectedGlyphNames = ["A", "B", "C"] ``` ``` -------------------------------- ### Managing selectedGlyphs Source: https://robofont.com/documentation/reference/api/fontParts/rlayer Get or set the list of glyph objects selected in the layer. ```python for glyph in layer.selectedGlyphs: ... glyph.markColor = (1, 0, 0, 0.5) ``` ```python layer.selectedGlyphs = someGlyphs ``` -------------------------------- ### Managing selectedGlyphNames Source: https://robofont.com/documentation/reference/api/fontParts/rlayer Get or set the list of names of glyphs selected in the layer. ```python for name in layer.selectedGlyphNames: ... print(name) ``` ```python layer.selectedGlyphNames = ["A", "B", "C"] ``` -------------------------------- ### Create a directory with os.mkdir Source: https://robofont.com/documentation/tutorials/python/standard-modules Creates a directory named path with numeric mode. ```python newFolder = "/Users/myUsername/Desktop/test" os.mkdir(newFolder) ``` -------------------------------- ### Manage point y coordinate Source: https://robofont.com/documentation/reference/api/fontParts/rpoint Get or set the y coordinate of the point as an integer or float. ```python point.y 100 point.y = 101 ```