Skip to main content
simene49649793
Known Participant
December 30, 2016
Answered

Getting the children of a selected group

  • December 30, 2016
  • 6 replies
  • 5447 views

I'm still trying to get the hang of Action Manager code, but I keep hitting a wall. I'm trying to figure out how I can get all of the children of the selected group, but I can't find a way to do this.

- How can I retrieve all elements below a group (LayerSet) through AM code?

- Is it possible to retrieve the parent of a specific layer?

- Is it possible to collapse/expand groups with AM code?

How do you guys attack these kinds of challenges? I have googled and searched github for these questions, but I haven't found anything of help. I feel like there's just a few of you that knows AM code, how did you plow your way into learning AM coding? I mean, the basic operations that can be recorded by the listener is doable even for me, but approaching challenges like the one's I have now, I don't even know where to start?

This topic has been closed for replies.
Correct answer JavierAroche

Hey simene49649793​, these 2 functions should get you half way there.

getDocumentInfo() - Retrieves the document information. Super cool function that I found in one of these forums. This is similar to Generator's getDocumentInfo. It has some optional params to retrieve more specific data.

This AM function will return the document object as a string, because ExtendScript is very limited unfortunately. You will have to parse it using a JSON polyfill. I use this one: https://github.com/JavierAroche/descriptor-info/blob/master/example/helpers/JSON.jsx

You can then grab the document.layers key which includes all the layers in the document.

function getDocumentInfo() {

  // Build Action Descriptor

  var ref = new ActionReference();

  var desc = new ActionDescriptor();

  var psJSON = stringIDToTypeID("json");

  ref.putProperty(charIDToTypeID('Prpr'), psJSON);

  ref.putEnumerated(stringIDToTypeID("document"), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));

  desc.putReference(charIDToTypeID('null'), ref);

// desc.putBoolean(stringIDToTypeID("expandSmartObjects"), true);

// desc.putBoolean(stringIDToTypeID("selectedLayers"), true);

// desc.putBoolean(stringIDToTypeID("getTextStyles"), true);

// desc.putBoolean(stringIDToTypeID("getFullTextStyles"), true);

// desc.putBoolean(stringIDToTypeID("getDefaultLayerFX"), true);

// desc.putBoolean(stringIDToTypeID("getPathData"), true);

  return executeAction(charIDToTypeID( "getd" ), desc, DialogModes.NO).getString(psJSON);

}

getSelectedLayerIDs() - Retrieves the selected layer IDs as an array.

function getSelectedLayerIDs() {

  var selectedLayerIDs = [];

  // Filter selected layers

  var docRef = new ActionReference();

  docRef.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );

  var docDesc = executeActionGet(docRef);

  // Get target layer ids

  if( docDesc.hasKey(stringIDToTypeID('targetLayersIDs'))) {

  targetLayersIDsDesc = docDesc.getList( stringIDToTypeID('targetLayersIDs'));

  for(var ii = 0; ii < targetLayersIDsDesc.count; ii++) {

  selectedLayerIDs.push(Number(targetLayersIDsDesc.getReference( ii ).getIdentifier()));

  }

  }

  return selectedLayerIDs;

}

Once you have the document layers as an object and the selected layer IDs, you can simply iterate through all the layers (recursive function) until you find the one you're looking for. If you're only targeting one group layer, it will be a lot easier.

- Is it possible to retrieve the parent of a specific layer?

This one is tricky, but if you're comfortable with recursive functions, you should be able to get the parent layer of the any layer in a document.

collapseAllGroups() - Collapses all document groups.

function collapseAllGroups() {

    try {

        var idcollapseAllGroupsEvent = stringIDToTypeID( "collapseAllGroupsEvent" );

        var desc13 = new ActionDescriptor();

        return executeAction( idcollapseAllGroupsEvent, desc13, DialogModes.NO );

    } catch(err) {}

}

How do you guys attack these kinds of challenges? I have googled and searched github for these questions, but I haven't found anything of help. I feel like there's just a few of you that knows AM code, how did you plow your way into learning AM coding? I mean, the basic operations that can be recorded by the listener is doable even for me, but approaching challenges like the one's I have now, I don't even know where to start?

It takes a lot of practice, patience, googling and spending lots of hours in forums

DBarranca​ is authoring a Photoshop Scripting book. That will be great for people trying to learn all of this.

http://www.davidebarranca.com/2016/10/new-course-photoshop-scripting/

Some resources that have helped me a lot:

I made this little module to better understand Action Descriptors.

https://github.com/JavierAroche/descriptor-info

Hope it helps!

6 replies

simene49649793
Known Participant
January 1, 2017

pixxxel schubser: That was a clever way to do it! I've been able to retrieve all children of selected groups using this method, works great I would wish it was possible to retrieve them without making selections, but if I can figure out how to collapse groups afterwards it won't matter Also, using this method I think it should be easy to find the parent layer also by just decreasing the layer index until a hit a LayerSet, I'll try that later. Thank you

JavierAroche: That getDocumentInfo was awesome I couldn't get collapseAllGroups to work though, it doesn't seem to do anything Thank you for all of the resources

Thank you everyone, this is extremely interesting and helpful! I'm still looking for a way to collapse/expand LayerSets, so if anyone have any ideas on how to do this I'd be really interested to hear how it can be done Happy new year guys, and thanks again

Inspiring
January 1, 2017

You can actually use AM to get the childrens of a layerset, without selecting each layer, that's the beauty of AM. I'm using it to get the chlid layers like this: I'm starting on the parent layerset and set a variable x to 1, then with a loop I go backwards by using the putIndex method for the layer class, with index set to i--, and everytime I get an LayerSectionStart, I'm incrementing the x var, and if I get an LayerSectionEnd I'm decrementing x, when I get x to be equal to 0, I stop the loop, and this way I know that all the layers gathered in an array while the loop run, are the child layers. I usually gather the id's of the layers, the idx and the names...this way I can use another function to select all the layers by Id.

As for the closing of a layer group I've solved it by selecting all the child layers, unparent them and group them into another set. And set the properties of the second set similar to the first group...Expanding the layer set it's easy you just have to select one child of that layer set. please take a look of the script I've mention to see how I've done it.

Thank you. And Happy New Year to you all.

Inspiring
December 31, 2016

I've aproach these problems into my toggle open close groups script, there is also a function that can get the children and one that can get the parent with AM. You will find them in this collection:

GitHub - cbuliarca/BCM_exetndShortcutsUI: Collection of Photoshop scripts accesed with an UI, main focused on game textu…

just go to the BCM_ToolsUi/ extras/scripts and here the file with all the necessay functions  is mb_utils.jsxinc

pixxxelschubser
Community Expert
Community Expert
December 31, 2016

Great.

The TO simene49649793​ ask: "It is possible? How can I do this?"

And we start a very usefully discussion about: What is the first class way to do this?

I will also take a look next year on the several links. Thx for this.

I wish you all the best and a Happy New Year 2017

Jarda Bereza
Inspiring
December 31, 2016

If want select all nested layers in one selected group then you can simply

1) create group - Ctrl + G

2) remove group - Ctrl + Shift + G

3) now you have all nested layers selected (recursively)

4) get selected layers indexes/ids

This should have good performance for complex nested layers.

Parent of specific layer

1) select your layer by index/id

2) app.activeDocument.activeLayer.parent.id

pixxxelschubser
Community Expert
Community Expert
December 31, 2016

Hi Jarda Bereza,

did you try the performance of the code in the last three posts? Do you have problems with the performance of it?

Perhaps you can show a working code snippet for making a time comparison? I like to learn.

Jarda Bereza
Inspiring
December 31, 2016

pixxxel schubser  napsal(a):

Perhaps you can show a working code snippet for making a time comparison? I like to learn.

I did some time measurement.

Document has 1634 layers, 287 groups and 11136px × 6740px

1) 64 layers selected in 5 groups - 1402ms

2) 141 layers selected in 32 groups - 291ms

(ExtendScript Toolkit his buil-in time measurement features.)

Limitations:

- you can't group artboards

- you can create only 10 nested groups

If you select one layer by ID/index it should be +- 20ms per layer

DOM recursion is slow. With ActionManager you can read layers, they don't have tree structure but it is linear structure. So it is very fast, but you probably need select layers one by one. You can select layers range, but I am not sure how much reliably it is.

Code here: http://pastebin.com/dgUqSb1n

SuperMerlin
Inspiring
December 31, 2016

The code is here to get layerset layers...

https://www.ps-scripts.com/viewtopic.php?f=68&t=11268

JavierAroche
JavierArocheCorrect answer
Inspiring
December 31, 2016

Hey simene49649793​, these 2 functions should get you half way there.

getDocumentInfo() - Retrieves the document information. Super cool function that I found in one of these forums. This is similar to Generator's getDocumentInfo. It has some optional params to retrieve more specific data.

This AM function will return the document object as a string, because ExtendScript is very limited unfortunately. You will have to parse it using a JSON polyfill. I use this one: https://github.com/JavierAroche/descriptor-info/blob/master/example/helpers/JSON.jsx

You can then grab the document.layers key which includes all the layers in the document.

function getDocumentInfo() {

  // Build Action Descriptor

  var ref = new ActionReference();

  var desc = new ActionDescriptor();

  var psJSON = stringIDToTypeID("json");

  ref.putProperty(charIDToTypeID('Prpr'), psJSON);

  ref.putEnumerated(stringIDToTypeID("document"), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));

  desc.putReference(charIDToTypeID('null'), ref);

// desc.putBoolean(stringIDToTypeID("expandSmartObjects"), true);

// desc.putBoolean(stringIDToTypeID("selectedLayers"), true);

// desc.putBoolean(stringIDToTypeID("getTextStyles"), true);

// desc.putBoolean(stringIDToTypeID("getFullTextStyles"), true);

// desc.putBoolean(stringIDToTypeID("getDefaultLayerFX"), true);

// desc.putBoolean(stringIDToTypeID("getPathData"), true);

  return executeAction(charIDToTypeID( "getd" ), desc, DialogModes.NO).getString(psJSON);

}

getSelectedLayerIDs() - Retrieves the selected layer IDs as an array.

function getSelectedLayerIDs() {

  var selectedLayerIDs = [];

  // Filter selected layers

  var docRef = new ActionReference();

  docRef.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );

  var docDesc = executeActionGet(docRef);

  // Get target layer ids

  if( docDesc.hasKey(stringIDToTypeID('targetLayersIDs'))) {

  targetLayersIDsDesc = docDesc.getList( stringIDToTypeID('targetLayersIDs'));

  for(var ii = 0; ii < targetLayersIDsDesc.count; ii++) {

  selectedLayerIDs.push(Number(targetLayersIDsDesc.getReference( ii ).getIdentifier()));

  }

  }

  return selectedLayerIDs;

}

Once you have the document layers as an object and the selected layer IDs, you can simply iterate through all the layers (recursive function) until you find the one you're looking for. If you're only targeting one group layer, it will be a lot easier.

- Is it possible to retrieve the parent of a specific layer?

This one is tricky, but if you're comfortable with recursive functions, you should be able to get the parent layer of the any layer in a document.

collapseAllGroups() - Collapses all document groups.

function collapseAllGroups() {

    try {

        var idcollapseAllGroupsEvent = stringIDToTypeID( "collapseAllGroupsEvent" );

        var desc13 = new ActionDescriptor();

        return executeAction( idcollapseAllGroupsEvent, desc13, DialogModes.NO );

    } catch(err) {}

}

How do you guys attack these kinds of challenges? I have googled and searched github for these questions, but I haven't found anything of help. I feel like there's just a few of you that knows AM code, how did you plow your way into learning AM coding? I mean, the basic operations that can be recorded by the listener is doable even for me, but approaching challenges like the one's I have now, I don't even know where to start?

It takes a lot of practice, patience, googling and spending lots of hours in forums

DBarranca​ is authoring a Photoshop Scripting book. That will be great for people trying to learn all of this.

http://www.davidebarranca.com/2016/10/new-course-photoshop-scripting/

Some resources that have helped me a lot:

I made this little module to better understand Action Descriptors.

https://github.com/JavierAroche/descriptor-info

Hope it helps!

pixxxelschubser
Community Expert
Community Expert
December 30, 2016

Hi simene49649793,

there is a script study years ago written by Michael L. Hale (RIP)

This should help you out:

// written by Michael L.Hale

// selektiert alle Ebenen in der aktiven Gruppe

// assumes the group you want to select the layer in is the activeLayer

var groupStartIdx = getActiveLayerIndex();

app.activeDocument.activeLayer = app.activeDocument.activeLayer.layers[app.activeDocument.activeLayer.layers.length-1];

var groupEndIdx = getActiveLayerIndex();

for(l=groupEndIdx;l<groupStartIdx;l++){

    addToLayerSelectionByIndex( l );

}

  

function addToLayerSelectionByIndex( idx ) {

    var desc = new ActionDescriptor();

        var ref = new ActionReference();

        ref.putIndex( charIDToTypeID('Lyr '), idx );

    desc.putReference( charIDToTypeID('null'), ref );

    desc.putEnumerated( stringIDToTypeID('selectionModifier'), stringIDToTypeID('selectionModifierType'), stringIDToTypeID('addToSelection') );

    desc.putBoolean( charIDToTypeID('MkVs'), false );

    executeAction( charIDToTypeID('slct'), desc, DialogModes.NO );

};

function getActiveLayerIndex() {

    var ref = new ActionReference();

    ref.putProperty( charIDToTypeID("Prpr") , charIDToTypeID( "ItmI" ));

    ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );

    var res = executeActionGet(ref).getInteger(charIDToTypeID( "ItmI" )) - Number( hasBackground() );

    return res;

};

function hasBackground(){

    var res = undefined;

    try{

        var ref = new ActionReference();

        ref.putProperty( 1349677170 , 1315774496);

        ref.putIndex( 1283027488, 0 );

        executeActionGet(ref).getString(1315774496 );;

        res = true;

    }catch(e){ res = false}

    return res;

};

Have fun