Multiple subscribers listening to one window ↩
This example shows how to control multiple glyph editor subscribers using a single vanilla Window
.
First of all, we need to create a custom subscriber event. It is handy to keep custom subscriber events in a separate module, so that the module can be executed only once, when an extension is started.
from mojo.subscriber import registerSubscriberEvent
DEFAULT_KEY = 'com.developerName.SomeTool'
if __name__ == '__main__':
registerSubscriberEvent(
subscriberEventName=f"{DEFAULT_KEY}.changed",
methodName="paletteDidChange",
lowLevelEventNames=[f"{DEFAULT_KEY}.changed"],
dispatcher="roboFont",
documentation="Send when the tool palette did change parameters.",
delay=0,
debug=True
)
If you pack this code into an extension, you should set the events.py module as extension startup script
The rest of the tool is organized in two objects:
- a WindowController subclass (owning a vanilla window)
- a Subscriber subclass (taking care of drawing in the glyph editors)
The Subscriber object draws a stroke around any outline in the glyph editors while the WindowController owns a slider that sets the stroke thickness. The drawing is performed using Merz.
#!/usr/bin/env python3
from mojo.subscriber import Subscriber, WindowController
from mojo.subscriber import registerGlyphEditorSubscriber, unregisterGlyphEditorSubscriber
from mojo.roboFont import OpenWindow
from mojo.events import postEvent
from vanilla import FloatingWindow, Slider, TextBox
from events import DEFAULT_KEY
class ToolPalette(WindowController):
debug = True
thickness = 5
def build(self):
self.w = FloatingWindow((200, 40), "Tool")
self.w.slider = Slider((10, 10, -30, 23),
minValue=0,
value=self.thickness,
maxValue=25,
stopOnTickMarks=True,
tickMarkCount=6,
continuous=False,
callback=self.sliderCallback)
self.w.textBox = TextBox((-25, 10, 30, 17), f"{self.thickness:.0f}")
self.w.open()
def started(self):
Tool.controller = self
registerGlyphEditorSubscriber(Tool)
def destroy(self):
unregisterGlyphEditorSubscriber(Tool)
Tool.controller = None
def sliderCallback(self, sender):
self.thickness = sender.get()
self.w.textBox.set(f"{self.thickness:.0f}")
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)
if __name__ == '__main__':
OpenWindow(ToolPalette)
A few aspects worth noting:
- You have to register and unregister the
Subscriber
tool inside thestarted
anddestroy
events of theWindowController
subclass. Here, you should set thecontroller
attribute of theSubscriber
subclass - Remember to fire
postEvent
inside the user interface callbacks, in this example thesliderCallback
- You can access the modified value from the user interface inside the custom event callback in the subscriber subclass with
self.controller.thickness