Select glyphs with components


#1

Hello there everyone!

I am currently working on a bunch of scripts that I would like to get done while I am improving my scripting skills with Glyphs. The point is that I am now stuck in a script that goes through all of the glyphs of a font and if it has components it has to select that glyph. Maybe it is because I don’t fully understand the structure of a Glyphs file while coding but I would really appreaciate any help in this sense, also some advice while iterating through a Glyphs file:

Here’s the current code that returns the glyph name followed by (what it is supposed to be) the amount of components but obviously it isn’t because my A doesn’t have any component:

f = Glyphs.font
glyphs = f.glyphs
layer = f.selectedLayers[0]

for g in glyphs:
    print(g.name)
    components = 0

    for c in layer.components:
       	components +=1

	    print(components)

    if components > 0:
    	g.selected
    else:
    	continue

Thank you so much for any time that you could invest on it.


#2

Let me help you with that. There was a time I was trying to write Python code cluelessly, and I share your frustration when you start. I’ve commented and added a lot to your code.

f = Glyphs.font
g = f.glyphs # it doesn't seem necessary. I'll explain later.

# this is only the first layer of whatever selected glyphs you have. The rest will be ignored.
# if you selected A B C, then it's the glyph A in the current master. Keep that in mind for later explanation.
layer = f.selectedLayers[0]

for g in glyphs: # this is the only place "glyphs" is used. This is better : for g in f.glyphs:
    print(g.name)
    components = 0

	# this is where you use "layer", which was defined wrongly.
	# bringing back the prior example, it will check A as many times as the number of glyphs in the font.
	# it doesn't make sense, does it?
    for c in layer.components:
       	components +=1
	    print(components)

    if components > 0: # What do you want to do after selection? It's probably better to write an actual action.
    	g.selected # because of how Glyphs works, you can't select a glyph, you select a layer.
    else:
    	continue
	
# now, the whole "for" loop thing above can be written as follows:
# (delete or comment-out your original "for" loop for this to work)
compoLayers = [] # empty list. I want to collect layers in it.
for g in f.glyphs:
	
	# l is the glyph only in the current master, assuming that's what you want
	l = g.layers[f.selectedFontMaster.id]
	
	# len() is a number, and the size of a string/list/dictionary/anything.
	# You do not have to do for loop and maths to know the number of components.
	compoCount = len(l.components)
	print(g.name, compoCount)
	
	if compoCount > 0:
		compoLayers.append(l)

# Things you can do 1: I can use compoLayers to open a new tab.
f.newTab() # opening empty tab
# f.tabs[-1] is the latest tab you just opened
f.tabs[-1].layers = compoLayers # throw in that list of layers here

# Things you can do 2: I can mark the layers in red:
for l in compoLayers:
	l.setColorIndex_(0) # Red=0, orange=1, brown=2...
	# or should I mark the GLYPH in purple?
	l.parent.setColorIndex_(8) # GSlayer class belongs to GSGlyph. A parent of a layer is a glyph.

# Things you can do 3: let's decompose them!
for l in compoLayers:
	l.decomposeComponents()

#3

or even shorter, if you digested what @Tosche wrote and explained. You don’t need to “collect” the glyphs or layers if you want to apply your action directly when the layer is found (like coloring glyphs). If you want to open them in a new tab it is indeed better to collect them all first, like you see in Tosche’s version. Also you don’t need to check for components count. if layer.components: won’t continue if there aren’t any.

f = Font
for glyph in f.selection:
	masterLayer = glyph.layers[f.selectedFontMaster.id] # this only works if you want the selected Master
	if masterLayer.components:
		masterLayer.setColorIndex_(1) # or masterLayer.parent.setC... if you want the glyph`

#4

Hello Tosche and Mark,

First of all, let me thank you a lot the time (even if it was a small or massive amount) you have invested in showing me how to solve this. This is impressive.

So, back to the script, now I see that instead of looping in a general layer.components I need to access the glyph layer. @Tosche, it is really helpful the three options that you brought in order to use this script but what if I want to select that glyph that has l.components > 0 instead of open a new tab with it or mark it in a certain colour? In that case, would make sense to use l.select having your code as a starting point or I this would involve iterating through all of the masters? As far as I know I you have one glyph selected in one master it is also selected in the other ones, am I wrong?

Again, thank you so much for your help. In the meantime I am going to work on with with your observations and try different options.


#5

I guess there’s not a function to select a glyph in the glyph level, right?


#6

No, it is always a layer that is displayed and selected.

What do you need glyph selections for?


#7

I was mainly thinking that I might be helpful to have a script that selects only glyphs with components, anchors, or whatever. Sometimes it is helpful to group them and check that all of the glyphs that must have components, for instance, do have one and it is not decomposed.

I thought that I could do something like you did with Select same colour but apparently it is not easy or at least that is how it looks in the code of your script. Any suggestion?

Thank you so much.


#8

Components have to be on a layer. And in a glyph, it is entirely possible that one layer has components and the other does not.

I suggest you either check for layers of the current master, or simply go through all layers and open them in a new tab, e.g. after you collected them in affectedLayers you could do this:

newTab = thisFont.newTab()
newTab.layers = affectedLayers

#9

So, for instance, I should keep in the level of the first layer (zero) and check if there’s a component in there. If so, could I use a function that would select that entire glyph if it has components in its first layer?

Thank you again!


#10

Wait a minute, you want to create a selection in Font view? Yes, that is possible. Create a list of glyphs, then set Font.selection to it. Here is a quick sample:

thisFont = Glyphs.font # frontmost font
thisMaster = thisFont.selectedFontMaster # active master
affectedGlyphs = []

for thisGlyph in thisFont.glyphs:
	# the layer with the master ID is the layer for that master:
	currentLayer = thisGlyph.layers[thisMaster.id] 
	if currentLayer.components:
		affectedGlyphs.append(thisGlyph)
		
if affectedGlyphs:
	thisFont.selection = affectedGlyphs
else:
	Message(title="No Components Found", message="Could not fond any components in the current master.", OKButton="Too bad")

You see I test not the first layer, but the current master layer of the glyph.


#11

That is it! Thank you so much @mekkablue. While getting used to the structure of a Glyphs file I have just realised that I also need to refer levels by their own names. Thank you so much, Rainer, this is working just as I wanted!


#12

Cool! And sorry I didn’t get right away what you were trying to do.


#13

Do not apologise! It was 100% my fault because I wasn’t using the correct word.