class Test: def __init__(self): self.w = Window((400, 400), minSize=(300, 300)) self.w.e = CodeEditor((0, 0, -0, -0), t, lexer=GlyphConstructionLexer()) self.w.open()
class SliderTest: def __init__(self): self.w = Window((300, 400), "SliderTest", autosaveName="SliderTestttt") # self.w.slider1 = SliderPlus((10, 10, -10, 50), "Slider 1", 0, 50, 100) # self.w.slider2 = SliderPlus((10, 60, -10, 50), "Slider 2", 0, 50, 100) info = [("abcd", "The alphabet"), ("xyz ", "The alphabet part 2"), ("wdth", "Width"), ("wght", "Weight")] self.sliderInfo = {} for tag, label in info: self.sliderInfo[tag] = (label, 0, 50, 100) self.w.sliderGroup = SliderGroup(300, self.sliderInfo, continuous=True, callback=self.sliderGroupCallback) self.w.mutateButton = Button((10, -40, 80, 20), "Mutate", callback=self.mutateCallback) self.w.open() def sliderGroupCallback(self, sender): print(sender.get()) def mutateCallback(self, sender): state = {} for tag, (label, minValue, defaultValue, maxValue) in self.sliderInfo.items(): v = minValue + (maxValue - minValue) * random() state[tag] = v self.w.sliderGroup.set(state)
class Preview: def __init__(self): ## create a window self.w = Window((400, 400), "Preview", minSize=(100, 100)) ## add a GlyphPreview to the window self.w.preview = GlyphPreview((0, 0, -0, -0)) ## set the currentGlyph self.setGlyph(CurrentGlyph()) ## add an observer when the glyph changed in the glyph view addObserver(self, "_currentGlyphChanged", "currentGlyphChanged") ## bind a windows close callback to this object self.w.bind("close", self.windowClose) ## open the window self.w.open() def _currentGlyphChanged(self, info): ## notification callback when the glyph changed in the glyph view ## just set the new glyph in the glyph preview self.setGlyph(CurrentGlyph()) def setGlyph(self, glyph): ## setting the glyph in the glyph Preview self.w.preview.setGlyph(glyph) def windowClose(self, sender): ## remove the observer if the window closes removeObserver(self, "_currentGlyphChanged")
class SymmetrifyDialog( object ): def buttonCallback( self, sender ): button = sender.getTitle() font.disableUpdateInterface() glyph.beginUndo() if button == 'S': rotate() if button == 'T': horiflip() if button == 'C': vertiflip() if button == 'H': bothflip() glyph.endUndo() font.enableUpdateInterface() self.w.close() def __init__( self, titles ): self.button = '' margin = 10 size = 40 self.w = Window( ( len( titles ) * ( margin + size ) + margin, 2 * margin + size ), "Symmetrify" ) top = margin left = margin for title in titles: button = SquareButton( ( left, top, size, size ), title, callback = self.buttonCallback ) setattr( self.w, title, button ) left += size + margin def run( self ): self.w.open()
class BaseApp(object): u"""The BaseApp class implements generic functions for more specialize App classes. The main function of apps is to create applications (with window UI) that offers an interface to PageBot publication building scripts. This way apps can be stored and standalone desktop applications, offering more interactive feedback to non-scripting users. Also it hides code form the user, just presenting a coherent set of choices, that then build into PDF documents, websites or identity stationary. """ W, H = 400, 400 PUBLICATION_CLASS = None # Defined by inheriting publication App classes. def __init__(self): self.w = Window((100, 100, self.W, self.H), self.__class__.__name__) self.initialize() # Initialize UI and Publication instance. self.w.open() # Open the application window. def initialize(self): u"""To be implemente by inheriting app classes. Should initialize the self._doc Publication instance. """ self._doc = self.PUBLICATIONS_CLASS() self.buildUI(self._doc.getUIParameters()) def build(self, sender=None): u"""Default behavior, building the publications. To be redefined by inheriting classes if additional functions are needed.""" self._doc.solve() fileName = self._doc.title.replace(' ', '_') self._doc.export('_export/%s.pdf' % fileName)
class Test: def __init__(self): tagGroups = {"GSUB": {"aalt", "salt", "ss02", "ccmb", "ccmp", "liga", "dlig", "rvrn", "cpsp"}, "GPOS": {"kern", "mark", "mkmk", "cpsp", "ZZZZ"}} self.w = Window((300, 500), "TagTest", minSize=(200, 200), autosaveName="TempTagTesttt") self.tags = FeatureTagGroup(300, tagGroups, callback=self.tagsChanged) self.w.tagsScrollView = AligningScrollView((0, 0, 0, -50), self.tags, drawBackground=False, borderType=AppKit.NSNoBorder) self.w.mutateButton = Button((10, -30, 100, 20), "Mutate", callback=self.mutate) self.w.repopulateButton = Button((120, -30, 100, 20), "Repopulate", callback=self.repopulate) self.w.open() def tagsChanged(self, sender): print(sender.get()) def mutate(self, sender): state = self.tags.get() import random for i in range(3): k = random.choice(list(state)) state[k] = random.choice([None, None, None, False, True]) self.tags.set(state) def repopulate(self, sender): tagGroups = {"GSUB": {"salt", "ss02", "ccmb", "ccmp", "liga", "cpsp"}, "GPOS": {"kern", "mkmk", "cpsp"}} self.tags.setTags(tagGroups)
class ParameterTester: def __init__(self): self.w = Window((300, 100)) self.w.inner = Group((10, 10, -10, -10)) p1 = VanillaSingleValueParameter('main', 10, (0, 100), 'int') p2 = VanillaSingleValueParameter('ratio', 10, (0, 100), 'int', master=p1, mode='ratio', dissociable=True) p3 = VanillaSingleValueParameter('offset', 10, (0, 100), 'int', master=p1, mode='offset', dissociable=True) self.w.inner.p1 = ParameterSliderTextInput(p1, (0, 0, -0, 22), 'master') self.w.inner.p2 = ParameterSliderTextInput(p2, (0, 25, -0, 22), 'ratio') self.w.inner.p3 = ParameterSliderTextInput(p3, (0, 50, -0, 22), 'offset') self.w.open()
class SymmetrifyDialog(object): def buttonCallback(self, sender): self.button = sender.getTitle() self.w.close() def __init__(self, titles): self.button = '' margin = 10 size = 40 self.w = Window( (len(titles) * (margin + size) + margin, 2 * margin + size), "Symmetrify") top = margin left = margin for title in titles: button = SquareButton((left, top, size, size), title, callback=self.buttonCallback) setattr(self.w, title, button) left += size + margin def run(self): self.w.open() while not self.button: pass return self.button
class GlyphnameDialog( object): def __init__( self ): x = 10 y = 10 height = 20 button_width = 30 glyphname_width = 180 gap = 6 self.w = Window( ( x + button_width + gap + glyphname_width + gap + button_width + x, y + height + y ), "insert glyph" ) self.w.center() self.w.glyphname = EditText( ( x, y, glyphname_width, height ), '') x += glyphname_width + gap self.w.alignleft = Button( ( x, y, button_width, height ), LEFT, callback = self.buttonCallback ) x += button_width + gap self.w.alignright = Button( ( x, y, button_width, height ), RIGHT, callback = self.buttonCallback ) self.w.setDefaultButton( self.w.alignleft ) self.w.alignright.bind( "\x1b", [] ) self.w.open() def buttonCallback( self, sender ): alignment = sender.getTitle() glyphname = self.w.glyphname.get() if not glyphname: self.w.close() return if len( glyphname ) == 1: uni = ord(glyphname) g = font.glyphForUnicode_("%.4X" % uni) if g: glyphname = g.name other_glyph = font.glyphs[ glyphname ] if not other_glyph: for glyph in font.glyphs: if glyph.name.startswith( glyphname ): other_glyph = glyph print 'Using', glyph.name break else: print 'No matching glyph found.' self.w.close() return selected_glyphs = set( [ layer.parent for layer in font.selectedLayers ] ) for glyph in selected_glyphs: glyph.beginUndo() for layer in glyph.layers: # find other layer for other_layer in other_glyph.layers: if other_layer.name == layer.name: insert_paths( layer, other_layer, alignment ) break else: if active_layerId == layer.layerId: insert_paths( layer, other_glyph.layers[layer.associatedMasterId], alignment ) glyph.endUndo() self.w.close()
class InstanceInterpolationsReport(object): def __init__(self): self.w = Window((350, 600), "Instance Interpolations Report", minSize=(300, 450)) self.w.textEditor = TextEditor((10, 10, -10, -10), text=text, readOnly=True) self.w.open()
class DesignSpaceExplorerTest(object): def __init__(self, designSpace, previewCharacter="e"): from vanilla import Window from tnbits.toolparts.buildvariables.designspaceexplorer import DesignSpaceExplorer self.w = Window((1000, 500), "DesignSpace Explorer", minSize=(600, 300)) self.w.designSpaceExplorer = DesignSpaceExplorer((0, 0, 0, 0), designSpace, previewCharacter=previewCharacter) self.w.open()
class CopyKerning: def __init__(self): if AllFonts() is None: from vanilla.dialogs import message message("No fonts open.", "Open or create a font to copy data to and fro.") return self.sourceFontList = AllFonts() self.destinationFontList = AllFonts() self.source_font = self.sourceFontList[0] self.destination_fonts = None self.groups = None self.kerning = None ## create a window self.w = Window((400, 500), "Copy Groups and Kerning", minSize=(500, 600)) self.w.sourceTitle = TextBox((15, 20, 200, 20), "Source Font:") self.w.sourceFont = PopUpButton((15, 42, 340, 20), [f.info.familyName + ' ' + f.info.styleName for f in self.sourceFontList], callback=self.sourceCallback) self.w.desTitle = TextBox((15, 76, 200, 20), "Destination Fonts:") self.w.destinationFonts = FontList((15, 96, -15, -115), self.destinationFontList, selectionCallback=self.desCallback) self.w.copyButton = Button((-215, -40, 200, 20), 'Copy Groups & Kerning', callback=self.copyCallback) self.w.line = HorizontalLine((10, -60, -10, 1)) self._updateDest() ## open the window self.w.open() def _updateDest(self): des = list(self.sourceFontList) des.remove(self.source_font) self.w.destinationFonts.set(des) def copyKerning(self, groups, kerning, source_font, destination_fonts): kerning = source_font.kerning.asDict() groups = source_font.groups for font in destination_fonts: font.groups.update(groups) font.kerning.update(kerning) def sourceCallback(self, sender): self.source_font = self.sourceFontList[sender.get()] self._updateDest() def desCallback(self, sender): self.destination_fonts = [sender.get()[x] for x in sender.getSelection()] def copyCallback(self, sender): self.sheet = Sheet((300, 50), self.w) self.sheet.bar = ProgressBar((10, 20, -10, 10), isIndeterminate=True, sizeStyle="small") self.sheet.open() self.sheet.bar.start() self.copyKerning(self.groups, self.kerning, self.source_font, self.destination_fonts) self.sheet.bar.stop() self.sheet.close() del self.sheet self.w.close()
class convertMasterToBraceLayers(object): def __init__(self): self.master_names = [m.name for m in Glyphs.font.masters][1:] item_height = 24.0 w_width = 350.0 w_height = item_height * 5 margin = 10 next_y = margin col_1_width = w_width - (margin * 2) item_height = 24 self.w = Window((w_width, w_height), "Convert Master to Brace Layers") next_y = margin self.w.master_names_label = TextBox((margin, next_y + 2, col_1_width, item_height), "Master to Convert (Cannot be the first Master)", sizeStyle='regular') next_y += item_height self.w.master_names = PopUpButton((margin, next_y, col_1_width, item_height), self.master_names) next_y += item_height + margin selected_master_index = Glyphs.font.masters.index(Glyphs.font.selectedFontMaster) self.w.master_names.set(selected_master_index - 1) self.w.gobutton = Button((margin + (col_1_width / 4), next_y, col_1_width / 2, item_height), 'Add Brace Layers', callback=self.makeitso) self.w.setDefaultButton(self.w.gobutton) self.w.open() def makeitso(self, sender): self.w.close() Glyphs.font.disableUpdateInterface() selected_master_index = self.w.master_names.get() + 1 selected_master = Glyphs.font.masters[selected_master_index] layer_name = '{{{}}}'.format(', '.join([bytes(x) for x in selected_master.axes])) master_to_attach_to = Glyphs.font.masters[selected_master_index - 1] for g in Glyphs.font.glyphs: for l in g.layers: if l.associatedMasterId == selected_master.id: if l.isSpecialLayer: l.associatedMasterId = master_to_attach_to.id continue newL = copy.copy(l) newL.associatedMasterId = master_to_attach_to.id newL.name = layer_name g.layers.append(newL) del(Glyphs.font.masters[selected_master_index]) Glyphs.font.enableUpdateInterface() Glyphs.showMacroWindow()
def userChecksInfoNote(font, fontState): window = Window((400, 400),"edit info.note", minSize=(100, 100)) window.textEditor = TextEditor(posSize=(0, 0, 400, 300)) noteContent = font.info.note or "" window.textEditor.set(noteContent) def handleCommit(s): editorContent = window.textEditor.get() window.close() commitInfoNote(editorContent, font, fontState) window.updateNoteButton = SquareButton(posSize=(0, 350, 100, 50), title="commit", callback=handleCommit) window.cancelButton = SquareButton(posSize=(110, 350, 130, 50), title="cancel operation", callback=lambda x: window.close()) window.open()
def openWindow(self, document): u""" Controls and paper view for a document. """ self.windowSize = (document.width, document.height) w = Window(self.windowSize, minSize=(1, 1), maxSize=self.windowSize, closable=False) self.setPaper(w, document) self.setStatusBar(w, document) w.bind("should close", self.windowShouldCloseCallback) w.bind("close", self.windowCloseCallback) w.open() return w
class Test: def __init__(self): self.w = Window((300, 500), minSize=(200, 100)) y = 10 self.w.g = Group((0, 0, 0, 0)) for i, tag in enumerate(["liga", "calt", "dlig", "smcp", "kern", "locl"]): setattr(self.w.g, f"tag{i}", TagView((10, y, 60, 20), tag, None, self.callback)) y += 26 self.w.open() def callback(self, sender): print("---", sender.tag, sender.state)
class ExampleWindow: def __init__(self): self.w = Window((400, 400), minSize=(200, 200)) self.w.canvas = Canvas((M, M, -M, -M), delegate=self) self.w.open() def draw(self, rect): #NSColor.redColor().set() yellowColor.set() for n in range(50): rgba(random(), random(), random()) rect = NSMakeRect(n * 400 + M, n * 400 + M, M, M) path = NSBezierPath.bezierPathWithRect_(rect) path.fill()
class SymmetrifyDialog(object): def buttonCallback(self, sender): button = sender.getTitle() font.disableUpdateInterface() glyph.beginUndo() if button == 'S': rotate() if button == 'T': horiflip() if button == 'C': vertiflip() if button == 'H': bothflip() if button == '*': rotate5() horiflip() rotate5() horiflip() rotate5() horiflip() rotate5() horiflip() glyph.endUndo() font.enableUpdateInterface() self.w.close() def __init__(self, titles): self.button = '' margin = 10 size = 40 self.w = Window( (len(titles) * (margin + size) + margin, 2 * margin + size), "Symmetrify") top = margin left = margin for title in titles: button = SquareButton((left, top, size, size), title, callback=self.buttonCallback) setattr(self.w, title, button) left += size + margin def run(self): self.w.open()
class DBFinalTool: def __init__(self): self.glyph = None # Typical RoboFont function self.updating = False pad = 10 leading = 32 y = pad w = 300 h = 400 buttonWidth = 100 buttonHeight = 48 self.w = Window((100, 100, w, h), 'Final tool window') self.w.bind('close', self.windowCloseCallback) self.w.open() def windowCloseCallback(self, sender): print('removeObservers')
class VarFontTextEditor: def __init__(self): self.fontName = "Bitcount Grid Single" fnt = getFont(self.fontName) axesInfo = getAxisInfo(fnt) self.w = Window((500, 400), "Test", minSize=(300, 200)) # self.w.button = Button((10, 10, 120, 24), "Click", callback=self.myCallback) y = 10 self.sliderMapping = {} for index, axisInfo in enumerate(axesInfo): slider = Slider((10, y, 130, 24), value=axisInfo['default'], minValue=axisInfo['minValue'], maxValue=axisInfo['maxValue'], callback=self.sliderChanged) self.sliderMapping[slider] = axisInfo['tag'] setattr(self.w, "slider_%s" % index, slider) y += 34 self.w.textEditor = TextEditor((150, 0, 0, 0)) attrs = {AppKit.NSFontAttributeName: fnt} self.w.textEditor._textView.setTypingAttributes_(attrs) self.w.open() def sliderChanged(self, sender): tag = self.sliderMapping[sender] location = {} for slider, tag in self.sliderMapping.items(): location[tag] = slider.get() attrs = { AppKit.NSFontAttributeName: getFont(self.fontName, location=location) } stor = self.w.textEditor._textView.textStorage() stor.setAttributes_range_(attrs, (0, stor.length()))
class DrawBotViewer(object): def __init__(self): # create a window self.w = Window((400, 400), minSize=(200, 200)) # add a slider self.w.slider = Slider((10, 10, -10, 22), callback=self.sliderCallback) # add a drawBox view self.w.drawBotCanvas = DrawView((0, 40, -0, -0)) # draw something self.drawIt() # open the window self.w.open() def sliderCallback(self, sender): # slider chagned so redraw it self.drawIt() def drawIt(self): # get the value from the slider value = self.w.slider.get() print(value) # initiate a new drawing newDrawing() # add a page newPage(300, 300) # set a fill color fill(1, value / 100., 0) # draw a rectangle rect(10, 10, 100, 100) # set a font size fontSize(48 + value) # draw some text text("H", (10, 120)) # get the pdf document pdfData = pdfImage() # set the pdf document into the canvas self.w.drawBotCanvas.setPDFDocument(pdfData)
class SymmetrifyDialog( object ): def buttonCallback( self, sender ): self.button = sender.getTitle() self.w.close() def __init__( self, titles ): self.button = '' margin = 10 size = 40 self.w = Window( ( len( titles ) * ( margin + size ) + margin, 2 * margin + size ), "Symmetrify" ) top = margin left = margin for title in titles: button = SquareButton( ( left, top, size, size ), title, callback = self.buttonCallback ) setattr( self.w, title, button ) left += size + margin def run( self ): self.w.open() while not self.button: pass return self.button
class CopyGlyphs: def __init__(self): self.doMarkGlyphs = 0 self.doOverwrite = 1 self.sourceFontList = AllFonts() self.destinationFontList = AllFonts() self.source_font = self.sourceFontList[0] self.destination_fonts = None self.glyphs = None self.mark = NSColor.redColor() ## create a window self.w = Window((700, 500), "Copy Glyphs", minSize=(700, 500)) self.w.sourceTitle = TextBox((15, 20, 200, 20), "Source Font:") self.w.sourceFont = PopUpButton((15, 42, -410, 20), [f.info.familyName + ' ' + f.info.styleName for f in self.sourceFontList], callback=self.sourceCallback) self.w.glyphs = GlyphCollectionView((16, 70, -410, -65), initialMode="list", enableDelete=False, allowDrag=False, selectionCallback=self.glyphCallback) self._sortGlyphs(self.source_font) self.w.desTitle = TextBox((-400, 20, 200, 20), "Destination Fonts:") self.w.destinationFonts = FontList((-400, 42, -15, -115), self.destinationFontList, selectionCallback=self.desCallback) self.w.overwrite = CheckBox((-395, -105, 130, 22), "Overwrite glyphs", callback=self.overwriteCallback, value=self.doOverwrite) self.w.markGlyphs = CheckBox((-395, -84, 100, 22), "Mark Glyphs", callback=self.markCallback, value=self.doMarkGlyphs) self.w.copyButton = Button((-115, -40, 100, 20), 'Copy Glyphs', callback=self.copyCallback) self.w.line = HorizontalLine((10, -50, -10, 1)) self._updateDest() ## open the window self.w.open() def _updateDest(self): des = list(self.sourceFontList) des.remove(self.source_font) self.w.destinationFonts.set(des) def _sortGlyphs(self, font): gs = font.keys() gs.sort() self.w.glyphs.set([font[x] for x in gs]) def _altName(self, font, glyph): name = glyph + '.copy' count = 1 while name in font.keys(): name = name + str(count) count += 1 return name def copyGlyphs(self, glyphs, source_font, destination_fonts, overwrite, mark): for glyph in glyphs: for font in destination_fonts: if glyph in font.keys() and overwrite == 0: n = self._altName(font, glyph) else: n = glyph font.insertGlyph(source_font[glyph], name=n) if mark == 1: font[n].mark = NSColorToRgba(self.mark) def overwriteCallback(self, sender): self.doOverwrite = sender.get() def markCallback(self, sender): self.doMarkGlyphs = sender.get() if self.doMarkGlyphs == 1: self.w.colorWell = ColorWell((-265, -85, 100, 23), callback=self.colorCallback, color=self.mark) else: del self.w.colorWell def colorCallback(self, sender): self.mark = sender.get() def sourceCallback(self, sender): self.source_font = self.sourceFontList[sender.get()] self._sortGlyphs(self.source_font) self._updateDest() def glyphCallback(self, sender): self.glyphs = [self.w.glyphs[x].name for x in sender.getSelection()] def desCallback(self, sender): self.destination_fonts = [sender.get()[x] for x in sender.getSelection()] def copyCallback(self, sender): self.sheet = Sheet((300, 50), self.w) self.sheet.bar = ProgressBar((10, 20, -10, 10), isIndeterminate=True, sizeStyle="small") self.sheet.open() self.sheet.bar.start() self.copyGlyphs(self.glyphs, self.source_font, self.destination_fonts, self.doOverwrite, self.doMarkGlyphs) self.sheet.bar.stop() self.sheet.close() del self.sheet self.w.close()
class GlyphnameDialog( object): def __init__( self ): x = 10 y = 10 height = 20 button_width = 30 glyphname_width = 180 gap = 6 self.w = Window( ( x + button_width + gap + glyphname_width + gap + button_width + x, y + height + y ), "insert glyph" ) self.w.center() self.w.glyphname = EditText( ( x, y, glyphname_width, height ), '') x += glyphname_width + gap self.w.alignleft = Button( ( x, y, button_width, height ), LEFT, callback = self.buttonCallback ) x += button_width + gap self.w.alignright = Button( ( x, y, button_width, height ), RIGHT, callback = self.buttonCallback ) self.w.setDefaultButton( self.w.alignleft ) self.w.alignright.bind( "\x1b", [] ) self.w.open() def buttonCallback( self, sender ): title = sender.getTitle() glyphname = self.w.glyphname.get() if not glyphname: self.w.close() return if len( glyphname ) == 1: uni = ord(glyphname) g = font.glyphForUnicode_("%.4X" % uni) if g: glyphname = g.name other_glyph = font.glyphs[ glyphname ] if not other_glyph: for glyph in font.glyphs: if glyph.name.startswith( glyphname ): other_glyph = glyph print 'Using', glyph.name break else: print 'No matching glyph found.' self.w.close() return for layer in font.selectedLayers: glyph = layer.parent glyph.beginUndo() # deselect all for path in layer.paths: for node in path.nodes: layer.removeObjectFromSelection_( node ) # find other layer for other_layer in other_glyph.layers: if other_layer.name == layer.name: # insert paths for path in other_layer.copyDecomposedLayer().paths: if title == RIGHT: shift = layer.width - other_layer.width for node in path.nodes: node.x = node.x + shift layer.paths.append( path ) # select path layer.paths[-1].selected = True break glyph.endUndo() self.w.close()
from vanilla import Window, SquareButton, TextEditor import vanilla import mojo def onSubmit(sender): CurrentFont().info.note = window.textEditor.get() window.close() if CurrentFont() is None: vanilla.dialogs.message("there is no current font, operation aborted") else: window = Window((400, 400),"edit info.note", minSize=(100, 100)) window.textEditor = TextEditor(posSize=(0, 0, 400, 300)) noteContent = CurrentFont().info.note or "" window.textEditor.set(noteContent) window.updateNoteButton = SquareButton(posSize=(0, 350, 100, 50), title="update", callback=onSubmit) window.cancelButton = SquareButton(posSize=(110, 350, 100, 50), title="cancel", callback=lambda x: window.close()) window.open()
class DBFinalTool: def __init__(self): self.glyph = None # Typical RoboFont function self.updating = False pad = 10 leading = 32 y = pad w = 500 h = 500 c1 = 150 c2 = 200 bh = 24 # Button height leading = bh + pad / 2 self.w = Window((100, 100, w, h), 'Final tool window', minSize=(w, h)) self.w.fontList = List((pad, pad, c2, -w / 2), [], doubleClickCallback=self.openFontCallback, selectionCallback=self.update) y = pad self.w.fixTabWidths = Button((-c1 - pad, y, c1, bh), 'Fix tab widths', callback=self.fixTabWidthsCallback) self.w.fixTabWidths.enable(False) y += leading self.w.fixNegativeWidths = Button( (-c1 - pad, y, c1, bh), 'Fix negative widths', callback=self.fixNegativeWidthsCallback) self.w.fixNegativeWidths.enable(False) y += leading self.w.fixMissingComponentsWidths = Button( (-c1 - pad, y, c1, bh), 'Fix missing components', callback=self.fixMissingComponentCallback) self.w.fixMissingComponentsWidths.enable(False) self.w.selectFolder = Button((-c1 - pad, -pad - bh, c1, bh), 'Select fonts', callback=self.selectFontFolder) self.w.report = EditText((pad, -w / 2 + pad, -pad, -pad), readOnly=False) self.w.bind('close', self.windowCloseCallback) self.w.open() self.dirPath = self.selectFontFolder() def update(self, sender): """Do some UI status update work""" enable = len(self.w.fontList.getSelection()) > 0 self.w.fixTabWidths.enable(enable) self.w.fixMissingComponentsWidths.enable(enable) self.w.fixNegativeWidths.enable(enable) def openFontCallback(self, sender): selectedFonts = [] for index in self.w.fontList.getSelection(): selectedFonts.append(self.w.fontList.get()[index]) # Do something here with the fonts after double click # Open in RoboFont of Glyphs cmd = 'open' for selectedFont in selectedFonts: cmd += ' ' + self.dirPath + selectedFont #self.report(cmd) os.system(cmd) def selectFontFolder(self, sender=None): result = getFolder(messageText='Select fonts folder', title='Select', allowsMultipleSelection=False) if result: path = result[0] fontNames = [] for filePath in os.listdir(path): if filePath.startswith('.') or not filePath.endswith('.ufo'): continue fontNames.append(filePath) self.w.fontList.set(fontNames) return path + '/' return None def fixTabWidthsCallback(self, sender): self.clearReport() for index in self.w.fontList.getSelection(): fontFile = self.w.fontList.get()[index] result = fixTabWidths(self.dirPath + fontFile) self.report('\n'.join(result)) #self.report(self.dirPath ) self.report('Done %s' % fontFile) def fixNegativeWidthsCallback(self, sender): self.clearReport() for index in self.w.fontList.getSelection(): fontFile = self.w.fontList.get()[index] try: result = fixNegativeWidths(self.dirPath + fontFile) self.report('\n'.join(result)) except: f = open(self.dirPath + 'error.txt', 'w') traceback.print_exc(file=f) f.close() self.report('Done %s\n' % fontFile) def fixMissingComponentCallback(self, sender): self.clearReport() for index in self.w.fontList.getSelection(): fontFile = self.w.fontList.get()[index] result = fixMissingComponent(self.dirPath + fontFile) self.report('\n'.join(result)) #self.report(self.dirPath ) self.report('Done %s\n' % fontFile) def windowCloseCallback(self, sender): self.report('removeObservers') def clearReport(self): self.w.report.set('') def report(self, report): s = self.w.report.get() if s: s += '\n' self.w.report.set(s + str(report))
class makeItSo(object): def __init__(self): self.w = Window((550, 140), "Replace named Layer") self.w.editText = EditText((10, 15, -10, 22), placeholder="Layer Name", text='{170}') self.w.correct_path_direction = CheckBox((10, 50, -10, 18), "Correct Path Direction", value=True, sizeStyle='small') self.w.sync_metrics = CheckBox((210, 50, -10, 18), "Sync Metrics", value=True, sizeStyle='small') self.w.add_if_missing = CheckBox((10, 70, -10, 18), "Add layer if missing", value=True, sizeStyle='small') self.w.copybutton = Button((10, 100, -10, 17), "Replace layer", callback=self.buttonCallback) self.w.open() def buttonCallback(self, sender): self.w.close() target_font = Glyphs.font source_font = Glyphs.fonts[1] target_font.disableUpdateInterface() target_layer_name = self.w.editText.get() for source_glyph in source_font.glyphs: target_glyph = target_font.glyphs[source_glyph.name] if target_glyph is None: continue source_layer = source_glyph.layers[0] newL = copy.copy(source_layer) extant = True try: target_layer = [x for x in target_glyph.layers if x.name == target_layer_name][0] newL.associatedMasterId = target_layer.associatedMasterId except IndexError: if not self.w.add_if_missing.get(): continue source_weightValue = source_font.instances[0].weightValue mid = target_font.masters[0].id for m in target_font.masters: if m.weightValue > source_weightValue: break mid = m.id newL.associatedMasterId = mid extant = False newL.name = target_layer_name for c in newL.components: c.automaticAlignment = False if target_font.glyphs[c.componentName] is None: newLayerPaths = source_layer.copyDecomposedLayer() newL.paths = newLayerPaths.paths newL.components = [] break if self.w.correct_path_direction: newL.correctPathDirection() if extant: target_glyph.layers[target_layer.layerId] = newL else: target_glyph.layers.append(newL) if self.w.sync_metrics and extant: target_glyph.layers[target_layer.layerId].syncMetrics() target_font.enableUpdateInterface()
class GlyphMetricsUI(object): def __init__(self): # debug windowname = 'Debug Glyph Metrics UI' windows = [w for w in NSApp().orderedWindows() if w.isVisible()] for window in windows: print(window.title()) if window.title() == windowname: window.close() if debug == True: self.debug = Window((333, 33), windowname) self.debug.bind('close', self.windowSideuiClose) self.debug.open() # setup self.showButtons = False self.sbui = None # add interface addObserver(self, 'addInterface', 'glyphWindowWillOpen') addObserver(self, 'updateSelfWindow', 'currentGlyphChanged') # toggle visibility of tool ui addObserver(self, 'showSideUIonDraw', 'draw') addObserver(self, 'hideSideUIonPreview', 'drawPreview') # remove UI and window manager when glyph window closes # addObserver(self, "observerGlyphWindowWillClose", "glyphWindowWillClose") # subscribe to glyph changes addObserver(self, "viewDidChangeGlyph", "viewDidChangeGlyph") # def observerGlyphWindowWillClose(self, notification): # self.window = notification['window'] # self.cleanup(self.window) # del windowViewManger[notification['window']] # del lll[notification['window']] # del ccc[notification['window']] # del rrr[notification['window']] def windowSideuiClose(self, sender): self.window.removeGlyphEditorSubview(self.sbui) removeObserver(self, 'glyphWindowWillOpen') removeObserver(self, 'glyphWindowWillClose') removeObserver(self, 'glyphWindowWillClose') removeObserver(self, 'currentGlyphChanged') removeObserver(self, 'draw') removeObserver(self, 'drawPreview') removeObserver(self, 'viewDidChangeGlyph') def cleanup(self, w): try: w.removeGlyphEditorSubview(self.sbui) except: return def addInterface(self, notification): self.window = notification['window'] # self.cleanup(self.window) # CONTAINER xywh = (margin, -55, -margin, height) self.sbui = CanvasGroup(xywh, delegate=CanvasStuff(self.window)) # LEFT x, y, w, h = xywh = (0, -height, dfltLwidth, height) this = self.sbui.L = Group(xywh) # text input xywh = (x, y + 3, width * 1.5, height * .75) this.Ltext = EditText(xywh, placeholder='angledLeftMargin', sizeStyle='mini', continuous=False, callback=self.setSB) # quick mod buttons xywh = (x + width * 1.5 + (gap * 1), y, width, height) this.Lminus = Button(xywh, iconminus, sizeStyle='mini', callback=self.setLminus) this.Lminus.getNSButton().setToolTip_('Adjust LSB -' + str(unit)) xywh = (x + width * 2.5 + (gap * 2), y, width, height) this.Lplus = Button(xywh, iconplus, sizeStyle='mini', callback=self.setLplus) this.Lplus.getNSButton().setToolTip_('Adjust LSB +' + str(unit)) xywh = (x + width * 3.5 + (gap * 3), y, width, height) this.Lround = Button(xywh, iconround, sizeStyle='mini', callback=self.setLround) this.Lround.getNSButton().setToolTip_('Round LSB to ' + str(unit)) xywh = (x + width * 4.5 + (gap * 4), y, width, height) this.Lright = Button(xywh, iconcopyR, sizeStyle='mini', callback=self.setLright) this.Lright.getNSButton().setToolTip_('Copy Right Value') # stylize this.Ltext.getNSTextField().setBezeled_(False) this.Ltext.getNSTextField().setBackgroundColor_(NSColor.clearColor()) self.flatButt(this.Lminus) self.flatButt(this.Lplus) self.flatButt(this.Lround) self.flatButt(this.Lright) # RIGHT x, y, w, h = xywh = (-dfltRwidth, y, dfltRwidth, h) this = self.sbui.R = Group(xywh) # text input xywh = (-x - width * 1.5, y + 3, width * 1.5, height * .75) this.Rtext = EditText(xywh, placeholder='angledRightMargin', sizeStyle='mini', continuous=False, callback=self.setSB) # quick mod buttons xywh = (-x - width * 5.5 - (gap * 4), y, width, height) this.Rleft = Button(xywh, iconcopyL, sizeStyle='mini', callback=self.setRleft) this.Rleft.getNSButton().setToolTip_('Copy Left Value') xywh = (-x - width * 4.5 - (gap * 3), y, width, height) this.Rround = Button(xywh, iconround, sizeStyle='mini', callback=self.setRround) this.Rround.getNSButton().setToolTip_('Round RSB to ' + str(unit)) xywh = (-x - width * 3.5 - (gap * 2), y, width, height) this.Rminus = Button(xywh, iconminus, sizeStyle='mini', callback=self.setRminus) this.Rminus.getNSButton().setToolTip_('Adjust RSB -' + str(unit)) xywh = (-x - width * 2.5 - (gap * 1), y, width, height) this.Rplus = Button(xywh, iconplus, sizeStyle='mini', callback=self.setRplus) this.Rplus.getNSButton().setToolTip_('Adjust RSB +' + str(unit)) # stylize this.Rtext.getNSTextField().setBezeled_(False) this.Rtext.getNSTextField().setBackgroundColor_(NSColor.clearColor()) this.Rtext.getNSTextField().setAlignment_(NSTextAlignmentRight) self.flatButt(this.Rminus) self.flatButt(this.Rplus) self.flatButt(this.Rround) self.flatButt(this.Rleft) # CENTER winX, winY, winW, winH = self.window.getVisibleRect() winW = winW - margin * 5 x, y, w, h = xywh = ((winW / 2) - (dfltCwidth / 2), y, dfltCwidth, h) this = self.sbui.C = Group(xywh) x = 0 # text input c = (dfltCwidth / 2) xywh = (c - (width * .75), y + 3, width * 1.5, height * .75) this.Ctext = EditText(xywh, placeholder='width', sizeStyle='mini', continuous=False, callback=self.setSB) # quick mod buttons xywh = (c - (width * .75) - width * 2 - (gap * 2), y, width, height) this.Ccenter = Button(xywh, iconcenter, sizeStyle='mini', callback=self.setCcenter) this.Ccenter.getNSButton().setToolTip_('Center on Width') xywh = (c - (width * .75) - width - (gap * 1), y, width, height) this.Cround = Button(xywh, iconround, sizeStyle='mini', callback=self.setCround) this.Cround.getNSButton().setToolTip_('Round Width to ' + str(unit)) xywh = (c + (width * .75) + (gap * 1), y, width, height) this.Cminus = Button(xywh, iconminus, sizeStyle='mini', callback=self.setCminus) this.Cminus.getNSButton().setToolTip_('Adjust Width -' + str(2 * unit)) xywh = (c + (width * .75) + width + (gap * 2), y, width, height) this.Cplus = Button(xywh, iconplus, sizeStyle='mini', callback=self.setCplus) this.Cplus.getNSButton().setToolTip_('Adjust Width +' + str(2 * unit)) # stylize this.Ctext.getNSTextField().setBezeled_(False) this.Ctext.getNSTextField().setBackgroundColor_(NSColor.clearColor()) this.Ctext.getNSTextField().setAlignment_(NSTextAlignmentCenter) self.flatButt(this.Cminus) self.flatButt(this.Cplus) self.flatButt(this.Cround) self.flatButt(this.Ccenter) # hide self.sbui.L.Lminus.show(False) self.sbui.L.Lround.show(False) self.sbui.L.Lplus.show(False) self.sbui.L.Lright.show(False) self.sbui.R.Rminus.show(False) self.sbui.R.Rround.show(False) self.sbui.R.Rplus.show(False) self.sbui.R.Rleft.show(False) self.sbui.C.Cminus.show(False) self.sbui.C.Cround.show(False) self.sbui.C.Ccenter.show(False) self.sbui.C.Cplus.show(False) # make it real self.sbWatcherInitialize() self.window.addGlyphEditorSubview(self.sbui) self.updateValues() self.buildMatchBase() windowViewManger[self.window] = self.sbui def updateSelfWindow(self, notification): self.window = CurrentGlyphWindow() self.buildMatchBase() self.updateValues() def showSideUIonDraw(self, notification): self.sbWatcher() sbui = windowViewManger.get(self.window) if sbui is not None: sbui.show(True) def hideSideUIonPreview(self, notification): sbui = windowViewManger.get(self.window) if sbui is not None: sbui.show(False) def updateValues(self, notification=None): try: g = self.window.getGlyph() f = g.font sbui = windowViewManger.get(self.window) sbui.L.Ltext.set(str(g.angledLeftMargin)) sbui.R.Rtext.set(str(g.angledRightMargin)) sbui.C.Ctext.set(str(g.width)) except Exception as e: # if debug == True: # print('Exception updateValues', e) return # hack sidebearings changed observer # used when things redraw def sbWatcherInitialize(self): g = self.window.getGlyph() f = g.font if g is not None: lll[self.window] = g.angledLeftMargin ccc[self.window] = g.width rrr[self.window] = g.angledRightMargin def sbWatcher(self): g = CurrentGlyph() if g is not None: f = g.font if lll[self.window] != None and ccc[self.window] != None and rrr[ self.window] != None: if lll[self.window] != g.angledLeftMargin or ccc[ self.window] != g.width or rrr[ self.window] != g.angledRightMargin: lll[self.window] = g.angledLeftMargin ccc[self.window] = g.width rrr[self.window] = g.angledRightMargin self.updateValues() self.buildMatchBase() def setSB(self, sender): changeAttribute = sender.getPlaceholder() g = self.window.getGlyph() f = g.font v = sender.get() if is_number(v): if debug == True: print('value is a number') g.prepareUndo('Change ' + changeAttribute + ' SB') setattr(g, changeAttribute, float(v)) g.performUndo() self.updateValues() elif v in f: if debug == True: print('value is a glyph') g.prepareUndo('Change ' + changeAttribute + ' SB') sb = getattr(f[v], changeAttribute) setattr(g, changeAttribute, sb) g.performUndo() self.updateValues() else: if debug == True: print('value is not a number or a glyph') return def setLminus(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('➖ Left SB') g.angledLeftMargin += -1 * unit g.performUndo() self.updateValues() def setLround(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Round Left SB') g.angledLeftMargin = int(unit * round(float(g.angledLeftMargin) / unit)) g.performUndo() self.updateValues() def setLplus(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('➕ Left SB') g.angledLeftMargin += unit g.performUndo() self.updateValues() def setLright(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Copy Right SB') g.angledLeftMargin = g.angledRightMargin g.performUndo() self.updateValues() def setLmatch(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Match Left SB') f = g.font gmatch = sender.getTitle() if f[gmatch] is not None: g.angledLeftMargin = f[gmatch].angledLeftMargin g.performUndo() self.updateValues() def setRminus(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('➖ Right SB') g.angledRightMargin += -1 * unit g.performUndo() self.updateValues() def setRround(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Round Right SB') g.angledRightMargin = int(unit * round(float(g.angledRightMargin) / unit)) g.performUndo() self.updateValues() def setRplus(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('➕ Right SB') g.angledRightMargin += unit g.performUndo() self.updateValues() def setRmatch(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Match Right SB') gmatch = sender.getTitle() f = g.font if f[gmatch] is not None: g.angledRightMargin = f[gmatch].angledRightMargin g.performUndo() self.updateValues() def setRleft(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Copy Left SB') g.angledRightMargin = g.angledLeftMargin g.performUndo() self.updateValues() def setCminus(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('➖ Width') # use whole units, not floats oldwidth = g.width leftby = unit g.angledLeftMargin += -1 * leftby g.width = oldwidth - unit * 2 g.performUndo() self.updateValues() def setCround(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Round Width') g.width = int(unit * round(float(g.width) / unit)) g.performUndo() self.updateValues() def setCcenter(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Center on Width') # use whole units, not floats padding = g.angledLeftMargin + g.angledRightMargin gwidth = g.width g.angledLeftMargin = int(padding / 2) g.width = gwidth g.performUndo() self.updateValues() def setCplus(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('➕ Width') # use whole units, not floats oldwidth = g.width leftby = unit g.angledLeftMargin += leftby g.width = oldwidth + unit * 2 g.performUndo() self.updateValues() def setCmatch(self, sender): g = self.window.getGlyph() if g is None: return g.prepareUndo('Match Width') f = g.font gmatch = sender.getTitle() if f[gmatch] is not None: g.width = f[gmatch].width g.performUndo() self.updateValues() def flatButt(self, this, match=False): this = this.getNSButton() this.setBezelStyle_(buttstyle) this.setBordered_(False) this.setWantsLayer_(True) this.setBackgroundColor_(NSColor.whiteColor()) if match == True: this.setBackgroundColor_( NSColor.colorWithCalibratedRed_green_blue_alpha_( .9, 1, .85, 1)) def buildMatchBase(self, notification=None): self.newheight = height try: g = self.window.getGlyph() f = g.font # remove old buttons for i in range(10): if hasattr(self.sbui.L, 'buttobj_%s' % i): delattr(self.sbui.L, 'buttobj_%s' % i) delattr(self.sbui.R, 'buttobj_%s' % i) delattr(self.sbui.C, 'buttobj_%s' % i) # add button for each component self.uniquecomponents = [] for c in g.components: if c.baseGlyph not in self.uniquecomponents: self.uniquecomponents.append(c.baseGlyph) for i, c in enumerate(self.uniquecomponents): row = i + 1 yy = -height * (row + 1) - 3 xywh = (0, yy, width * 5.5 + (gap * 4), height) buttobj = Button(xywh, c, sizeStyle='mini', callback=self.setLmatch) setattr(self.sbui.L, 'buttobj_%s' % i, buttobj) this = getattr(self.sbui.L, 'buttobj_%s' % i) this.getNSButton().setAlignment_(NSTextAlignmentLeft) this.getNSButton().setToolTip_('Match LSB of ' + c) xywh = (-width * 5.5 - (gap * 4), yy, width * 5.5 + (gap * 4), height) buttobj = Button(xywh, c, sizeStyle='mini', callback=self.setRmatch) setattr(self.sbui.R, 'buttobj_%s' % i, buttobj) this = getattr(self.sbui.R, 'buttobj_%s' % i) this.getNSButton().setAlignment_(NSTextAlignmentRight) this.getNSButton().setToolTip_('Match RSB of ' + c) xywh = ((dfltLwidth / 2) - (width * 2.75 + (gap * 2)), yy, width * 5.5 + (gap * 4), height) buttobj = Button(xywh, c, sizeStyle='mini', callback=self.setCmatch) setattr(self.sbui.C, 'buttobj_%s' % i, buttobj) this = getattr(self.sbui.C, 'buttobj_%s' % i) this.getNSButton().setToolTip_('Match Width of ' + c) for i, c in enumerate(self.uniquecomponents): try: this = getattr(self.sbui.L, 'buttobj_%s' % i) # hide if hidden if self.showButtons == False: this.show(False) # check if metrics match base glyphs if int(f[c].angledLeftMargin) == int(g.angledLeftMargin): self.flatButt(this, True) else: self.flatButt(this) this = getattr(self.sbui.R, 'buttobj_%s' % i) if self.showButtons == False: this.show(False) if int(f[c].angledRightMargin) == int(g.angledRightMargin): self.flatButt(this, True) else: self.flatButt(this) this = getattr(self.sbui.C, 'buttobj_%s' % i) if self.showButtons == False: this.show(False) if f[c].width == g.width: self.flatButt(this, True) else: self.flatButt(this) except Exception as e: return # change height of canvas to fit buttons self.newheight = height * (len(self.uniquecomponents) + 2) newy = -55 - height * (len(self.uniquecomponents)) self.sbui.setPosSize((margin, newy, -margin, self.newheight)) self.sbui.L.setPosSize((0, 0, dfltLwidth, self.newheight)) self.sbui.R.setPosSize( (-dfltRwidth, 0, dfltRwidth, self.newheight)) winX, winY, winW, winH = self.window.getVisibleRect() winW = winW - margin * 5 offsetcenter = (winW / 2) - (width * 2.25) self.sbui.C.setPosSize( (offsetcenter, 0, width * 5 + 12, self.newheight)) except Exception as e: return ############################################# # watch for glyph changes ############################################# def viewDidChangeGlyph(self, notification): self.glyph = CurrentGlyph() self.unsubscribeGlyph() self.subscribeGlyph() self.glyphChanged() def subscribeGlyph(self): self.glyph.addObserver(self, 'glyphChanged', 'Glyph.Changed') def unsubscribeGlyph(self): if self.glyph is None: return self.glyph.removeObserver(self, 'Glyph.Changed') def glyphChanged(self, notification=None): self.updateValues()
class OCCToucheTool(): def __init__(self): NSUserDefaults.standardUserDefaults().registerDefaults_( {"ToucheWindowHeight": 340}) self.windowHeight = NSUserDefaults.standardUserDefaults( ).integerForKey_("ToucheWindowHeight") self.minWindowHeight = 340 if self.windowHeight < self.minWindowHeight: self.windowHeight = self.minWindowHeight self.closedWindowHeight = 100 self.w = Window((180, self.windowHeight), u'2ché!', minSize=(180, 340), maxSize=(250, 898)) self.w.bind("resize", self.windowResized) self.isResizing = False p = 10 w = 160 # options self.w.options = Group((0, 0, 180, 220)) buttons = { "checkSelBtn": { "text": "Check selected glyphs", "callback": self.checkSel, "y": p }, } for button, data in buttons.iteritems(): setattr( self.w.options, button, Button((p, data["y"], w - 22, 22), data["text"], callback=data["callback"], sizeStyle="small")) self.w.options.zeroCheck = CheckBox((p, 35, w, 20), "Ignore zero-width glyphs", value=True, sizeStyle="small") self.w.options.progress = ProgressSpinner((w - 8, 13, 16, 16), sizeStyle="small") # Modification(Nic): prev / next buttons self.w.options.prevButton = Button((w + 22, 35, 10, 10), "Prev Button", callback=self.prevPair_) self.w.options.prevButton.bind("uparrow", []) self.w.options.nextButton = Button((w + 22, 35, 10, 10), "Next Button", callback=self.nextPair_) self.w.options.nextButton.bind("downarrow", []) # /Modification(Nic) # Modification(Nic): prev / next buttons self.w.options.decrementKerningHigh = Button( (w + 22, 35, 10, 10), "Decrement Kerning High", callback=self.decrementKerningByHigh_) self.w.options.decrementKerningHigh.bind("leftarrow", ["command"]) self.w.options.decrementKerningLow = Button( (w + 22, 35, 10, 10), "Decrement Kerning Low", callback=self.decrementKerningByLow_) self.w.options.decrementKerningLow.bind("leftarrow", []) self.w.options.incrementKerningHigh = Button( (w + 22, 35, 10, 10), "Increment Kerning High", callback=self.incrementKerningByHigh_) self.w.options.incrementKerningHigh.bind("rightarrow", ["command"]) self.w.options.incrementKerningLow = Button( (w + 22, 35, 10, 10), "Increment Kerning Low", callback=self.incrementKerningByLow_) self.w.options.incrementKerningLow.bind("rightarrow", []) # /Modification(Nic) # results self.w.results = Group((0, 220, 180, -0)) self.w.results.show(False) textBoxes = {"stats": -34, "result": -18} for box, y in textBoxes.iteritems(): setattr(self.w.results, box, TextBox((p, y, w, 14), "", sizeStyle="small")) # list and preview self.w.outputList = List((0, 58, -0, -40), [{ "left glyph": "", "right glyph": "" }], columnDescriptions=[{ "title": "left glyph", "width": 90 }, { "title": "right glyph" }], showColumnTitles=False, allowsMultipleSelection=False, enableDelete=False, selectionCallback=self.showPair) self.w.outputList._setColumnAutoresizing() self._resizeWindow(False) self.w.open() # callbacks # Modification(Nic): Incrementing and Decrementing the Pair Index in the list. def prevPair_(self, sender=None): currentIndices = self.w.outputList.getSelection() prevIndices = map(lambda i: max(i - 1, 0), currentIndices) if (len(self.w.outputList) > 0): self.w.outputList.setSelection(prevIndices) def nextPair_(self, sender=None): currentIndices = self.w.outputList.getSelection() nextIndices = map(lambda i: min(i + 1, len(self.w.outputList) - 1), currentIndices) if (len(self.w.outputList) > 0): self.w.outputList.setSelection(nextIndices) # /Modification(Nic) # Modification(Nic): Incrementing and Decrementing the Pair Index in the list. def decrementKerningByLow_(self, sender=None): print('dec by low') currentIndices = self.w.outputList.getSelection() currentPair = self.w.outputList[currentIndices[0]] increment = Glyphs.intDefaults['GSKerningIncrementLow'] self.bumpKerningForPair(currentPair['left glyph'], currentPair['right glyph'], -increment) def decrementKerningByHigh_(self, sender=None): print('dec by high') currentIndices = self.w.outputList.getSelection() currentPair = self.w.outputList[currentIndices[0]] increment = Glyphs.intDefaults['GSKerningIncrementHigh'] self.bumpKerningForPair(currentPair['left glyph'], currentPair['right glyph'], -increment) def incrementKerningByLow_(self, sender=None): print('inc by low') currentIndices = self.w.outputList.getSelection() currentPair = self.w.outputList[currentIndices[0]] increment = Glyphs.intDefaults['GSKerningIncrementLow'] self.bumpKerningForPair(currentPair['left glyph'], currentPair['right glyph'], increment) def incrementKerningByHigh_(self, sender=None): print('inc by high') currentIndices = self.w.outputList.getSelection() currentPair = self.w.outputList[currentIndices[0]] increment = Glyphs.intDefaults['GSKerningIncrementHigh'] self.bumpKerningForPair(currentPair['left glyph'], currentPair['right glyph'], increment) # /Modification(Nic) # Modification(Nic): Routine to increment/decrement kerning properly def bumpKerningForPair(self, left, right, increment): leftGlyph, rightGlyph = Glyphs.font[left], Glyphs.font[right] k = Glyphs.font.kerningForPair(Glyphs.font.selectedFontMaster.id, leftGlyph.rightKerningKey, rightGlyph.leftKerningKey) if k > 10000: k = 0 # Glyphs uses MAXINT to signal no kerning. Glyphs.font.setKerningForPair(Glyphs.font.selectedFontMaster.id, leftGlyph.rightKerningKey, rightGlyph.leftKerningKey, k + increment) # Modification(Nic) def checkAll(self, sender=None): self.check(useSelection=False) def checkSel(self, sender=None): self.check(useSelection=True) def check(self, useSelection): self._resizeWindow(enlarge=False) self.checkFont(useSelection=useSelection, excludeZeroWidth=self.w.options.zeroCheck.get()) def showPair(self, sender=None): try: index = sender.getSelection()[0] glyphs = [self.f[gName] for gName in self.touchingPairs[index]] ActiveFont = self.f._font EditViewController = ActiveFont.currentTab if EditViewController is None: tabText = "/%s/%s" % (glyphs[0].name, glyphs[1].name) ActiveFont.newTab(tabText) else: textStorage = EditViewController.graphicView() if not hasattr(textStorage, "replaceCharactersInRange_withString_" ): # compatibility with API change in 2.5 textStorage = EditViewController.graphicView().textStorage( ) LeftChar = ActiveFont.characterForGlyph_(glyphs[0]._object) RightChar = ActiveFont.characterForGlyph_(glyphs[1]._object) if LeftChar != 0 and RightChar != 0: selection = textStorage.selectedRange() if selection.length < 2: selection.length = 2 if selection.location > 0: selection.location -= 1 NewString = "" if LeftChar < 0xffff and RightChar < 0xffff: NewString = u"%s%s" % (unichr(LeftChar), unichr(RightChar)) else: print "Upper plane codes are not supported yet" textStorage.replaceCharactersInRange_withString_( selection, NewString) selection.length = 0 selection.location += 1 textStorage.setSelectedRange_(selection) #self.w.preview.set(glyphs) except IndexError: pass # checking def _hasSufficientWidth(self, g): # to ignore combining accents and the like if self.excludeZeroWidth: # also skips 1-unit wide glyphs which many use instead of 0 if g.width < 2 or g._object.subCategory == "Nonspacing": return False return True def _trimGlyphList(self, glyphList): newGlyphList = [] for g in glyphList: if g.box is not None and self._hasSufficientWidth(g): newGlyphList.append(g) return newGlyphList def windowResized(self, window): posSize = self.w.getPosSize() Height = posSize[3] if Height > self.closedWindowHeight and self.isResizing is False: print "set new Height", Height NSUserDefaults.standardUserDefaults().setInteger_forKey_( Height, "ToucheWindowHeight") self.windowHeight = Height def _resizeWindow(self, enlarge=True): posSize = self.w.getPosSize() if enlarge: self.w.results.show(True) self.w.outputList.show(True) targetHeight = self.windowHeight if targetHeight < 340: targetHeight = 340 else: self.w.results.show(False) self.w.outputList.show(False) targetHeight = self.closedWindowHeight self.isResizing = True self.w.setPosSize((posSize[0], posSize[1], posSize[2], targetHeight)) self.isResizing = False # ok let's do this def checkFont(self, useSelection=False, excludeZeroWidth=True): f = CurrentFont() if f is not None: # initialize things self.w.options.progress.start() time0 = time.time() self.excludeZeroWidth = excludeZeroWidth self.f = f glyphNames = f.selection if useSelection else f.keys() glyphList = [f[x] for x in glyphNames] glyphList = self._trimGlyphList(glyphList) self.touchingPairs = OCCTouche(f).findTouchingPairs(glyphList) # display output self.w.results.stats.set("%d glyphs checked" % len(glyphList)) self.w.results.result.set("%d touching pairs found" % len(self.touchingPairs)) self.w.results.show(True) outputList = [{ "left glyph": g1, "right glyph": g2 } for (g1, g2) in self.touchingPairs] self.w.outputList.set(outputList) if len(self.touchingPairs) > 0: self.w.outputList.setSelection([0]) #self.w.preview.setFont(f) self.w.options.progress.stop() self._resizeWindow(enlarge=True) time1 = time.time() print u'2ché: finished checking %d glyphs in %.2f seconds' % ( len(glyphList), time1 - time0) else: Message(u'2ché: Can’t find a font to check')
class SimpleFontWindow(BaseWindowController): def __init__(self, font): self._font = font if font.path: document = DoodleDocument.alloc().init() document.setFileURL_(NSURL.fileURLWithPath_(font.path)) dc = NSDocumentController.sharedDocumentController() dc.addDocument_(document) self._canUpdateChangeCount = True 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) self._font.UIdocument().addWindowController_(windowController) self._font.addObserver(self, "fontChanged", "Font.Changed") self.setUpBaseWindowBehavior() self.w.open() self.openFirstGlyph() 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 # notifications def fontChanged(self, notification): if self._canUpdateChangeCount: self._font.UIdocument().updateChangeCount_(0) # to make EditThatNextMaster work as needed def openFirstGlyph(self): glyphName = "A" if glyphName in font.keys(): OpenGlyphWindow(self._font[glyphName])
class Dialogs(object): u""" New dialog creation, for dialog closing see Callbacks class. """ # Start. def openStartWindow(self): u""" Offers a 'New' and 'Open...' button. """ self.startDialog = Window(self.dialogSize, "Welcome", minSize=self.dialogSize, maxSize=self.dialogSize) self.startDialog.newText = TextBox((20, 60, 60, 30), "New") self.startDialog.newButton = Button((20, 100, 80, 20), "Create...", callback=self.new_) self.startDialog.openText = TextBox((160, 60, 60, 30), "Existing") self.startDialog.openButton = Button((160, 100, 60, 20), "Open...", callback=self.open_) self.startDialog.open() def closeStartDialog(self): u""" Closes and clears start dialog. """ if not self.startDialog is None: self.startDialog.close() self.startDialog = None # Document. def openOpenDocumentDialog(self): u""" Open document dialog. """ self.closeStartDialog() panel = NSOpenPanel.openPanel() panel.setCanChooseDirectories_(False) panel.setCanChooseFiles_(True) panel.setAllowsMultipleSelection_(False) panel.setAllowedFileTypes_(['wf']) if panel.runModal() == NSOKButton: added, document = self.model.openDocument(panel.filenames()[0]) self.initDocument(added, document) def openNewDialog(self): u""" Opens the document canvas if it doesn't exist yet. """ self.closeStartDialog() self.setDefaultDocumentValues() size = (400, 300) self.newDialog = w = Window(size, "New Document", minSize=size, maxSize=size) w.nameText = TextBox((20, 20, 180, 20), "Name") w.nameBox = EditText((200, 20, 180, 20), callback=self.newNameCallback) w.nameBox.set(self.documentValues['documentName']) w.sizeText = TextBox((20, 40, 180, 20), "Size") values = PaperSizes.getAllPaperSizes() w.sizeComboBox = ComboBox((200, 40, 180, 20), values, callback=self.newSizeCallback) w.sizeComboBox.set(self.defaultPaperSize) w.whText = TextBox((20, 60, 220, 20), u"w×h in ㎜") width, height = PaperSizes.getSize(self.defaultPaperSize, 'mm') w.width = EditText((200, 60, 80, 20), callback=self.newWidthCallback) w.width.set(width) w.x = TextBox((285, 60, 10, 20), u"×") w.dimensionBoxHeight = EditText((300, 60, 80, 20), callback=self.newHeightCallback) w.dimensionBoxHeight.set(height) w.okayButton = Button((240, 260, 60, 20), "Okay", callback=self.newOkayCallback) #w.okayButton.getNSButton().setEnabled_(False) w.cancelButton = Button((320, 260, 60, 20), "Cancel", callback=self.newCancelCallback) w.open() def closeOpenDialog(self): if not self.openDialog is None: self.openDialog.close() self.openDialog = None # Save dialog. def openSaveDialog(self): if not self.saveDialog is None: return self.saveDialog = Window(self.saveDialogSize, "Save", minSize=self.saveDialogSize, maxSize=self.saveDialogSize) self.saveDialog.saveText = TextBox((60, 20, 280, 60), "Save changes to the document %s?" % self.currentDocument.name) self.saveDialog.dontButton = Button((60, 70, 100, 20), "Don't Save", callback=self.saveDontCallback) self.saveDialog.cancelButton = Button((260, 70, 60, 20), "Cancel", callback=self.saveCloseCallback) self.saveDialog.doButton = Button((320, 70, 60, 20), "Save", callback=self.saveDoCallback) self.saveDialog.open() def closeSaveDialog(self): self.saveDocumentDialog.close() self.saveDocumentDialog = None # Close. def windowShouldCloseCallback(self, sender): window = self.getCurrentWindow() if sender == window: self.closeDocument_(sender)
class ToucheTool: def __init__(self): self.w = Window((180, 340), u"Touché!", minSize=(180, 340), maxSize=(1000, 898)) p = 10 w = 160 # options self.w.options = Group((0, 0, 180, 220)) buttons = { "checkSelBtn": {"text": "Check selected glyphs\nfor touching pairs", "callback": self.checkSel, "y": p}, "checkAllBtn": { "text": "Check entire font\n(can take several minutes!)", "callback": self.checkAll, "y": 60, }, } for button, data in buttons.iteritems(): setattr( self.w.options, button, SquareButton((p, data["y"], w, 40), data["text"], callback=data["callback"], sizeStyle="small"), ) self.w.options.zeroCheck = CheckBox((p, 108, w, 20), "Ignore zero-width glyphs", value=True, sizeStyle="small") self.w.options.progress = ProgressSpinner((82, 174, 16, 16), sizeStyle="small") # results self.w.results = Group((0, 220, 180, -0)) self.w.results.show(False) textBoxes = {"stats": 24, "result": 42} for box, y in textBoxes.iteritems(): setattr(self.w.results, box, TextBox((p, y, w, 14), "", sizeStyle="small")) moreButtons = { "spaceView": {"text": "View all in Space Center", "callback": self.showAllPairs, "y": 65}, "exportTxt": {"text": "Export as MM pair list", "callback": self.exportPairList, "y": 90}, } for button, data in moreButtons.iteritems(): setattr( self.w.results, button, SquareButton((p, data["y"], w, 20), data["text"], callback=data["callback"], sizeStyle="small"), ) # list and preview self.w.outputList = List( (180, 0, 188, -0), [{"left glyph": "", "right glyph": ""}], columnDescriptions=[{"title": "left glyph"}, {"title": "right glyph"}], showColumnTitles=False, allowsMultipleSelection=False, enableDelete=False, selectionCallback=self.showPair, ) self.w.preview = MultiLineView((368, 0, -0, -0), pointSize=256) self.w.open() # callbacks def checkAll(self, sender=None): self.check(useSelection=False) def checkSel(self, sender=None): self.check(useSelection=True) def check(self, useSelection): self.w.results.show(False) self._resizeWindow(enlarge=False) self.checkFont(useSelection=useSelection, excludeZeroWidth=self.w.options.zeroCheck.get()) def showPair(self, sender=None): try: index = sender.getSelection()[0] glyphs = [self.f[gName] for gName in self.touchingPairs[index]] self.w.preview.set(glyphs) except IndexError: pass def showAllPairs(self, sender=None): # open all resulting pairs in Space Center rawString = "" for g1, g2 in self.touchingPairs: rawString += "/%s/%s/space" % (g1, g2) s = OpenSpaceCenter(self.f) s.setRaw(rawString) def exportPairList(self, sender=None): # Save the list of found touching pairs in a text file which can be read by MetricsMachine as a pair list path = PutFile("Choose save location", "TouchingPairs.txt") if path is not None: reportString = "#KPL:P: TouchingPairs\n" for g1, g2 in self.touchingPairs: reportString += "%s %s\n" % (g1, g2) fi = open(path, "w+") fi.write(reportString) fi.close() # checking def _hasSufficientWidth(self, g): # to ignore combining accents and the like if self.excludeZeroWidth: # also skips 1-unit wide glyphs which many use instead of 0 if g.width < 2: return False return True def _trimGlyphList(self, glyphList): newGlyphList = [] for g in glyphList: if g.box is not None and self._hasSufficientWidth(g): newGlyphList.append(g) return newGlyphList def _resizeWindow(self, enlarge=True): posSize = self.w.getPosSize() targetWidth = 700 if enlarge else 180 self.w.setPosSize((posSize[0], posSize[1], targetWidth, posSize[3])) # ok let's do this def checkFont(self, useSelection=False, excludeZeroWidth=True): f = CurrentFont() if f is not None: # initialize things self.w.options.progress.start() time0 = time.time() self.excludeZeroWidth = excludeZeroWidth self.f = f glyphNames = f.selection if useSelection else f.keys() glyphList = [f[x] for x in glyphNames] glyphList = self._trimGlyphList(glyphList) self.touchingPairs = Touche(f).findTouchingPairs(glyphList) # display output self.w.results.stats.set("%d glyphs checked" % len(glyphList)) self.w.results.result.set("%d touching pairs found" % len(self.touchingPairs)) self.w.results.show(True) outputList = [{"left glyph": g1, "right glyph": g2} for (g1, g2) in self.touchingPairs] self.w.outputList.set(outputList) if len(self.touchingPairs) > 0: self.w.outputList.setSelection([0]) else: self.w.preview.set("") outputButtons = [self.w.results.spaceView, self.w.results.exportTxt] for b in outputButtons: b.enable(False) if len(self.touchingPairs) == 0 else b.enable(True) self.w.preview.setFont(f) self.w.options.progress.stop() self._resizeWindow(enlarge=True) time1 = time.time() print u"Touché: finished checking %d glyphs in %.2f seconds" % (len(glyphList), time1 - time0) else: Message(u"Touché: Can’t find a font to check")
class GlyphnameDialog(object): def __init__(self): x = 10 y = 10 height = 20 button_width = 30 glyphname_width = 180 gap = 6 self.w = Window( (x + button_width + gap + glyphname_width + gap + button_width + x, y + height + y), "insert glyph") self.w.center() self.w.glyphname = EditText((x, y, glyphname_width, height), '') x += glyphname_width + gap self.w.alignleft = Button((x, y, button_width, height), LEFT, callback=self.buttonCallback) x += button_width + gap self.w.alignright = Button((x, y, button_width, height), RIGHT, callback=self.buttonCallback) self.w.setDefaultButton(self.w.alignleft) self.w.alignright.bind("\x1b", []) self.w.open() def buttonCallback(self, sender): title = sender.getTitle() glyphname = self.w.glyphname.get() if not glyphname: self.w.close() return if len(glyphname) == 1: uni = ord(glyphname) g = font.glyphForUnicode_("%.4X" % uni) if g: glyphname = g.name other_glyph = font.glyphs[glyphname] if not other_glyph: for glyph in font.glyphs: if glyph.name.startswith(glyphname): other_glyph = glyph break else: self.w.close() return for layer in font.selectedLayers: glyph = layer.parent glyph.beginUndo() # deselect all for path in layer.paths: for node in path.nodes: layer.removeObjectFromSelection_(node) # find other layer for other_layer in other_glyph.layers: if other_layer.name == layer.name: # insert paths for path in other_layer.copyDecomposedLayer().paths: if title == RIGHT: shift = layer.width - other_layer.width for node in path.nodes: node.x = node.x + shift layer.paths.append(path) # select path layer.paths[-1].selected = True break glyph.endUndo() self.w.close()
class makeDisplay(object): def __init__(self): self.verboten = { 'right': ['napostrophe', 'Omegadasiavaria'], 'left': ['ldot', 'Ldot', 'ldot.sc', 'sigmafinal'], 'both': ['*.tf', '*.tosf', '.notdef', 'NULL', 'CR'] } self.category = None self.messages = [] self.interpolated_fonts = dict() self.use_real = True self.use_selection = False self.ignore_red = False self.current_glyph = None self.leftside_kerning_groups = None self.rightside_kerning_groups = None self.all_kern_categories = self.get_all_kern_categories() self.categories_leftside = self.get_categorised_glyphs('left') self.categories_rightside = self.get_categorised_glyphs('right') item_height = 24.0 w_width = 300.0 w_height = item_height * (7 + len(self.all_kern_categories)) margin = 10 next_y = margin col_1_width = w_width - (margin * 2) item_height = 24 radio_height = item_height * len(self.all_kern_categories) self.w = Window((w_width, w_height), "Make Kerning Strings") self.w.text_1 = TextBox((margin, next_y, w_width, item_height), "Kern with:", sizeStyle='regular') next_y += item_height self.w.radioCategories = RadioGroup((margin, next_y, col_1_width, radio_height), self.all_kern_categories, sizeStyle='regular') self.w.radioCategories.set(0) next_y += radio_height + margin self.w.use_real = CheckBox((margin, next_y, col_1_width, item_height), "Use real words", value=True, sizeStyle='regular') next_y += item_height self.w.use_selected = CheckBox((margin, next_y, col_1_width, item_height), "Use the selected glyphs verbatum", value=False, sizeStyle='regular') next_y += item_height self.w.ignore_red = CheckBox((margin, next_y, col_1_width, item_height), "Ignore red marked glyphs", value=False, sizeStyle='regular') next_y += item_height + margin self.w.gobutton = Button((margin + (col_1_width / 4), next_y, col_1_width / 2, item_height), 'Make Strings', callback=self.makeitso) self.w.setDefaultButton(self.w.gobutton) self.w.center() self.w.open() # self.makeitso(None) def sbuttonCallback(self, sender): self.s.close() @staticmethod def has_smallcaps(): for g in Glyphs.font.glyphs: if g.subCategory == 'Smallcaps': return True return False def get_all_kern_categories(self): kcats = [ 'Uppercase', 'Lowercase', ] if self.has_smallcaps: kcats.append('Smallcaps') kcats += [ 'Quotes', 'Number', 'Punctuation', 'Other', ] return kcats def get_canonincal_kerning_glyph(self, layer, pair_side): g = layer.parent if self.use_selection: return g if pair_side == 'left': g = Glyphs.font.glyphs[layer.parent.rightKerningGroup] or layer.parent if pair_side == 'right': g = Glyphs.font.glyphs[layer.parent.leftKerningGroup] or layer.parent if g is None: g = layer.parent return g @staticmethod def make_list_unique(this_list): unique_list = [] for x in this_list: if x in unique_list or x is None: continue unique_list.append(x) return unique_list def get_categorised_glyphs(self, side): # cats = defaultdict(lambda: defaultdict(list)) cats = dict((k, defaultdict(list)) for k in self.all_kern_categories) for g in [x for x in Glyphs.font.glyphs if self.is_elligable(x)]: l = cats.get(g.category, cats.get(g.subCategory, cats['Other'])) l[g.script].append(self.get_canonincal_kerning_glyph(g.layers[0], side)) for cat in cats.keys(): for script in cats[cat].keys(): cats[cat][script] = self.make_list_unique(cats[cat][script]) return cats def get_string(self, left_g, right_g): string = None if self.category == 'Quotes': cat = left_g.subCategory if left_g.subCategory != 'Other' else left_g.category pattern = _kerningStrings.patterns.get(left_g.script, _kerningStrings.patterns.get('latin')).get(cat + '-Quotes', '') strings = [pattern.format(right=right_g.name, left=left_g.name, qL=quote_pair[0], qR=quote_pair[1]).replace(' /', '/') for quote_pair in _kerningStrings.quotations] string = ' '.join(strings) if not string and self.use_real: base_name_left, _, suffix_left = left_g.name.partition('.') base_name_right, _, suffix_right = right_g.name.partition('.') potentials = [ base_name_left + base_name_right, base_name_left + '/' + base_name_right, '/' + base_name_left + ' ' + base_name_right, '/' + base_name_left + '/' + base_name_right, ] for s in potentials: string = _kerningStrings.strings.get(s) if string: break print(s) if not string: pattern = self.get_pattern(left_g, right_g) string = pattern.format(right=right_g.name, left=left_g.name).replace(' /', '/') if not string: string = '/' + left_g.name + '/' + right_g.name return string def get_category_for_glyph(self, glyph): if glyph.category in self.all_kern_categories: return glyph.category if glyph.subCategory in self.all_kern_categories: return glyph.subCategory if glyph.subCategory == 'Currancy': return 'Number' return 'Other' def get_pattern(self, main_glyph, other_glyph): scripts_patterns = _kerningStrings.patterns.get(main_glyph.script, {}) # print(self.get_category_for_glyph(main_glyph)) # print(self.get_category_for_glyph(main_glyph) + '-' + self.get_category_for_glyph(other_glyph), self.all_kern_categories) pattern = scripts_patterns.get(self.get_category_for_glyph(main_glyph) + '-' + self.get_category_for_glyph(other_glyph), '') if self.category == 'Number': suffix = ''.join(main_glyph.name.partition('.')[1:]) else: suffix = '' try: pattern = pattern.format( suffix=suffix, left='{left}', right='{right}', ) except KeyError: pass return pattern def is_elligable(self, glyph, side='both'): if self.ignore_red and glyph.color == 0: return False if not glyph.export: return False for vgn in self.verboten[side]: if re.match(vgn.replace('.', '\\.').replace('*', '.*'), glyph.name): return False return True def makeitso(self, sender): try: self.w.close() except AttributeError: pass self.category = self.all_kern_categories[self.w.radioCategories.get()] self.use_real = self.w.use_real.get() self.use_selection = self.w.use_selected.get() self.ignore_red = self.w.ignore_red.get() all_strings = [] if self.category == 'Quotes': left_of_string_glyphs = self.make_list_unique([self.get_canonincal_kerning_glyph(sl, 'right') for sl in Glyphs.font.selectedLayers if self.is_elligable(sl.parent, 'right')]) right_of_string_glyphs = self.make_list_unique([self.get_canonincal_kerning_glyph(sl, 'left') for sl in Glyphs.font.selectedLayers if self.is_elligable(sl.parent, 'left')]) pairs = zip_longest(left_of_string_glyphs, right_of_string_glyphs) for p in pairs: gl, gr = p if gl is None: gl = gr if gr in left_of_string_glyphs else left_of_string_glyphs[0] if gr is None: gr = gl if gl in left_of_string_glyphs else right_of_string_glyphs[0] kerning_string = self.get_string(gl, gr) if kerning_string not in all_strings: all_strings.append(kerning_string) else: # Holds kerning key glyphs that have been seen already, to avoid duplicates processed_main_glyphs_left = OrderedDict() processed_main_glyphs_right = OrderedDict() # print([(k, self.categories_rightside[k].keys()) for k in self.categories_rightside.keys()]) for sl in Glyphs.font.selectedLayers: # Process the selected glyph on the left side main_g_left = self.get_canonincal_kerning_glyph(sl, 'left') pair_strings_left = [] if self.is_elligable(main_g_left, 'left'): if main_g_left.name not in processed_main_glyphs_left.keys(): processed_main_glyphs_left[main_g_left.name] = [sl.parent.name] try: if sl.parent.script: other_glyphs_rightside = self.categories_rightside[self.category].get(sl.parent.script, self.categories_rightside[self.category].get(None)) else: other_glyphs_rightside = self.categories_rightside[self.category].get(None, self.categories_rightside[self.category].get('latin')) except KeyError: other_glyphs_rightside = [] # print(self.category, self.categories_rightside.keys()) print(sl.parent.script, self.category, self.categories_rightside[self.category].keys()) for g in other_glyphs_rightside: if not self.is_elligable(g, 'right'): continue other_g = self.get_canonincal_kerning_glyph(g.layers[sl.associatedMasterId], 'right') kerning_string_left = self.get_string(main_g_left, other_g) if kerning_string_left not in pair_strings_left: pair_strings_left.append(kerning_string_left) else: processed_main_glyphs_left[main_g_left.name].append(sl.parent.name) if pair_strings_left: pair_strings_left.insert(0, main_g_left.name) # Process the selected glyph on the right side main_g_right = self.get_canonincal_kerning_glyph(sl, 'right') pair_strings_right = [] if self.is_elligable(main_g_right, 'right'): if main_g_right.name not in processed_main_glyphs_right.keys(): processed_main_glyphs_right[main_g_right.name] = [sl.parent.name] if self.category == 'Quotes': other_glyphs_leftside = [main_g_right] main_g_right = self.get_canonincal_kerning_glyph(sl, 'left') else: if sl.parent.script: other_glyphs_leftside = self.categories_leftside[self.category].get(sl.parent.script, self.categories_leftside[self.category].get(None, [])) else: other_glyphs_leftside = self.categories_leftside[self.category].get(None, self.categories_leftside[self.category].get('latin', [])) for g in other_glyphs_leftside: if not self.is_elligable(g, 'left'): continue other_g = self.get_canonincal_kerning_glyph(g.layers[sl.associatedMasterId], 'left') kerning_string_right = self.get_string(other_g, main_g_right) if kerning_string_right not in pair_strings_right: pair_strings_right.append(kerning_string_right) else: processed_main_glyphs_right[main_g_right.name].append(sl.parent.name) if pair_strings_right: pair_strings_right.insert(0, main_g_right.name) left_string = ' '.join(self.make_list_unique(pair_strings_left)) right_string = ' '.join(self.make_list_unique(pair_strings_right)) if all([left_string, right_string]): pair_strings = '\n'.join([left_string, right_string]) else: pair_strings = left_string or right_string # print(':', pair_strings, ':') if pair_strings: all_strings.append(pair_strings) Glyphs.font.newTab('\n\n'.join(all_strings)) Glyphs.font.currentTab.previewInstances = 'live' Glyphs.font.currentTab.scale = 0.065 Glyphs.font.currentTab.textCursor = 3 Glyphs.font.tool = 'TextTool'
class ScriptRunnerApp: """Wrapper class to bundle all document page typesetter and composition functions, generating export document.""" def __init__(self): """ Connects main window and output window for errors. """ self.outputWindow = Window((400, 300), minSize=(1, 1), closable=True) self.outputWindow.outputView = OutPutEditor((0, 0, -0, -0), readOnly=True) self.window = Window((800, 600), minSize=(1, 1), closable=True) self.window.drawView = DrawView((0, 32, -0, -0)) self.scriptPath = None self.scriptFileName = None self.scriptName = None self.initialize() self.window.open() self.outputWindow.open() def getPath(self): """ TODO: store in preferences. TODO: add example scripts to menu. TODO: remember name. """ if self.scriptPath is not None: return self.scriptPath def initialize(self): """ Sets up GUI contents. """ self.buildTop() self.run() def buildTop(self): """Builds buttons at top. TODO: put in a group. """ x = 4 y = 4 w = 100 h = 24 self.window.openFile = Button((x, y, w, h), 'Open', sizeStyle='small', callback=self.openCallback) x += 110 self.window.saveButton = Button((x, y, w, h), 'Save', sizeStyle='small', callback=self.saveCallback) x += 110 self.window.path = TextBox((x, y + 2, -40, h), '', sizeStyle='small') def run(self): """ Runs the script code and writes PDF contents to drawView. """ self.runCode() pdfDocument = self.getPageBotDocument() self.window.drawView.setPDFDocument(pdfDocument) def runCode(self): """ Runs a PageBot script. """ path = self.getPath() if path is None: return _drawBotDrawingTool.newDrawing() namespace = {} _drawBotDrawingTool._addToNamespace(namespace) # Creates a new standard output, catching all print statements and tracebacks. self.output = [] self.stdout = StdOutput(self.output, outputView=self.outputWindow.outputView) self.stderr = StdOutput(self.output, isError=True, outputView=self.outputWindow.outputView) # Calls DrawBot's ScriptRunner with above parameters. ScriptRunner(None, path, namespace=namespace, stdout=self.stdout, stderr=self.stderr) self.printErrors() def printErrors(self): for output in self.output: print(output[0]) def getPageBotDocument(self): """Converts template drawn in memory to a PDF document.""" context = getContextForFileExt('pdf') _drawBotDrawingTool._drawInContext(context) pdfDocument = _drawBotDrawingTool.pdfImage() return pdfDocument def saveCallback(self, sender): """Saves current template to a PDF file.""" self.saveAs() def saveDoCallback(self, path): _drawBotDrawingTool.saveImage(path) def openCallback(self, sender): self.open() def terminate(self): pass def new(self): print('something new') def open(self): """Opens a different script by calling up the get file dialog.""" paths = getFile(messageText='Please select your script', title='Select a script.', allowsMultipleSelection=False, fileTypes=('py', )) if paths is not None: self.scriptPath = paths[0] self.scriptFileName = self.scriptPath.split('/')[-1] self.scriptName = self.scriptFileName.split('.')[0] self.window.path.set(self.scriptPath) self.run() def close(self): print('close something') def saveAs(self): if self.scriptPath is not None: doc = self.getPageBotDocument() putFile(messageText='Save PDF', title='Save PDF as...', fileName='%s.pdf' % self.scriptName, parentWindow=self.window, resultCallback=self.saveDoCallback) def save(self): print('save something') def cut(self): print('cut something') def copy(self): print('copy something') def paste(self): print('paste something') def delete(self): print('delete something') def undo(self): print('undo something') def redo(self): print('redo something')
class MM2SpaceCenter: def activateModule(self): addObserver(self, "MMPairChangedObserver", "MetricsMachine.currentPairChanged") print('MM2SpaceCenter activated') def deactivateModule(self, sender): removeObserver(self, "MetricsMachine.currentPairChanged") print('MM2SpaceCenter deactivated') def __init__(self, ): self.font = metricsMachine.CurrentFont() self.pair = metricsMachine.GetCurrentPair() #self.wordlistPath = wordlistPath leftMargin = 10 topMargin = 5 yPos = 0 lineHeight = 20 yPos += topMargin self.messageText = 'MM2SpaceCenter activated 😎' self.wordCount = 20 self.minLength = 3 self.maxLength = 15 self.activateModule() self.w = Window((250, 155), "MM2SpaceCenter") self.w.myTextBox = TextBox((leftMargin, yPos, -10, 17), self.messageText, sizeStyle="regular") yPos += (lineHeight * 1.2) topLineFields = { "wordCount": [0 + leftMargin, self.wordCount, 20], #"minLength": [108+leftMargin, self.minLength, 3], #"maxLength": [145+leftMargin, self.maxLength, 10], } topLineLabels = { "wcText": [31 + leftMargin, 78, 'words', 'left'], #"wcText": [31+leftMargin, 78, 'words with', 'left'], # "lenTextTwo": [133+leftMargin, 10, u'–', 'center'], #"lenTextThree": [176+leftMargin, -0, 'letters', 'left'], } # for label, values in topLineFields.items(): # setattr(self.w, label, EditText((values[0], 0+yPos, 28, 22), text=values[1], placeholder=str(values[2]))) self.w.wordCount = EditText((0 + leftMargin, 0 + yPos, 28, 22), text=self.wordCount, placeholder=self.wordCount, callback=self.wordCountCallback) for label, values in topLineLabels.items(): setattr( self.w, label, TextBox((values[0], 3 + yPos, values[1], 22), text=values[2], alignment=values[3])) yPos += lineHeight * 1.3 self.loadDictionaries() # language selection languageOptions = list(self.languageNames) self.w.source = PopUpButton((leftMargin, yPos, 85, 20), [], sizeStyle="small", callback=self.changeSourceCallback) self.w.source.setItems(languageOptions) self.w.source.set(4) #default to English for now self.source = None self.source = self.w.source.get( ) #get value, to use for other functions yPos += lineHeight * 1.2 checkBoxSize = 18 self.w.listOutput = CheckBox( (leftMargin, yPos, checkBoxSize, checkBoxSize), "", sizeStyle="small", callback=self.sortedCallback) self.w.listLabel = TextBox( (checkBoxSize + 5, yPos + 2, -leftMargin, checkBoxSize), "Output as list sorted by width", sizeStyle="small") yPos += lineHeight * 1.2 checkBoxSize = 18 self.w.openCloseContext = CheckBox( (leftMargin, yPos, checkBoxSize, checkBoxSize), "", sizeStyle="small", callback=self.sortedCallback) self.w.openCloseContextLabel = TextBox( (checkBoxSize + 5, yPos + 2, -leftMargin, checkBoxSize), "Show open+close context {n}", sizeStyle="small") yPos += lineHeight * 1.2 self.w.mirroredPair = CheckBox( (leftMargin, yPos, checkBoxSize, checkBoxSize), "", sizeStyle="small", callback=self.sortedCallback) self.w.mirroredPairLabel = TextBox( (checkBoxSize + 5, yPos + 2, -leftMargin, checkBoxSize), "Show mirrored pair (LRL)", sizeStyle="small") self.sorted = self.w.listOutput.get() self.w.bind("close", self.deactivateModule) self.w.open() def sortedCallback(self, sender): self.sorted = self.w.listOutput.get() self.wordsForMMPair() def wordCountCallback(self, sender): #print ('old', self.wordCount) self.wordCount = self.w.wordCount.get() or 1 #update space center self.wordsForMMPair() #from word-o-mat def loadDictionaries(self): """Load the available wordlists and read their contents.""" self.dictWords = {} self.allWords = [] self.outputWords = [] self.textfiles = [ 'catalan', 'czech', 'danish', 'dutch', 'ukacd', 'finnish', 'french', 'german', 'hungarian', 'icelandic', 'italian', 'latin', 'norwegian', 'polish', 'slovak', 'spanish', 'vietnamese' ] self.languageNames = [ 'Catalan', 'Czech', 'Danish', 'Dutch', 'English', 'Finnish', 'French', 'German', 'Hungarian', 'Icelandic', 'Italian', 'Latin', 'Norwegian', 'Polish', 'Slovak', 'Spanish', 'Vietnamese syllables' ] #self.source = getExtensionDefault("com.cjtype.MM2SpaceCenter.source", 4) bundle = ExtensionBundle("MM2SpaceCenter") contentLimit = '*****' # If word list file contains a header, start looking for content after this delimiter # read included textfiles for textfile in self.textfiles: path = bundle.getResourceFilePath(textfile) #print (path) with codecs.open(path, mode="r", encoding="utf-8") as fo: lines = fo.read() self.dictWords[textfile] = lines.splitlines( ) # this assumes no whitespace has to be stripped # strip header try: contentStart = self.dictWords[textfile].index(contentLimit) + 1 self.dictWords[textfile] = self.dictWords[textfile][ contentStart:] except ValueError: pass # read user dictionary with open('/usr/share/dict/words', 'r') as userFile: lines = userFile.read() self.dictWords["user"] = lines.splitlines() #print ('load dicts') def changeSourceCallback(self, sender): """On changing source/wordlist, check if a custom word list should be loaded.""" customIndex = len(self.textfiles) + 2 if sender.get() == customIndex: # Custom word list try: filePath = getFile( title="Load custom word list", messageText= "Select a text file with words on separate lines", fileTypes=["txt"])[0] except TypeError: filePath = None self.customWords = [] print("Input of custom word list canceled, using default") if filePath is not None: with codecs.open(filePath, mode="r", encoding="utf-8") as fo: lines = fo.read() # self.customWords = lines.splitlines() self.customWords = [] for line in lines.splitlines(): w = line.strip() # strip whitespace from beginning/end self.customWords.append(w) self.source = self.w.source.get() #update space center self.wordsForMMPair() #print ('source changed') def sortWordsByWidth(self, wordlist): """Sort output word list by width.""" f = self.font wordWidths = [] for word in wordlist: unitCount = 0 for char in word: try: glyphWidth = f[char].width except: try: gname = self.glyphNamesForValues[char] glyphWidth = f[gname].width except: glyphWidth = 0 unitCount += glyphWidth # add kerning for i in range(len(word) - 1): pair = list(word[i:i + 2]) unitCount += int(self.findKerning(pair)) wordWidths.append(unitCount) wordWidths_sorted, wordlist_sorted = zip(*sorted( zip(wordWidths, wordlist))) # thanks, stackoverflow return wordlist_sorted def findKerning(self, chars): """Helper function to find kerning between two given glyphs. This assumes MetricsMachine style group names.""" markers = ["@MMK_L_", "@MMK_R_"] keys = [c for c in chars] for i in range(2): allGroups = self.font.groups.findGlyph(chars[i]) if len(allGroups) > 0: for g in allGroups: if markers[i] in g: keys[i] = g continue key = (keys[0], keys[1]) if self.font.kerning.has_key(key): return self.font.kerning[key] else: return 0 def MMPairChangedObserver(self, sender): #add code here for when myObserver is triggered currentPair = sender["pair"] if currentPair == self.pair: return self.pair = currentPair #print ('current MM pair changed', self.pair) self.wordsForMMPair() #pass # def getMetricsMachineController(self): # # Iterate through ALL OBJECTS IN PYTHON! # import gc # for obj in gc.get_objects(): # if hasattr(obj, "__class__"): # # Does this one have a familiar name? Cool. Assume that we have what we are looking for. # if obj.__class__.__name__ == "MetricsMachineController": # return obj def setSpaceCenter(self, font, text): currentSC = CurrentSpaceCenter() if currentSC is None: print('opening space center, click back into MM window') OpenSpaceCenter(font, newWindow=False) currentSC = CurrentSpaceCenter() currentSC.setRaw(text) def randomly(self, seq): shuffled = list(seq) random.shuffle(shuffled) return iter(shuffled) def gname2char(self, f, gname): uni = f[gname].unicodes[0] char = chr(uni) return char def checkForUnencodedGname(self, font, gname): glyphIsEncoded = False escapeList = ['slash', 'backslash'] if (not font[gname].unicodes) or (gname in escapeList): scString = '/' + gname + ' ' else: scString = self.gname2char(font, gname) glyphIsEncoded = True return (scString, glyphIsEncoded) def getPairstring(self, pair): left, self.leftEncoded = self.checkForUnencodedGname( self.font, pair[0]) right, self.rightEncoded = self.checkForUnencodedGname( self.font, pair[1]) pairstring = left + right return pairstring #convert char gnames to chars to find words in dict def pair2char(self, pair): debug = False try: #print ('pair =', pair) left = self.gname2char(CurrentFont(), pair[0]) right = self.gname2char(CurrentFont(), pair[1]) pair_char = (left, right) return pair_char except: if debug == True: print("couldn't convert pair to chars") return pair def lcString(self, pairstring): string = 'non' + pairstring + 'nono' + pairstring + 'oo' return string def ucString(self, pairstring): string = 'HH' + pairstring + 'HOHO' + pairstring + 'OO' return string openClosePairs = { # initial/final punctuation (from https://www.compart.com/en/unicode/category/Pi and https://www.compart.com/en/unicode/category/Pf) "‚": "‘", "„": "“", "„": "”", "‘": "’", "‛": "’", "“": "”", "‟": "”", "‹": "›", "›": "‹", "«": "»", "»": "«", "⸂": "⸃", "⸄": "⸅", "⸉": "⸊", "⸌": "⸍", "⸜": "⸝", "⸠": "⸡", "”": "”", "’": "’", # Miscellaneous but common open/close pairs "'": "'", '"': '"', "¡": "!", "¿": "?", "←": "→", "→": "←", # opening/closing punctuation (from https://www.compart.com/en/unicode/category/Ps & https://www.compart.com/en/unicode/category/Pe) "(": ")", "[": "]", "{": "}", "༺": "༻", "༼": "༽", "᚛": "᚜", "‚": "‘", "„": "“", "⁅": "⁆", "⁽": "⁾", "₍": "₎", "⌈": "⌉", "⌊": "⌋", "〈": "〉", "❨": "❩", "❪": "❫", "❬": "❭", "❮": "❯", "❰": "❱", "❲": "❳", "❴": "❵", "⟅": "⟆", "⟦": "⟧", "⟨": "⟩", "⟪": "⟫", "⟬": "⟭", "⟮": "⟯", "⦃": "⦄", "⦅": "⦆", "⦇": "⦈", "⦉": "⦊", "⦋": "⦌", "⦍": "⦎", "⦏": "⦐", "⦑": "⦒", "⦓": "⦔", "⦕": "⦖", "⦗": "⦘", "⧘": "⧙", "⧚": "⧛", "⧼": "⧽", "⸢": "⸣", "⸤": "⸥", "⸦": "⸧", "⸨": "⸩", "〈": "〉", "《": "》", "「": "」", "『": "』", "【": "】", "〔": "〕", "〖": "〗", "〘": "〙", "〚": "〛", "〝": "〞", "⹂": "〟", "﴿": "﴾", "︗": "︘", "︵": "︶", "︷": "︸", "︹": "︺", "︻": "︼", "︽": "︾", "︿": "﹀", "﹁": "﹂", "﹃": "﹄", "﹇": "﹈", "﹙": "﹚", "﹛": "﹜", "﹝": "﹞", "(": ")", "[": "]", "{": "}", "⦅": "⦆", "「": "」", } def openCloseContext(self, pair): if self.w.openCloseContext.get() == True: # get unicodes to make sure we don’t show pairs that don’t exist in the font # TODO? may be better to move outside this function, if running it each time is slow. BUT it would have to listen for the CurrentFont to change. unicodesInFont = [ u for glyph in CurrentFont() for u in glyph.unicodes ] left, self.leftEncoded = self.checkForUnencodedGname( self.font, pair[0]) right, self.rightEncoded = self.checkForUnencodedGname( self.font, pair[1]) openCloseString = "" for openClose in self.openClosePairs.items(): # if both sides of pair are in an open+close pair, just add them if openClose[0] == left and openClose[1] == right: openCloseString += left + right + " " # if the left is in an openClose pair and its companion is in the font, add them if openClose[0] == left and ord( openClose[1]) in unicodesInFont: openCloseString += left + right + self.openClosePairs[ left] + " " # if the right is in an openClose pair and its companion is in the font, add them if openClose[1] == right and ord( openClose[0]) in unicodesInFont: openCloseString += openClose[0] + left + right + " " else: continue return openCloseString else: return "" # make mirrored pair to judge symmetry of kerns def pairMirrored(self, pair): if self.w.mirroredPair.get() == True: left, self.leftEncoded = self.checkForUnencodedGname( self.font, pair[0]) right, self.rightEncoded = self.checkForUnencodedGname( self.font, pair[1]) return left + right + left + " " else: return "" def wordsForMMPair(self, ): self.mixedCase = False ### temp comment out to check speed self.source = self.w.source.get() wordsAll = self.dictWords[self.textfiles[self.source]] #default values are hard coded for now #self.wordCount = self.getIntegerValue(self.w.wordCount) #v = self.getIntegerValue(self.w.wordCount) wordCountValue = int(self.wordCount) #print(v) #print ('self.wordCount', self.wordCount) #currently allows any word lenght, this could be customized later text = '' textList = [] # try getting pairstring once in order to check if encoded pairstring = self.getPairstring(self.pair) #convert MM tuple into search pair to check uc, lc, mixed case. Maybe need a different var name here? pair2char = ''.join(self.pair2char(self.pair)) #check Encoding #print (pairstring) #default value makeUpper = False if pair2char.isupper(): #print (pairstring, 'upper') makeUpper = True #make lower for searching searchString = pair2char.lower() else: #print(pairstring, 'not upper') makeUpper = False searchString = pair2char pass #check for mixed case if self.pair2char(self.pair)[0].isupper(): if self.pair2char(self.pair)[1].islower(): if (self.leftEncoded == True) and (self.rightEncoded == True): self.mixedCase = True try: currentSC = CurrentSpaceCenter() previousText = currentSC.getRaw() except: previousText = '' pass count = 0 #self.usePhrases = False # more results for mixed case if we include lc words and capitalize if self.mixedCase == True: for word in self.randomly(wordsAll): # first look for words that are already mixed case if searchString in word: #avoid duplicates if not word in textList: #print (word) textList.append(word) count += 1 #then try capitalizing lowercase words if (searchString.lower() in word[:2]): word = word.capitalize() #avoid duplicates if not word in textList: #print (word) textList.append(word) count += 1 #stop when you get enough results if count >= wordCountValue: #print (text) break pass else: for word in self.randomly(wordsAll): if searchString in word: #avoid duplicates if not word in textList: #print (word) textList.append(word) count += 1 #stop when you get enough results if count >= wordCountValue: #print (text) break if makeUpper == True: #make text upper again textList = list(text.upper() for text in textList) if not len(textList) == 0: #see if box is checked self.sorted = self.w.listOutput.get() #self.sorted = False if self.sorted == True: sortedText = self.sortWordsByWidth(textList) textList = sortedText joinString = "\\n" text = joinString.join([str(word) for word in textList]) if self.w.mirroredPair.get( ) == True: #if "start with mirrored pair" is checked, add this to text text = self.pairMirrored(self.pair) + joinString + text if self.w.openCloseContext.get( ) == True: # if "show open+close" is checked, add this to text text = self.openCloseContext(self.pair) + text else: text = ' '.join([str(word) for word in textList]) if self.w.mirroredPair.get( ) == True: #if "start with mirrored pair" is checked, add this to text text = self.pairMirrored(self.pair) + text if self.w.openCloseContext.get( ) == True: # if "show open+close" is checked, add this to text text = self.openCloseContext(self.pair) + text # if no words are found, show spacing string and previous text if len(text) == 0: #do i need to get pairstring again or can I used the previous one? #pairstring = self.getPairstring(self.pair) previousText = '\\n no words for pair ' + pairstring self.messageText = '😞 no words found: ' + pairstring self.w.myTextBox.set(self.messageText) if makeUpper == True: text = self.ucString(pairstring) + previousText if self.w.mirroredPair.get( ) == True: #if "start with mirrored pair" is checked, add this to text text = self.pairMirrored(self.pair) + text if self.w.openCloseContext.get( ) == True: # if "show open+close" is checked, add this to text text = self.openCloseContext(self.pair) + text else: text = self.lcString(pairstring) + previousText if self.w.mirroredPair.get( ) == True: #if "start with mirrored pair" is checked, add this to text text = self.pairMirrored(self.pair) + text if self.w.openCloseContext.get( ) == True: # if "show open+close" is checked, add this to text text = self.openCloseContext(self.pair) + text text = text.lstrip() #remove whitespace self.setSpaceCenter(self.font, text) else: #set space center if words are found #not sure why there's always a /slash in from of the first word, added ' '+ to avoid losing the first word text = text.lstrip() #remove whitespace self.setSpaceCenter(self.font, text) self.messageText = '😎 words found: ' + pairstring self.w.myTextBox.set(self.messageText)
class Unicron(object): def __init__(self): self.locations = [ 'User Agents', 'Global Agents', 'Global Daemons', 'System Agents', 'System Daemons' ] self.listItems = [] self.selected = {} # Preferences self.homedir = os.path.expanduser('~') self.prefsFolder = self.homedir + "/Library/Preferences/" self.prefsFile = "de.nelsonfritsch.unicron.plist" if os.path.isfile(self.prefsFolder + self.prefsFile): self.prefs = self._loadPrefs(self) else: self.prefs = dict(showSystemWarning=True, windowStyle='System') self._savePrefs(self) # Preferences Window self.prefsWindow = Window((300, 105), 'Preferences') self.styles = ['System', 'Light', 'Dark'] self.prefsWindow.styleTxt = TextBox((10, 10, -10, 20), "Window Style:") self.prefsWindow.style = PopUpButton((30, 35, -10, 20), self.styles, callback=self.prefsSetStyle) self.prefsWindow.restore = Button((10, 75, -10, 20), 'Restore Warnings', callback=self.prefsRestoreWarnings) # Main Window minsize = 285 self.w = Window((minsize, 400), 'Unicron', closable=True, fullSizeContentView=True, titleVisible=False, minSize=(minsize, minsize), maxSize=(600, 1200), autosaveName="UnicronMainWindow") self.pathList = NSPopUpButton.alloc().initWithFrame_( ((0, 0), (160, 20))) self.pathList.addItemsWithTitles_(self.locations) refreshIcon = NSImage.alloc().initWithSize_((32, 32)) sourceImage = NSImage.imageNamed_(NSImageNameRefreshTemplate) w, h = sourceImage.size() if w > h: diffx = 0 diffy = w - h else: diffx = h - w diffy = 0 maxSize = max([w, h]) refreshIcon.lockFocus() sourceImage.drawInRect_fromRect_operation_fraction_( NSMakeRect(diffx, diffy + 4, 22, 22), NSMakeRect(0, 0, maxSize, maxSize), NSCompositeSourceOver, 1) refreshIcon.unlockFocus() refreshIcon.setTemplate_(True) toolbarItems = [ dict(itemIdentifier="Daemons", label="Daemons", toolTip="Daemon Group", view=self.pathList, callback=self.populateList), dict(itemIdentifier="image", label="Image", imageObject=refreshIcon, callback=self.populateList), ] self.w.addToolbar("Unicron Toolbar", toolbarItems=toolbarItems, displayMode="icon") self.w.blend = Group((0, 0, 0, 0), blendingMode='behindWindow') self.listColumnDescriptions = [{ 'title': '', 'key': 'image', 'width': 25, 'typingSensitive': True, 'allowsSorting': True, 'cell': ImageListCell() }, { 'title': 'Name', 'key': 'name', 'typingSensitive': True, 'allowsSorting': True, }] self.rowHeight = 20 self.w.list = List((0, 37, -0, 0), items=self.listItems, columnDescriptions=self.listColumnDescriptions, showColumnTitles=True, allowsEmptySelection=True, allowsMultipleSelection=False, autohidesScrollers=True, drawFocusRing=False, rowHeight=self.rowHeight, selectionCallback=self._selectionCallback, menuCallback=self._menuCallback) self.w.list._nsObject.setBorderType_(NSNoBorder) self.w.statusbar = Group((0, -26, 0, 0), blendingMode='behindWindow') self.w.statusbar.border = HorizontalLine((0, 0, 0, 1)) self.w.counter = TextBox((16, -20, -16, 15), '', alignment='center', sizeStyle='small') self.populateList(self) self.w.rowIndicator = Group((0, 0, 0, 10)) self.prefsSetStyle(self) self.w.open() def prefsSetStyle(self, sender): style = self.prefsWindow.style.getItem() self._changePref(self, 'windowStyle', style) if style == 'System': style = NSUserDefaults.standardUserDefaults().stringForKey_( 'AppleInterfaceStyle') if style == 'Dark': winAppearance = 'NSAppearanceNameVibrantDark' else: winAppearance = 'NSAppearanceNameVibrantLight' appearance = NSAppearance.appearanceNamed_(winAppearance) self.w._window.setAppearance_(appearance) self.prefsWindow._window.setAppearance_(appearance) def prefsRestoreWarnings(self, sender): self._changePref(self, 'showSystemWarning', True) def populateList(self, sender): self.selected.clear() self.w.list._removeSelection() item = self.pathList.titleOfSelectedItem() for i in range(len(self.w.list)): del self.w.list[0] thisItem = {} image = None id = os.getuid() systemWarning = "You should not edit or remove existing system's daemons. These jobs are required for a working macOS system." if item != 'Active Daemons': if item == 'User Agents': homedir = os.path.expanduser('~') path = homedir + '/Library/LaunchAgents' # If the folder doesn't exist in the user folder, create it try: os.listdir(path) except: os.mkdir(path) elif item == 'Global Agents': path = '/Library/LaunchAgents' elif item == 'Global Daemons': path = '/Library/LaunchDaemons' elif item == 'System Agents': path = '/System/Library/LaunchAgents' self._warning(self, systemWarning, "showSystemWarning") elif item == 'System Daemons': path = '/System/Library/LaunchDaemons' self._warning(self, systemWarning, "showSystemWarning") items = [] files = os.listdir(path) count = 0 for file in files: if file.endswith('.plist'): file = file.replace('.plist', '') try: pid = launchd.LaunchdJob(file).pid except: pid = False if launchd.LaunchdJob(file).exists() and pid != None: image = NSImage.imageNamed_(NSImageNameStatusAvailable) elif launchd.LaunchdJob(file).exists() and pid == None: image = NSImage.imageNamed_( NSImageNameStatusPartiallyAvailable) else: image = NSImage.imageNamed_(NSImageNameStatusNone) state = True thisItem['image'] = image thisItem['name'] = file self.w.list.append(thisItem) count += 1 self.w.counter.set(str(count) + ' Jobs') def _showInFinder(self, sender): file = self.selected['file'] subprocess.call(['open', '-R', '%s' % file], cwd='/', shell=False, universal_newlines=False) def _loadUnloadDaemon(self, sender, command): self.w.list.scrollToSelection() name = self.selected['name'] path = self.selected['file'] if bool(launchd.LaunchdJob(name).exists()): try: subprocess.call( ['launchctl', 'unload', '%s' % path], cwd='/', shell=False, universal_newlines=False) except: return else: try: subprocess.call( ['launchctl', 'load', '%s' % path], cwd='/', shell=False, universal_newlines=False) except: return self.populateList(self) def _removeDaemon(self, sender): self._loadUnloadDaemon(self, 'unload') self._loadUnloadDaemon(self, 'remove') # def addGroup(self, sender, key, value): def _selectionCallback(self, sender): try: if not self.w.list.getSelection(): # Application did not finish loading yet pass else: # Get job name self.selected.clear() job = sender.get()[self.w.list.getSelection()[0]] self.selected['name'] = job['name'] self.valueGroups = [] # Get job path and file location item = self.pathList.titleOfSelectedItem() if 'User' in item: import getpass username = getpass.getuser() user = username path = '/Users/%s/Library/Launch' % username elif 'Global' in item: user = '******' path = '/Library/Launch' elif 'System' in item: user = '******' path = '/System/Library/Launch' if 'Agents' in item: path += 'Agents/' else: path += 'Daemons/' self.selected['path'] = path self.selected['file'] = str(self.selected['path'].replace( ' ', '\ ')) + job['name'].replace(' ', '\ ') + '.plist' f = open(self.selected['file'], "r") self.selected['raw'] = str(f.read()) self.selected['short'] = ( self.selected['name'][:32] + '…') if len( self.selected['name']) > 32 else self.selected['name'] # Get status if job['image'] == NSImage.imageNamed_(NSImageNameStatusNone): status = None else: status = 'Available' self.selected['status'] = status index = sender.getSelection()[0] relativeRect = sender.getNSTableView().rectOfRow_(index) self.pop = Popover((300, 100)) self.pop.tabs = Tabs((20, 40, -20, -20), ["Editor", "Raw View"]) self.pop.tabs._nsObject.setTabViewType_(NSNoTabsNoBorder) self.pop.tabBtn = SegmentedButton( (10, 10, -10, 20), [dict(title="Editor"), dict(title="Raw View")], callback=self._segmentPressed, selectionStyle='one') self.pop.tabBtn.set(0) self.edit = self.pop.tabs[0] self.rawEdit = self.pop.tabs[1] self.rawEdit.editor = TextEditor((0, 0, -0, -45), text=self.selected['raw']) self.selected['dict'] = launchd.plist.read( self.selected['name']) # TODO: Add stackview to scrollview as group # Waiting for merge into master: https://github.com/robotools/vanilla/issues/132 self.edit.stack = VerticalStackGroup((0, 0, -0, -45)) for idx, (key, value) in enumerate( sorted(self.selected['dict'].items())): group = ValueGroup((0, 0, -0, -0), sender=self, key=key, value=value, idx=idx) self.valueGroups.append(group) self.edit.stack.addView( self.valueGroups[idx], 300, self.valueGroups[idx].getPosSize()[3]) self.pop.save = Button((20, -50, -20, 40), "Save", callback=self._savePlist) self.pop.save.enable(False) self.pop.open(parentView=sender.getNSTableView(), preferredEdge='right', relativeRect=relativeRect) except: pass # TODO def _savePlist(self, sender): return def _segmentPressed(self, sender): self.pop.tabs.set(self.pop.tabBtn.get()) def _menuCallback(self, sender): items = [] items.append(dict(title=self.selected['short'], enabled=False)) items.append("----") if self.selected['status'] == None: load, able = 'Load', 'Enable' else: load, able = 'Unload', 'Disable' loadCallback = partial(self._loadUnloadDaemon, command=load) ableCallback = partial(self._loadUnloadDaemon, command=able) items.append(dict(title=load, callback=loadCallback)) items.append(dict(title=able, callback=ableCallback)) items.append(dict(title="Show in Finder", callback=self._showInFinder)) items.append(dict(title="Refresh list", callback=self.populateList)) return items def _loadPrefs(self, sender): with open(self.prefsFolder + self.prefsFile, 'rb') as fp: self.prefs = plistlib.load(fp) return self.prefs def _savePrefs(self, sender): with open(self.prefsFolder + self.prefsFile, 'wb') as fp: plistlib.dump(self.prefs, fp) def _changePref(self, sender, key, value): self.prefs[key] = value self._savePrefs(self) def _warning(self, sender, warning, prefKey): if self.prefs.get(prefKey): self.warning = Sheet((400, 140), self.w) self.warning.img = ImageView((10, 10, 60, 60)) self.warning.img.setImage(imageNamed=NSImageNameCaution) self.warning.txt = TextBox((70, 10, -10, -40), "Warning\n" + warning) callback = partial(self._changePref, key=prefKey, value=not self.prefs.get(prefKey)) self.warning.check = CheckBox((70, 80, -10, 20), "Always show this warning", value=self.prefs.get(prefKey), callback=callback) self.warning.closeButton = Button((10, 110, -10, 20), "I understand", callback=self._closeWarning) self.warning.setDefaultButton(self.warning.closeButton) self.warning.center() self.w.list.enable(False) self.warning.open() def _closeWarning(self, sender): self.warning.close() self.w.list.enable(True) del self.warning
script = 'fill(random(), random(), random())\nrect(10+random()*100, 10+random()*100, 200, 300)' newDrawing() namespace = DrawBotNamespace(_drawBotDrawingTool, _drawBotDrawingTool._magicVariables) _drawBotDrawingTool._addToNamespace(namespace) # Creates a new standard output, catching all print statements and tracebacks. stdout = StdOutput(output, outputView=outputWindow.outputView) stderr = StdOutput(output, isError=True, outputView=outputWindow.outputView) # Calls DrawBot's ScriptRunner with above parameters. ScriptRunner(script, None, namespace=namespace, stdout=stdout, stderr=stderr) _drawBotDrawingTool._drawInContext(context) pdfDocument = _drawBotDrawingTool.pdfImage() if __name__ == '__main__': w = Window((10, 10, 400, 200), 'Window') w.button = Button((20, 20, 100, 30), 'Hit', callback=hitCallback) w.open() outputWindow = Window((500, 10, 400, 300), minSize=(1, 1), closable=True) outputWindow.outputView = OutPutEditor((0, 0, -0, -0), readOnly=True) outputWindow.open()
class CornersRounder(BaseWindowController): layerNames = [] targetLayerName = None sourceLayerName = None roundingsData = None selectedFont = None allFonts = None def __init__(self): super(CornersRounder, self).__init__() self._initLogger() self.rounderLogger.info('we are on air! start: __init__()') self._updateFontsAttributes() if self.allFonts != []: self.selectedFont = self.allFonts[0] self._initRoundingsData() if self.selectedFont is not None: self.layerNames = ['foreground'] + self.selectedFont.layerOrder self.sourceLayerName = self.layerNames[0] self.targetLayerName = self.layerNames[0] if PLUGIN_LIB_NAME in self.selectedFont.lib: self.roundingsData = pullRoundingsDataFromFont( self.selectedFont) self.w = Window((0, 0, PLUGIN_WIDTH, PLUGIN_HEIGHT), PLUGIN_TITLE) jumpingY = MARGIN_VER self.w.fontPopUp = PopUpButton( (MARGIN_HOR, jumpingY, NET_WIDTH, vanillaControlsSize['PopUpButtonRegularHeight']), [os.path.basename(item.path) for item in self.allFonts], callback=self.fontPopUpCallback) jumpingY += vanillaControlsSize['PopUpButtonRegularHeight'] + MARGIN_VER self.w.sepLineOne = HorizontalLine( (MARGIN_HOR, jumpingY, NET_WIDTH, vanillaControlsSize['HorizontalLineThickness'])) jumpingY += MARGIN_VER for eachI in range(LABELS_AMOUNT): singleLabel = Label((MARGIN_HOR, jumpingY, NET_WIDTH, vanillaControlsSize['EditTextRegularHeight']), attachCallback=self.attachCallback, labelName='') setattr(self.w, 'label{:d}'.format(eachI), singleLabel) jumpingY += MARGIN_ROW + vanillaControlsSize[ 'EditTextRegularHeight'] self._fromRoundingsData2LabelCtrls() jumpingY += MARGIN_ROW self.w.sepLineTwo = HorizontalLine( (MARGIN_HOR, jumpingY, NET_WIDTH, vanillaControlsSize['HorizontalLineThickness'])) jumpingY += MARGIN_ROW * 2 # tables labelListWdt = 78 marginTable = 1 angleListWdt = (NET_WIDTH - labelListWdt - marginTable * 3) // 3 tableLineHeight = 16 tableHgt = LABELS_AMOUNT * tableLineHeight + 33 captionY = jumpingY captionOffset = 12 jumpingY += vanillaControlsSize['TextBoxSmallHeight'] + MARGIN_ROW labelColumnDesc = [{"title": "labelName", 'editable': True}] jumpingX = MARGIN_HOR self.w.labelNameList = List( (jumpingX, jumpingY, labelListWdt, tableHgt), [], columnDescriptions=labelColumnDesc, showColumnTitles=True, editCallback=self.labelNameListCallback, rowHeight=tableLineHeight, drawHorizontalLines=True, drawVerticalLines=True, autohidesScrollers=True, allowsMultipleSelection=False) anglesColumnDesc = [{ "title": "rad", 'editable': True }, { "title": "bcp", 'editable': True }] jumpingX += labelListWdt + marginTable self.w.fortyFiveCaption = TextBox( (jumpingX + captionOffset, captionY, angleListWdt, vanillaControlsSize['TextBoxSmallHeight']), u'45°', sizeStyle='small') self.w.fortyFiveList = List( (jumpingX, jumpingY, angleListWdt, tableHgt), [], columnDescriptions=anglesColumnDesc, showColumnTitles=True, rowHeight=tableLineHeight, editCallback=self.fortyFiveListCallback, drawHorizontalLines=True, drawVerticalLines=True, autohidesScrollers=True, allowsMultipleSelection=False) jumpingX += angleListWdt + marginTable self.w.ninetyCaption = TextBox( (jumpingX + captionOffset, captionY, angleListWdt, vanillaControlsSize['TextBoxSmallHeight']), u'90°', sizeStyle='small') self.w.ninetyList = List((jumpingX, jumpingY, angleListWdt, tableHgt), [], columnDescriptions=anglesColumnDesc, showColumnTitles=True, rowHeight=tableLineHeight, editCallback=self.ninetyListCallback, drawHorizontalLines=True, drawVerticalLines=True, autohidesScrollers=True, allowsMultipleSelection=False) jumpingX += angleListWdt + marginTable self.w.hundredThirtyFiveCaption = TextBox( (jumpingX + captionOffset, captionY, angleListWdt, vanillaControlsSize['TextBoxSmallHeight']), u'135°', sizeStyle='small') self.w.hundredThirtyFiveList = List( (jumpingX, jumpingY, angleListWdt, tableHgt), [], columnDescriptions=anglesColumnDesc, showColumnTitles=True, rowHeight=tableLineHeight, editCallback=self.hundredThirtyFiveListCallback, drawHorizontalLines=True, drawVerticalLines=True, autohidesScrollers=True, allowsMultipleSelection=False) self._fromRoundingsData2Lists() jumpingY += tableHgt + MARGIN_ROW * 2 rgtX = MARGIN_HOR + NET_WIDTH * .52 midWdt = NET_WIDTH * .48 self.w.pushButton = SquareButton( (MARGIN_HOR, jumpingY, midWdt, vanillaControlsSize['ButtonRegularHeight'] * 1.5), 'Push Data', callback=self.pushButtonCallback) self.w.clearLibButton = SquareButton( (rgtX, jumpingY, midWdt, vanillaControlsSize['ButtonRegularHeight'] * 1.5), 'Clear Lib', callback=self.clearLibButtonCallback) jumpingY += vanillaControlsSize[ 'ButtonRegularHeight'] * 1.5 + MARGIN_ROW * 2 self.w.sepLineThree = HorizontalLine( (MARGIN_HOR, jumpingY, NET_WIDTH, vanillaControlsSize['HorizontalLineThickness'])) jumpingY += MARGIN_ROW * 2 self.w.sourceLayerCaption = TextBox( (MARGIN_HOR, jumpingY, midWdt, vanillaControlsSize['TextBoxRegularHeight']), 'source layer') self.w.targetLayerCaption = TextBox( (rgtX, jumpingY, midWdt, vanillaControlsSize['TextBoxRegularHeight']), 'target layer') jumpingY += vanillaControlsSize['TextBoxRegularHeight'] + MARGIN_ROW self.w.sourceLayerPopUp = PopUpButton( (MARGIN_HOR, jumpingY, midWdt, vanillaControlsSize['PopUpButtonRegularHeight']), self.layerNames, callback=self.sourceLayerPopUpCallback) if self.layerNames and self.sourceLayerName: self.w.sourceLayerPopUp.set( self.layerNames.index(self.sourceLayerName)) self.w.targetLayerCombo = ComboBox( (rgtX, jumpingY, midWdt, vanillaControlsSize['PopUpButtonRegularHeight']), self.layerNames, callback=self.targetLayerComboCallback) if self.layerNames and self.targetLayerName: self.w.targetLayerCombo.set(self.targetLayerName) jumpingY += vanillaControlsSize[ 'PopUpButtonRegularHeight'] + MARGIN_ROW * 4 self.w.roundGlyphButton = SquareButton( (MARGIN_HOR, jumpingY, midWdt, vanillaControlsSize['ButtonRegularHeight'] * 1.5), u'Round Glyph (⌘+R)', callback=self.roundGlyphButtonCallback) self.w.roundGlyphButton.bind('r', ['command']) self.w.roundFontButton = SquareButton( (rgtX, jumpingY, midWdt, vanillaControlsSize['ButtonRegularHeight'] * 1.5), 'Round Font', callback=self.roundFontButtonCallback) jumpingY += vanillaControlsSize[ 'ButtonRegularHeight'] * 1.5 + MARGIN_VER * 2 self.w.resize(PLUGIN_WIDTH, jumpingY) self._checkPushButton() self._checkRoundButtons() self.setUpBaseWindowBehavior() addObserver(self, 'fontDidOpenCallback', 'fontDidOpen') addObserver(self, 'fontDidCloseCallback', 'fontDidClose') addObserver(self, '_keyDown', 'keyDown') self.w.open() # private methods def _initLogger(self): # create a logger self.rounderLogger = logging.getLogger('rounderLogger') self.rounderLogger.setLevel(logging.INFO) # create file handler which logs info messages fileHandle = logging.FileHandler('cornersRounderLogger.log') fileHandle.setLevel(logging.INFO) # create console handler with a higher log level, only errors # create formatter and add it to the handlers formatter = logging.Formatter(u'%(asctime)s – %(message)s') fileHandle.setFormatter(formatter) # add the handlers to the logger self.rounderLogger.addHandler(fileHandle) def _initRoundingsData(self): self.roundingsData = [ makeRoundingsDataEmptyDict() for ii in range(LABELS_AMOUNT) ] def _updateFontsAttributes(self): self.allFonts = AllFonts() if self.allFonts == []: self.selectedFont = None def _updateRoundingsLabels(self, labels): for indexLabel, eachLabel in enumerate(labels): self.roundingsData[indexLabel]['labelName'] = '{}'.format( eachLabel['labelName']) def _updateRoundingsNumbers(self, data, keyStart): for indexRow, eachRow in enumerate(data): try: self.roundingsData[indexRow]['{}Rad'.format(keyStart)] = int( eachRow['rad']) except ValueError: self.roundingsData[indexRow]['{}Rad'.format(keyStart)] = '' try: self.roundingsData[indexRow]['{}Bcp'.format(keyStart)] = int( eachRow['bcp']) except ValueError: self.roundingsData[indexRow]['{}Bcp'.format(keyStart)] = '' def _fromRoundingsData2LabelCtrls(self): for eachI in range(LABELS_AMOUNT): try: labelName = self.roundingsData[eachI]['labelName'] except IndexError as e: labelName = '' getattr(self.w, 'label{:d}'.format(eachI)).setLabelName(labelName) def _updateLayersCtrls(self): self.layerNames = ['foreground'] + self.selectedFont.layerOrder currentName = self.w.sourceLayerPopUp.getItems()[ self.w.sourceLayerPopUp.get()] self.w.sourceLayerPopUp.setItems(self.layerNames) if currentName in self.layerNames: self.w.sourceLayerPopUp.set(self.layerNames.index(currentName)) currentName = self.w.targetLayerCombo.get() self.w.targetLayerCombo.setItems(self.layerNames) if currentName in self.layerNames: self.w.targetLayerCombo.set(currentName) def _extractDataFromRoundings(self, keyTitle): listData = [{ 'rad': aDict['{}Rad'.format(keyTitle)], 'bcp': aDict['{}Bcp'.format(keyTitle)] } for aDict in self.roundingsData] return listData def _fromRoundingsData2Lists(self): labelNameListData = [{ 'labelName': aDict['labelName'] } for aDict in self.roundingsData] self.w.labelNameList.set(labelNameListData) fortyFiveListData = self._extractDataFromRoundings('fortyFive') self.w.fortyFiveList.set(fortyFiveListData) ninetyListData = self._extractDataFromRoundings('ninety') self.w.ninetyList.set(ninetyListData) hundredThirtyFiveListData = self._extractDataFromRoundings( 'hundredThirtyFive') self.w.hundredThirtyFiveList.set(hundredThirtyFiveListData) def _roundCurrentGlyph(self): if self.sourceLayerName == self.targetLayerName: self.showMessage( 'ERROR', u'source layer name and target layer name should be different') return None currentGlyph = CurrentGlyph() if currentGlyph is not None: self.rounderLogger.info( 'start: _roundCurrentGlyph(), glyph {} from {} {}'.format( currentGlyph.name, self.selectedFont.info.familyName, self.selectedFont.info.styleName)) selectedFont = currentGlyph.getParent() roundingsData = pullRoundingsDataFromFont(selectedFont) if roundingsData is not None: makeGlyphRound(currentGlyph, roundingsData, sourceLayerName=self.sourceLayerName, targetLayerName=self.targetLayerName) UpdateCurrentGlyphView() self.rounderLogger.info( 'end: _roundCurrentGlyph(), glyph {} from {} {}'.format( currentGlyph.name, selectedFont.info.familyName, selectedFont.info.styleName)) elif currentGlyph is not None: self.showMessage('ERROR', NO_DATA_INTO_FONT) self.rounderLogger.error(NO_DATA_INTO_FONT) else: self.showMessage('ERROR', NO_GLYPH_TO_ROUND) self.rounderLogger.error(NO_GLYPH_TO_ROUND) # observers callbacks def _keyDown(self, notification): glyph = notification['glyph'] pressedKeys = notification['event'].charactersIgnoringModifiers() modifierFlags = notification['event'].modifierFlags() if modifierFlags in MODIFIERS and MODIFIERS[ modifierFlags] == 'CMD_LEFT' and pressedKeys == 'r': self._roundCurrentGlyph() def fontDidOpenCallback(self, notification): if self.allFonts != []: currentName = os.path.basename( self.allFonts[self.w.fontPopUp.get()].path) else: currentName = None self._updateFontsAttributes() newNames = [os.path.basename(item.path) for item in self.allFonts] self.w.fontPopUp.setItems(newNames) if currentName is not None: self.w.fontPopUp.set(newNames.index(currentName)) def fontDidCloseCallback(self, notification): self._updateFontsAttributes() newNames = [os.path.basename(item.path) for item in self.allFonts] self.w.fontPopUp.setItems(newNames) if self.allFonts != []: self.selectedFont = self.allFonts[0] self.roundingsData = pullRoundingsDataFromFont(self.selectedFont) if self.roundingsData is None: self._initRoundingsData() self._fromRoundingsData2Lists() self._fromRoundingsData2LabelCtrls() else: self.selectedFont = None self.roundingsData = None def windowCloseCallback(self, sender): removeObserver(self, 'fontDidOpen') removeObserver(self, 'fontDidClose') removeObserver(self, 'keyDown') self.rounderLogger.info('that\'s all folks!') # standard callbacks def fontPopUpCallback(self, sender): self.selectedFont = self.allFonts[sender.get()] self.roundingsData = pullRoundingsDataFromFont(self.selectedFont) if self.roundingsData is None: self._initRoundingsData() self._fromRoundingsData2Lists() self._fromRoundingsData2LabelCtrls() self._updateLayersCtrls() self._checkPushButton() def attachCallback(self, sender): labelName = sender.get() attachLabelToSelectedPoints(labelName) def _checkPushButton(self): if hasattr(self.w, 'pushButton') is True: if PLUGIN_LIB_NAME not in self.selectedFont.lib or self.roundingsData != self.selectedFont.lib[ PLUGIN_LIB_NAME]: self.w.pushButton.enable(True) else: self.w.pushButton.enable(False) def labelNameListCallback(self, sender): self._updateRoundingsLabels(sender.get()) self._checkPushButton() self._checkRoundButtons() def _checkRoundButtons(self): if hasattr(self.w, 'roundGlyphButton') is True and hasattr( self.w, 'roundFontButton'): if (PLUGIN_LIB_NAME in self.selectedFont.lib and self.roundingsData != self.selectedFont.lib[PLUGIN_LIB_NAME] ) or self.roundingsData == DUMMY_ROUNDINGS: self.w.roundGlyphButton.enable(False) self.w.roundFontButton.enable(False) else: self.w.roundGlyphButton.enable(True) self.w.roundFontButton.enable(True) def fortyFiveListCallback(self, sender): self._updateRoundingsNumbers(sender.get(), 'fortyFive') self._checkPushButton() self._checkRoundButtons() def ninetyListCallback(self, sender): self._updateRoundingsNumbers(sender.get(), 'ninety') self._checkPushButton() self._checkRoundButtons() def hundredThirtyFiveListCallback(self, sender): self._updateRoundingsNumbers(sender.get(), 'hundredThirtyFive') self._checkPushButton() self._checkRoundButtons() def pushButtonCallback(self, sender): thisFont = self.selectedFont if thisFont is not None: pushRoundingsDataIntoFont(thisFont, self.roundingsData) self.showMessage( 'INFO', DATA_PUSHED.format(familyName=thisFont.info.familyName, styleName=thisFont.info.styleName)) self.rounderLogger.info( DATA_PUSHED.format(familyName=thisFont.info.familyName, styleName=thisFont.info.styleName)) else: self.showMessage('ERROR', NO_FONT_TO_PUSH) self.rounderLogger.error(NO_FONT_TO_PUSH) self._checkPushButton() self._checkRoundButtons() def clearLibButtonCallback(self, sender): if PLUGIN_LIB_NAME in self.selectedFont.lib: del self.selectedFont.lib[PLUGIN_LIB_NAME] self._initRoundingsData() self._fromRoundingsData2Lists() self._fromRoundingsData2LabelCtrls() self._checkPushButton() self._checkRoundButtons() def sourceLayerPopUpCallback(self, sender): self.sourceLayerName = self.layerNames[sender.get()] def targetLayerComboCallback(self, sender): self.targetLayerName = sender.get() def roundGlyphButtonCallback(self, sender): if self.selectedFont == CurrentFont(): self._roundCurrentGlyph() else: self.showMessage('ERROR', FONT_MISMATCH) self.rounderLogger.error(FONT_MISMATCH) def roundFontButtonCallback(self, sender): if self.sourceLayerName == self.targetLayerName: self.showMessage('ERROR', LAYERS_MATCH) self.rounderLogger.error(LAYERS_MATCH) return None if self.selectedFont == CurrentFont(): self.rounderLogger.info( 'start: roundFontButtonCallback(), {} {}'.format( selectedFont.info.familyName, selectedFont.info.styleName)) for eachGlyph in self.selectedFont: makeGlyphRound(eachGlyph, self.roundingsData, sourceLayerName=self.sourceLayerName, targetLayerName=self.targetLayerName) self.rounderLogger.info( 'end: roundFontButtonCallback(), {} {}'.format( selectedFont.info.familyName, selectedFont.info.styleName)) else: self.showMessage('ERROR', FONT_MISMATCH) self.rounderLogger.error(FONT_MISMATCH)
class RotateView(GeneralPlugin): @objc.python_method def settings(self): self.name = "Rotate View" @objc.python_method def start(self): newMenuItem = NSMenuItem(self.name, self.showWindow_) Glyphs.menu[WINDOW_MENU].append(newMenuItem) ## creates Vanilla Window #------------------------ def showWindow_(self, sender): try: from vanilla import Group, Slider, TextBox, Window self.windowWidth = 300 self.windowHeight = 240 self.w = Window((self.windowWidth, self.windowWidth), "Rotate View", minSize=(self.windowWidth, self.windowWidth)) window = self.w.getNSWindow() window.setStyleMask_(window.styleMask() | NSFullSizeContentViewWindowMask) try: # only available in 10.10 window.setTitlebarAppearsTransparent_(True) except: pass #window.toolbar = nil; window.setMovableByWindowBackground_(True) self.w.Preview = RoatatePreview((0, 0, -0, -28)) self.w.controlBox = Group((0, -28, -0, -0)) self.w.controlBox.slider = Slider((10, 2, -55, 28), tickMarkCount=17, callback=self.sliderCallback, value=0, minValue=-360, maxValue=360) self.w.controlBox.textBox = TextBox((-55, -23, -5, -3), text="0°", alignment="center") self.w.controlBox.slider.getNSSlider().setEnabled_(False) self.w.open() self.changeGlyph_(None) Glyphs.addCallback( self.changeGlyph_, UPDATEINTERFACE ) #will be called on ever change to the interface except: print(traceback.format_exc()) @objc.python_method def __del__(self): Glyphs.removeCallback(self.changeGlyph_, UPDATEINTERFACE) ## slider callback #------------------------------ @objc.python_method def sliderCallback(self, sender): currentValue = '{:.0f}'.format(sender.get()) self.w.controlBox.textBox.set(str(currentValue) + "°") self.w.Preview._rotationFactor = float(currentValue) self.w.Preview.redraw() ## on Glyph Change, update the viewer #------------------------------ def changeGlyph_(self, sender): self.w.controlBox.slider.getNSSlider().setEnabled_( Glyphs.font and Glyphs.font.selectedLayers) self.w.Preview.redraw() @objc.python_method def __file__(self): """Please leave this method unchanged""" return __file__
class Test: def __init__(self): self.w = Window((400, 400)) self.w.e = CodeEditor((0, 0, -0, -0), t, lexer=GlyphConstructionLexer()) self.w.open()
def show(self): self.w.show() def documentWindowToFront(self, sender=None): self.w.makeKey() def do(sender): controller = getController() controller.runCode() # runt je script opnieuw. def getController(): document = AppKit.NSDocumentController.sharedDocumentController().currentDocument() print document if not document: raise DrawBotError("There is no document open") controller = document.vanillaWindowController try: controller._variableController.buildUI(variables) controller._variableController.show() except: controller._variableController = VariableController(variables, controller.runCode, document) return controller w = Window((978, 388, 400, 400), 'Test Vanilla in DrawBot') w.button = Button((10, 10, 150, 30), 'Do', callback=do) w.checkbox = CheckBox((10, 150, 150, 30), 'Check', callback=do) w.open()
class PenBallWizardController(object): def __init__(self): self.filters = PenBallFiltersManager() self.filters.loadFiltersFromJSON('/'.join([LOCALPATH, JSONFILE])) self.glyphNames = [] self.observedGlyphs = [] self.cachedFont = RFont(showUI=False) self.currentFont = CurrentFont() filtersList = self.filters.keys() if len(filtersList) > 0: self.currentFilterName = filtersList[0] else: self.currentFilterName = None self.fill = True self.observers = [ ('fontChanged', 'fontBecameCurrent'), ('fontChanged', 'fontDidOpen'), ('fontChanged', 'fontDidClose'), ] self.w = Window((100, 100, 800, 500), 'PenBall Wizard v{0}'.format(__version__), minSize=(500, 400)) self.w.filtersPanel = Group((0, 0, 300, -0)) self.w.filtersPanel.filtersList = List((0, 0, -0, -40), filtersList, selectionCallback=self.filterSelectionChanged, doubleClickCallback=self.filterEdit, allowsMultipleSelection=False, allowsEmptySelection=False, rowHeight=22) self.w.filtersPanel.controls = Group((0, -40, -0, 0)) self.w.filtersPanel.addFilter = SquareButton((0, -40, 100, 40), 'Add filter', sizeStyle='small', callback=self.addFilter) self.w.filtersPanel.addFilterChain = SquareButton((100, -40, 100, 40), 'Add operations', sizeStyle='small', callback=self.addFilterChain) self.w.filtersPanel.removeFilter = SquareButton((-100, -40, 100, 40), 'Remove filter', sizeStyle='small', callback=self.removeFilter) self.w.textInput = EditText((300, 0, -90, 22), '', callback=self.stringInput) self.w.generate = SquareButton((-90, 0, 90, 22), 'Generate', callback=self.generateGlyphsToFont, sizeStyle='small') self.w.preview = MultiLineView((300, 22, -0, -0)) self.w.switchFillStroke = SquareButton((-75, -40, 60, 25), 'Stroke', callback=self.switchFillStroke, sizeStyle='small') displayStates = self.w.preview.getDisplayStates() for key in ['Show Metrics','Upside Down','Stroke','Beam','Inverse','Water Fall','Multi Line']: displayStates[key] = False for key in ['Fill','Single Line']: displayStates[key] = True self.w.preview.setDisplayStates(displayStates) for callback, event in self.observers: addObserver(self, callback, event) self.updateControls() self.w.bind('close', self.end) self.launchWindow() self.w.open() def generateGlyphsToFont(self, sender): newFont = RFont(showUI=False) font = self.currentFont filterName = self.currentFilterName currentFilter = self.filters[filterName] if font is not None: glyphs = [font[glyphName] for glyphName in font.selection if glyphName in font] for glyph in glyphs: if len(glyph.components) > 0: for comp in glyph.components: baseGlyphName = comp.baseGlyph baseGlyph = font[baseGlyphName] baseFilteredGlyph = currentFilter(baseGlyph) newFont.insertGlyph(baseFilteredGlyph, baseGlyphName) newFont[baseGlyphName].unicode = baseFilteredGlyph.unicode filteredGlyph = currentFilter(glyph) if filteredGlyph is not None: newFont.insertGlyph(filteredGlyph, glyph.name) newFont.showUI() def generateGlyphsToLayer(self, layerName): font = self.currentFont filterName = self.currentFilterName currentFilter = self.filters[filterName] if font is not None: glyphs = [font[glyphName] for glyphName in font.selection if glyphName in font] for glyph in glyphs: if len(glyph.components) == 0: layerGlyph = glyph.getLayer(layerName) filteredGlyph = currentFilter(glyph) if filteredGlyph is not None: layerGlyph.appendGlyph(filteredGlyph) def updateFiltersList(self, selectedIndex=0): filtersList = self.filters.keys() self.w.filtersPanel.filtersList.set(filtersList) self.w.filtersPanel.filtersList.setSelection([selectedIndex]) def setArgumentValue(self, sender): value = sender.get() valueType = sender.type if valueType == 'bool': value = bool(value) key = sender.name if self.currentFilterName is not None: self.filters.setArgumentValue(self.currentFilterName, key, value) self.resetRepresentations() self.updatePreview() def resetRepresentations(self): font = self.currentFont self.cachedFont = RFont(showUI=False) if font is not None: for glyphName in self.glyphNames: if glyphName in font: font[glyphName].naked().destroyAllRepresentations() def processGlyphs(self): font = self.currentFont if font is not None: sourceGlyphs = [] for glyphName in self.glyphNames: if glyphName in font: glyph = font[glyphName] if glyph not in self.observedGlyphs: glyph.addObserver(self, 'glyphChanged', 'Glyph.Changed') self.observedGlyphs.append(glyph) sourceGlyphs.append(glyph) filterName = self.currentFilterName filteredGlyphs = self.filterGlyphs(filterName, sourceGlyphs, self.cachedFont) return filteredGlyphs return [] def filterGlyphs(self, filterName, glyphs, font): currentFilter = self.filters[filterName] filteredGlyphs = [] for glyph in glyphs: if len(glyph.components) > 0: for comp in glyph.components: baseGlyphName = comp.baseGlyph baseGlyph = glyph.getParent()[baseGlyphName] baseFilteredGlyph = currentFilter(baseGlyph) if baseFilteredGlyph is not None: font.insertGlyph(baseFilteredGlyph, baseGlyphName) filteredGlyph = currentFilter(glyph) if filteredGlyph is not None: font.insertGlyph(filteredGlyph, glyph.name) filteredGlyphs.append(font[glyph.name]) return filteredGlyphs def updatePreview(self, notification=None): glyphs = self.processGlyphs() self.w.preview.setFont(self.cachedFont) self.w.preview.set(glyphs) def updateControls(self): if self.currentFilterName is not None: if hasattr(self.w.filtersPanel, 'controls'): delattr(self.w.filtersPanel, 'controls') currentFilter = self.filters[self.currentFilterName] self.w.filtersPanel.controls = Group((0, 0, 0, 0)) hasSubfilters = self.filters.hasSubfilters(self.currentFilterName) if hasSubfilters: argumentBlocks = [(subfilterName, self.filters[subfilterName].arguments) for subfilterName, mode, source in currentFilter.subfilters] else: argumentBlocks = [(currentFilter.name, currentFilter.arguments)] gap = 5 height = gap end = 0 lineheight = 27 top = 35 boxes = 0 for j, (filterName, arguments) in enumerate(argumentBlocks): if len(arguments) > 0: blockfilterName = '{0}{1}'.format(filterName, j) setattr(self.w.filtersPanel.controls, blockfilterName, Box((5, 5, 0, 0))) block = getattr(self.w.filtersPanel.controls, blockfilterName) if hasSubfilters: _, filterMode, filterSource = currentFilter.subfilters[j] modes = '({0}) [{1}]'.format(filterMode, filterSource) block.modes = TextBox((-150, 11, -8, 12), modes, alignment='right', sizeStyle='mini') block.title = TextBox((8, 8, -150, 22), filterName.upper()) start = height boxHeight = top for i, (arg, value) in enumerate(arguments.items()): valueType = None if hasSubfilters: argumentName = currentFilter.joinSubfilterArgumentName(filterName, arg, j) if argumentName in currentFilter.arguments: value = currentFilter.arguments[argumentName] else: value = self.filters[filterName].arguments[argumentName] currentFilter.setArgumentValue(argumentName, value) else: argumentName = arg limits = currentFilter.getLimits(argumentName) if limits is None: limits = (0, 100) if isinstance(value, bool): setattr(block, arg, CheckBox((8, top + (i*lineheight), -8, 22), arg, value=value, callback=self.setArgumentValue, sizeStyle='small')) valueType = 'bool' elif isinstance(value, (str, unicode)): setattr(block, arg, EditText((8, top + (i*lineheight), -8, 22), value, callback=self.setArgumentValue, sizeStyle='small')) elif isinstance(value, (int, float)): parameter = VanillaSingleValueParameter(arg, value, limits=limits) setattr(block, arg, ParameterSliderTextInput(parameter, (8, top + (i*lineheight), -8, 22), title=arg, callback=self.setArgumentValue)) control = getattr(block, arg) control.name = argumentName control.type = valueType boxHeight += lineheight boxHeight += 12 block.setPosSize((5, start, -5, boxHeight)) height += boxHeight + gap height += 40 self.w.filtersPanel.filtersList.setPosSize((0, 0, -0, -height)) self.w.filtersPanel.controls.setPosSize((0, -height, -0, -45)) def stringInput(self, sender): text = sender.get() if self.currentFont is not None: cmap = self.currentFont.getCharacterMapping() self.glyphNames = splitText(text, cmap) else: self.glyphNames = [] self.updatePreview() def filterEdit(self, sender): filterName = self.currentFilterName if self.filters.hasSubfilters(filterName): self.buildFilterGroupSheet(filterName) else: self.buildFilterSheet(filterName) self.filterSheet.open() # def buildGenerationSheet(self, sender): # self.generationSheet = Sheet((0, 0, 400, 350), self.w) # self.generationSheet.title = TextBox((15, 15, -15, 22), u'Generate selected glyphs to:') def buildFilterSheet(self, filterName='', makeNew=False): sheetFields = { 'file': '', 'module': '', 'filterObjectName': '', 'limits': {}, 'arguments': {}, } if filterName != '': filterDict = self.filters[filterName].getFilterDict() for key in filterDict: if key == "arguments": entry = OrderedDict(filterDict[key]) else: entry = filterDict[key] sheetFields[key] = entry self.filterSheet = Sheet((0, 0, 400, 350), self.w) self.filterSheet.new = makeNew self.filterSheet.index = self.filters[filterName].index if not makeNew else -1 applyTitle = 'Add Filter' if filterName == '' else 'Update Filter' self.filterSheet.apply = SquareButton((-115, -37, 100, 22), applyTitle, callback=self.processFilter, sizeStyle='small') self.filterSheet.cancel = SquareButton((-205, -37, 80, 22), 'Cancel', callback=self.closeFilterSheet, sizeStyle='small') y = 20 self.filterSheet.nameTitle = TextBox((15, y, 100, 22), 'Filter Name') self.filterSheet.name = EditText((125, y, -15, 22), filterName) y += 22 tabs = ['module','file'] selectedTab = 0 if len(sheetFields['module']) >= len(sheetFields['file']) else 1 filterObjectName = sheetFields['filterObjectName'] y += 20 self.filterSheet.importPath = Tabs((15, y, -15, 75), tabs) self.filterSheet.importPath.set(selectedTab) modulePathTab = self.filterSheet.importPath[0] filePathTab = self.filterSheet.importPath[1] modulePathTab.pathInput = EditText((10, 10, -10, -10), sheetFields['module']) filePathTab.pathInput = EditText((10, 10, -110, -10), sheetFields['file']) filePathTab.fileInput = SquareButton((-100, 10, 90, -10), u'Add File…', sizeStyle='small', callback=self.getFile) y += 75 y += 10 self.filterSheet.filterObjectTitle = TextBox((15, y, 100, 22), 'Filter Object (pen, function)') self.filterSheet.filterObject = EditText((125, y, -15, 22), filterObjectName) y += 22 y += 20 columns = [ {'title': 'argument', 'width': 160, 'editable':True}, {'title': 'value', 'width': 71, 'editable':True}, {'title': 'min', 'width': 49, 'editable':True}, {'title': 'max', 'width': 49, 'editable':True} ] arguments = sheetFields['arguments'] limits = sheetFields['limits'] argumentItems = [] for key, value in arguments.items(): if isinstance(value, bool): value = str(value) elif isinstance(value, float): value = round(value, 2) argItem = { 'argument': key, 'value': value } if key in limits: minimum, maximum = sheetFields['limits'][key] argItem['min'] = minimum argItem['max'] = maximum argumentItems.append(argItem) buttonSize = 20 gutter = 7 self.filterSheet.arguments = List((15 + buttonSize + gutter, y, -15, -52), argumentItems, columnDescriptions=columns, allowsMultipleSelection=False, allowsEmptySelection=False) self.filterSheet.addArgument = SquareButton((15, -52-(buttonSize*2)-gutter, buttonSize, buttonSize), '+', sizeStyle='small', callback=self.addArgument) self.filterSheet.removeArgument = SquareButton((15, -52-buttonSize, buttonSize, buttonSize), '-', sizeStyle='small', callback=self.removeArgument) if len(argumentItems) == 0: self.filterSheet.removeArgument.enable(False) if filterName == '': self.currentFilterName = '' def buildFilterGroupSheet(self, filterName='', makeNew=False): subfilters = self.filters[filterName].subfilters if filterName in self.filters else [] subfilterItems = [{'filterName': subfilterName, 'mode': subfilterMode if subfilterMode is not None else '', 'source': source if source is not None else ''} for subfilterName, subfilterMode, source in subfilters] self.filterSheet = Sheet((0, 0, 400, 350), self.w) self.filterSheet.new = makeNew self.filterSheet.index = self.filters[filterName].index if not makeNew else -1 applyTitle = 'Add Operation' if filterName == '' else 'Update Operation' self.filterSheet.apply = SquareButton((-145, -37, 130, 22), applyTitle, callback=self.processFilterGroup, sizeStyle='small') self.filterSheet.cancel = SquareButton((-210, -37, 60, 22), 'Cancel', callback=self.closeFilterSheet, sizeStyle='small') y = 20 self.filterSheet.nameTitle = TextBox((15, y, 100, 22), 'Filter Name') self.filterSheet.name = EditText((125, y, -15, 22), filterName) y += 22 columns = [ {'title': 'filterName', 'editable': True, 'width': 140}, {'title': 'mode', 'editable': True, 'width': 89}, {'title': 'source', 'editable': True, 'width': 100} ] buttonSize = 20 gutter = 7 y += 20 self.filterSheet.subfilters = List((15 + buttonSize + gutter, y, -15, -52), subfilterItems, columnDescriptions=columns, allowsMultipleSelection=False, allowsEmptySelection=False) self.filterSheet.addSubfilter = SquareButton((15, -52-(buttonSize*2)-gutter, buttonSize, buttonSize), '+', sizeStyle='small', callback=self.addSubfilter) self.filterSheet.removeSubfilter = SquareButton((15, -52-buttonSize, buttonSize, buttonSize), '-', sizeStyle='small', callback=self.removeSubfilter) if len(subfilters) == 0: self.filterSheet.removeSubfilter.enable(False) y += 75 self.filterSheet.moveSubfilterUp = SquareButton((15, y, buttonSize, buttonSize), u'⇡', sizeStyle='small', callback=self.moveSubfilterUp) self.filterSheet.moveSubfilterDown = SquareButton((15, y + buttonSize + gutter, buttonSize, buttonSize), u'⇣', sizeStyle='small', callback=self.moveSubfilterDown) if filterName == '': self.currentFilterName = '' def addArgument(self, sender): argumentsList = self.filterSheet.arguments.get() argumentsList.append({'argument': 'rename me', 'value': 50, 'min': 0, 'max': 100}) if len(argumentsList) > 0: self.filterSheet.removeArgument.enable(True) self.filterSheet.arguments.set(argumentsList) def removeArgument(self, sender): argumentsList = self.filterSheet.arguments.get() if len(argumentsList) == 0: self.filterSheet.removeArgument.enable(False) selection = self.filterSheet.arguments.getSelection()[0] argumentsList.pop(selection) self.filterSheet.arguments.set(argumentsList) def addSubfilter(self, sender): subfiltersList = self.filterSheet.subfilters.get() subfilterDict = {'filterName': '{enter filter name}', 'mode': '', 'source': ''} subfiltersList.append(subfilterDict) if len(subfiltersList) > 0: self.filterSheet.removeSubfilter.enable(True) self.filterSheet.subfilters.set(subfiltersList) def removeSubfilter(self, sender): subfiltersList = self.filterSheet.subfilters.get() if len(subfiltersList) == 0: self.filterSheet.removeSubfilter.enable(False) selection = self.filterSheet.subfilters.getSelection()[0] subfiltersList.pop(selection) self.filterSheet.subfilters.set(subfiltersList) def moveSubfilterUp(self, sender): subfiltersList = self.filterSheet.subfilters.get() nItems = len(subfiltersList) if nItems > 1: selection = self.filterSheet.subfilters.getSelection()[0] if selection > 0: itemToMove = subfiltersList.pop(selection) subfiltersList.insert(selection-1, itemToMove) self.filterSheet.subfilters.set(subfiltersList) def moveSubfilterDown(self, sender): subfiltersList = self.filterSheet.subfilters.get() nItems = len(subfiltersList) if nItems > 1: selection = self.filterSheet.subfilters.getSelection()[0] if selection < nItems-1: itemToMove = subfiltersList.pop(selection) subfiltersList.insert(selection+1, itemToMove) self.filterSheet.subfilters.set(subfiltersList) def getFile(self, sender): path = getFile(fileTypes=['py'], allowsMultipleSelection=False, resultCallback=self.loadFilePath, parentWindow=self.filterSheet) def loadFilePath(self, paths): path = paths[0] self.filterSheet.importPath[1].pathInput.set(path) def closeFilterSheet(self, sender): self.filterSheet.close() delattr(self, 'filterSheet') def processFilter(self, sender): argumentsList = self.filterSheet.arguments.get() filterName = self.filterSheet.name.get() index = self.filterSheet.index filterDict = {} if len(filterName) > 0: sourceIndex = self.filterSheet.importPath.get() mode = ['module','file'][sourceIndex] importString = self.filterSheet.importPath[sourceIndex].pathInput.get() if len(importString) > 0: filterDict[mode] = importString filterObjectName = self.filterSheet.filterObject.get() filterDict['filterObjectName'] = filterObjectName if len(filterObjectName) > 0: for argItem in argumentsList: if 'argument' in argItem: key = argItem['argument'] if 'value' in argItem: value = self.parseValue(argItem['value']) if 'arguments' not in filterDict: filterDict['arguments'] = OrderedDict() filterDict['arguments'][key] = value if 'min' in argItem and 'max' in argItem and isinstance(value, (float, int)): try: mini, maxi = float(argItem['min']), float(argItem['max']) if 'limits' not in filterDict: filterDict['limits'] = {} filterDict['limits'][key] = (mini, maxi) except: pass if filterName in self.filters: self.filters.setFilter(filterName, filterDict) elif self.filterSheet.new == False: index = self.filterSheet.index self.filters.changeFilterNameByIndex(index, filterName) self.filters.setFilter(filterName, filterDict) elif self.filterSheet.new == True: self.filters.setFilter(filterName, filterDict) self.closeFilterSheet(sender) self.updateFiltersList(index) self.updateControls() self.resetRepresentations() self.updatePreview() def processFilterGroup(self, sender): filterName = self.filterSheet.name.get() subfiltersList = self.filterSheet.subfilters.get() isNew = self.filterSheet.new index = self.filterSheet.index subfilters = [] for item in subfiltersList: subfilterName = item['filterName'] if 'filterName' in item else '' mode = item['mode'] if 'mode' in item else None source = item['source'] if 'source' in item else None try: source = int(source) except: pass subfilters.append((subfilterName, mode, source)) if filterName in self.filters: self.filters.updateFilterChain(filterName, subfilters) elif not isNew: self.filters.changeFilterNameByIndex(index, filterName) self.filters.updateFilterChain(filterName, subfilters) elif isNew: self.filters.setFilterChain(filterName, subfilters) self.closeFilterSheet(sender) self.updateFiltersList(index) self.updateControls() self.resetRepresentations() self.updatePreview() def addFilter(self, sender): self.buildFilterSheet(makeNew=True) self.filterSheet.open() def addFilterChain(self, sender): self.buildFilterGroupSheet(makeNew=True) self.filterSheet.open() def addExternalFilter(self, filterName, filterDict): self.filters.addFilter(filterName, filterDict) self.updateFiltersList() def removeFilter(self, sender): filterName = self.currentFilterName self.filters.removeFilter(filterName) self.updateFiltersList() def filterSelectionChanged(self, sender): selectedFilterName = self.getSelectedFilterName() self.cachedFont = RFont(showUI=False) self.currentFilterName = selectedFilterName self.updateControls() self.updatePreview() def getSelectedFilterName(self): filtersList = self.w.filtersPanel.filtersList filterNamesList = filtersList.get() if len(filterNamesList): selectedIndices = filtersList.getSelection() if len(selectedIndices): selection = filtersList.getSelection()[0] return filterNamesList[selection] return None def switchFillStroke(self, sender): self.fill = not self.fill displayStates = self.w.preview.getDisplayStates() if self.fill == True: sender.setTitle('Stroke') displayStates['Fill'] = True displayStates['Stroke'] = False elif self.fill == False: sender.setTitle('Fill') displayStates['Fill'] = False displayStates['Stroke'] = True self.w.preview.setDisplayStates(displayStates) def parseValue(self, value): if isinstance(value, bool): value = bool(value) elif isinstance(value, (str, unicode)) and value.lower() == 'true': value = True elif isinstance(value, (str, unicode)) and value.lower() == 'false': value = False elif value is not '' or value is not None: try: value = float(value) except: pass return value def fontChanged(self, notification): if 'font' in notification: self.releaseObservedGlyphs() self.stringInput(self.w.textInput) self.currentFont = notification['font'] self.cachedFont = RFont(showUI=False) self.updatePreview() def releaseObservedGlyphs(self): for glyph in self.observedGlyphs: glyph.removeObserver(self, 'Glyph.Changed') self.observedGlyphs = [] def glyphChanged(self, notification): glyph = notification.object glyph.destroyAllRepresentations() self.updatePreview() def launchWindow(self): postEvent("PenBallWizardSubscribeFilter", subscribeFilter=self.addExternalFilter) def end(self, notification): self.filters.saveFiltersToJSON('/'.join([LOCALPATH, JSONFILE])) self.releaseObservedGlyphs() for callback, event in self.observers: removeObserver(self, event)
from vanilla import Window, Slider, Button # or from vanilla import * w = Window((400, 300), "My new window") w.mySlider = Slider((10, 10, 200, 20)) w.myButton = Button((10, 40, 100, 20), "Click me!") w.open()