Create List Filter via Script

Is this possible? Couldn’t find it in the docu.

unfortunately not. At least not in a sane way. Can you add a feature request to: http://bugreport.glyphsapp.com

Still the same answer here?

not sure if there’s a better way, but I do it like this:

filterContent = {"Count" : 1,
				 "ShallHaveCount" : 1,
				 "icon" : "CustomFilterListTemplate",
				 "isLeaf" : 1,
				 "list" : ["A"],
				 "name" : "myNewFilter"
				 }

filterController = Font.fontView.glyphsGroupViewController()
filters = filterController.glyphGroups()[2]["subGroup"]
filters.append(filterContent)
filterController.update()
2 Likes

That looks very good.

Any updates on this? It doesn’t seem to work anymore with Glyphs 3.

from Foundation import NSMutableDictionary
font = Glyphs.font
filterController = font.fontView.glyphsGroupViewController()
glyphGroups = filterController.glyphGroups()
filters = glyphGroups[3]
print(filters)
flt = NSMutableDictionary.dictionaryWithObjectsAndKeys_(
	True, "isLeaf",
	"MyListFilter", "name",
	["A", "B"], "list",
	"CustomFilterListTemplate", "icon",
	None
)
# Neither of those two options work:
# filters.insertObject_inSubItemsAtIndex_(flt, 0)
# filters.insertSubItems_atIndex_([flt], 0)
filterController.update()

Alternatively, is there a way to filter the font view from within a script temporarily without adding a filter to the sidebar?

Alternatively, is there a way to filter the font view from within a script temporarily without adding a filter to the sidebar?

I have thought the same thing a few years ago:

class SearchState(object):
	ALL     = 0
	NAME    = 1
	UNICODE = 2
	NOTE    = 3
	@staticmethod
	def make_key(key, searchField):
		if Glyphs.versionNumber < 3.0:
			return 'GlyphList{0}'.format(key)
		return searchField.key_(key)
	@classmethod
	def set_state_in_font(cls, font, state, search=False):
		controller = font.fontView
		searchField = controller.glyphSearchField()
		searchField.setStringValue_(state.pattern)
		Glyphs.defaults[cls.make_key('SearchState', searchField)] = state.mode
		Glyphs.defaults[cls.make_key('SearchStateMatchCase', searchField)] = state.match_case
		Glyphs.defaults[cls.make_key('SearchStateRegex', searchField)] = state.regex
		if search:
			controller.filterGlyphs_(searchField)
	@classmethod
	def from_font(cls, font):
		controller = font.fontView
		searchField = controller.glyphSearchField()
		return cls(
			searchField.stringValue(),
			Glyphs.defaults[cls.make_key('SearchState', searchField)],
			bool(Glyphs.defaults[cls.make_key('SearchStateMatchCase', searchField)]),
			bool(Glyphs.defaults[cls.make_key('SearchStateRegex', searchField)])
		)
	def __init__(self, pattern, mode=0, match_case=False, regex=False):
		self.pattern    = pattern
		self.mode       = mode if not regex else self.NAME
		self.match_case = match_case
		self.regex      = regex
	def perform_temporal_search(self, font):
		prev = self.from_font(font)
		try:
			self.set_state_in_font(font, self, search=True)
		finally:
			self.set_state_in_font(font, prev, search=False)
	@classmethod
	def reset(cls, font):
		cls.set_state_in_font(font, cls.from_font(font), search=True)

if __name__ == '__main__':
	SearchState('A|B|C', mode=SearchState.NAME, match_case=True, regex=True).perform_temporal_search(Glyphs.font)

I came up with the code above. My idea at that time was to perform a regex search, and then restore only the UI state as if nothing happened.

To add a new filter:

GSSidebarItem = objc.lookUpClass("GSSidebarItem")
font = Glyphs.font
filterController = font.fontView.glyphsGroupViewController()
glyphGroups = filterController.glyphGroups()
filters = glyphGroups[3]
print(filters)
newItem = GSSidebarItem.new()
newItem.setName_("MyListFilter1")
newItem.setCoverage_(["A", "B"])
newItem.setIconName_("CustomFilterListTemplate")
newItem.setItemType_(3)
filters.insertObject_inSubItemsAtIndex_(newItem, len(filters.subItems()))
filterController.didChangeSidebarItem_(filters)

And to temporarily change the sorting in Font view:

from AppKit import NSSortDescriptor
font = Glyphs.font
glyphsArrayController = font.fontView.glyphsArrayController()
glyphsArrayController.setSortDescriptors_([NSSortDescriptor.alloc().initWithKey_ascending_("layer0.LSB", True)])

And when you like to use a predefined list of names:

GSSortDescriptorNameList = objc.lookUpClass("GSSortDescriptorNameList")
font = Glyphs.font
glyphsArrayController = font.fontView.glyphsArrayController()
sortDescriptor = GSSortDescriptorNameList.alloc().initWithKey_ascending_("name", True)
sortDescriptor.setReferenceList_(["z", "y", "x"])
glyphsArrayController.setSortDescriptors_([sortDescriptor])
5 Likes

Whoa. I can even set up a filter smarter than a smart filter:

newItem = GSSidebarItem.new()
newItem.setName_("MySmartFilter1")
newItem.setPredicate_(NSPredicate.predicateWithFormat_("""\
	NOT (name MATCHES '[A-Z].+')
	AND ((name CONTAINS 'acute') OR (name CONTAINS 'grave'))
	AND (export == 1)
	AND ((layer0.countOfPaths > 0) OR (layer0.countOfComponents > 0))
"""))
newItem.setIconName_('CustomFilterPredicateTemplate')
newItem.setItemType_(3)

This will be definitely a time saver for me. Thanks for the snippets!

2 Likes

I am trying to use this code to implement a temporarily change to the sorting in the Font view using a pre-defined list of names. I’m able to see the Font View change successfully, but after a second or two, it updates again and restores back to the standard view. This didn’t happen when I was running the plugin on Glyphs2, so that leads me to think this is something new in Glyphs3. Is there some way to prevent the fontview from updating again or to preserve the predefined sort for longer?

Following up on this @GeorgSeifert. Any idea why this is happening?

I used the script and the sorting is still there even after I worked on the font for some time.

But any selection in the sidebar and adding/removing glyphs will most likely reset it.

Ah, thanks! It seems one of the instances of Glyphs.font.fontView.glyphsGroupViewController().update() in my script was being re-run for some reason, clearing the view.

I’m working on a Python-based filter using the technique described in this thread. I’d like the order of the subset of glyphs to be as it would be in unfiltered view. Is there a simple way to achieve this?

What exactly are you doing. A NSPredicate based filter should not alter the sorting. For list filters, you control the order yourself.

I am working on a filter that shows all glyphs not found (by name) in the other opened font (only works with exactly 2 fonts open).

This is what I have got so far:

#MenuTitle: Glyphset Diff
# encoding: utf-8

__doc__='''
Shows the glyphs that are not present in the other font.
'''

from GlyphsApp import *

if (len(Glyphs.documents) == 2):
	thisFont = Glyphs.currentDocument.font
	otherDocument = [doc for doc in Glyphs.documents if doc is not Glyphs.currentDocument][0]
	otherFont = otherDocument.font
	thisGlyphnames = set([glyph.name for glyph in thisFont.glyphs])
	otherGlyphnames = set([glyph.name for glyph in otherFont.glyphs])
	diff = list(thisGlyphnames - otherGlyphnames)
	# https://forum.glyphsapp.com/t/create-list-filter-via-script/2134/7
	GSSortDescriptorNameList = objc.lookUpClass("GSSortDescriptorNameList")
	glyphsArrayController = thisFont.fontView.glyphsArrayController()
	sortDescriptor = GSSortDescriptorNameList.alloc().initWithKey_ascending_("name", True)
	sortDescriptor.setReferenceList_(diff)
	glyphsArrayController.setSortDescriptors_([sortDescriptor])
else:
	Message('Please make sure that\nexactly two fonts are open', '')

This works but the order is somewhat random. I’d like the order of the glyphs to be as it would be in unfiltered view.

1 Like

You might not need that GSSortDescriptorNameList part.

And have you checked it diff is in the order you expect?

Oh, now I understand. It was the use of sets that jumbled the order!

I can simply use:

diff = [glyph.name for glyph in thisFont.glyphs if not glyph.name in otherGlyphnames]

Just tried this and it gives me the desired order.

Seems like GSfont.glyphs always returns a nicely sorted list, or was I just lucky?

How would it work without a GSSortDescriptorNameList? Can it be simplified?

If you only set the filter, the underlying order should be preserved.

You were looking at the wrong sample code. It was about sorting, not filtering.

# MenuTitle: Glyphset Diff
# encoding: utf-8

__doc__ = '''
Shows the glyphs that are not present in the other font.
'''

from GlyphsApp import *

if len(Glyphs.documents) == 2:
	thisFont = Glyphs.documents[0].font
	otherFont = Glyphs.documents[1].font

	otherGlyphnames = set(otherFont.glyphNames())
	diff = [glyph for glyph in thisFont.glyphs if glyph.name not in otherGlyphnames]
	fontView = thisFont.fontView
	glyphsArrayController = fontView.glyphsArrayController()
	
	predicate = fontView.predicateForNameList_negative_(diff, False)
	glyphsArrayController.setFilterPredicate_(predicate)
else:
	Message('Please make sure that\nexactly two fonts are open', '')
1 Like

Ah this is the original reason why I asked about this API :sweat_smile: