• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Best clean way to get number of selected layers within script.

Participant ,
May 15, 2020 May 15, 2020

Copy link to clipboard

Copied

So,

I'm looking for the best way to count the number of selected layers in a PS document.

 

I've been looking around and stumbled on this piece of code : https://graphicdesign.stackexchange.com/questions/104786/is-it-possible-to-count-the-number-of-layer...

 

It does work, but it works by creating a new group before counting the layers, then undoing it all.  This fires a "Mk " event.  And since the script has an EventListenner looking out for "Mk " events, well... I get stuck in an endless loop...

 

Isn't there a simple and effective way to count the selected layers ?  Actually... I just need to know if there's more than one selected...

 

Many thanks.

 

J.

TOPICS
Actions and scripting , SDK

Views

2.7K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Guide , May 15, 2020 May 15, 2020
s2t = stringIDToTypeID;
(r = new ActionReference).putProperty(s2t('property'), p = s2t('targetLayers'));
r.putEnumerated (s2t ('document'), s2t ('ordinal'), s2t ('targetEnum'));
n =  executeActionGet (r).hasKey(p) ?  executeActionGet (r).getList(p).count : 0;
alert (n)

Votes

Translate

Translate
Adobe
Guide ,
May 15, 2020 May 15, 2020

Copy link to clipboard

Copied

s2t = stringIDToTypeID;
(r = new ActionReference).putProperty(s2t('property'), p = s2t('targetLayers'));
r.putEnumerated (s2t ('document'), s2t ('ordinal'), s2t ('targetEnum'));
n =  executeActionGet (r).hasKey(p) ?  executeActionGet (r).getList(p).count : 0;
alert (n)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 15, 2020 May 15, 2020

Copy link to clipboard

Copied

Hi, This is a nice solution. Could you please explain me (just want to learn), how exactly it is working.

Highly appreciated!

 

Thanks

Best regards

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
May 15, 2020 May 15, 2020

Copy link to clipboard

Copied

This is the code for the action manager, the Photoshop subsystem. Action manager objects are stored in ActionDescriptors. The names of properties and methods are encoded by numeric keys, which we can receive and convert using functions like stringIDToTypeID.
In that case, we get access to the ActionDescriptor of the current document using the ActionReference (this is something like a link to the desired object). Using the ActionReference, we describe the property that we need. We need to get a list of selected layers - it is stored as an ActionList named 'targetLayers'. To do that we create a new ActionReference, first put the name of the property we need into it (so as not to request the entire document object):
(r = new ActionReference) .putProperty (s2t ('property'), p = s2t ('targetLayers'));
then we indicate that this property should be taken from the active document:
r.putEnumerated (s2t ('document'), s2t ('ordinal'), s2t ('targetEnum'));
Since layers may or may not be selected, then using executeActionGet and the .hasKey () function, we check if we have the property we need, if so, we write the number of elements in the list of selected layers to a variable
executeActionGet (r) .getList (p) .count , if not, then write to the variable 0.

At the same time, it is not difficult to obtain the index of each layer from the resulting list and with its help obtain all the necessary properties of each selected layer.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jul 19, 2023 Jul 19, 2023

Copy link to clipboard

Copied

I've been wanting to understand this markup a bit better, as the recorded markup is sometimes verbose and redundant. This is a start. Thanks!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jul 19, 2023 Jul 19, 2023

Copy link to clipboard

Copied

@Deleted User 
You mentioned it wouldn't be difficult to get the list of IDs from this, but I'm not sure I understand how?
I tried an alert/dump on all parts of your script, but at most it seems to have count associated with it.
I thought it was most likely here:
executeActionGet(r).getList(p)
But again it's just the count. Any tips?


Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 20, 2023 Jul 20, 2023

Copy link to clipboard

Copied

var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var docDesc = executeActionGet(ref);
var targetLayersIDs = docDesc.getList(stringIDToTypeID("targetLayersIDs"));
var theIDs = new Array;
for (var m = 0; m < targetLayersIDs.count; m++) {
	theIDs.push(targetLayersIDs.getReference(m).getIdentifier())
};
alert ("the ids are\n"+theIDs.join("\n"));

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jul 20, 2023 Jul 20, 2023

Copy link to clipboard

Copied

Thanks! In testing it out, I've also realized that the id and itemIndex don't let you manipulate the layer directly: for example app.activeDocument.layers[858] will produce an error for id and app.activeDocument.layers[858] will produce an error for id and app.activeDocument.layers[57] will produce an error for itemIndex specifically. I did find addSelectedLayer() from Flatten All Layers.jsx, which seems to bridge the gap. The script I'm currently working on, sizes the active layer based on the layer below. Ideally I'd be able to reference the second layer without selecting it, but I'm not sure that's necessary?

 

    ///////////////////////////////////////////////////////////////////////////////
    // From: Flatten All Layer Effects.jsx
    // Function: getSelectedLayers
    // Usage: creates and array of the currently selected layers
    // Input: <none> Must have an open document
    // Return: Array selectedLayers
    ///////////////////////////////////////////////////////////////////////////////
function addSelectedLayer( layerIndexOrName ) {
	try {
		var id243 = charIDToTypeID( "slct" );
		var desc46 = new ActionDescriptor();
		var id244 = charIDToTypeID( "null" );
		var ref44 = new ActionReference();
		var id245 = charIDToTypeID( "Lyr " );
		if ( typeof layerIndexOrName == "number" ) {
			ref44.putIndex( id245, layerIndexOrName );
		} else {
			ref44.putName( id245, layerIndexOrName );
		}
		desc46.putReference( id244, ref44 );
		var id246 = stringIDToTypeID( "selectionModifier" );
		var id247 = stringIDToTypeID( "selectionModifierType" );
		var id248 = stringIDToTypeID( "addToSelection" );
		desc46.putEnumerated( id246, id247, id248 );
		var id249 = charIDToTypeID( "MkVs" );
		desc46.putBoolean( id249, false );
		executeAction( id243, desc46, DialogModes.NO );
	}

 

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 20, 2023 Jul 20, 2023

Copy link to clipboard

Copied

DOM code is »problematic« and doesn’t use the identifier anway. 

And its indexing may, if I remember correctly, be messed up by Groups. (Edit: »messed up« in relation to the AM indices, the indices retrieved via DOM-code work for DOM-code itself fine.)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 20, 2023 Jul 20, 2023

Copy link to clipboard

Copied

Also what do you mean by »manipulate« exactly? 

Certain operations can be performed with AM-code by addressing a Layer via index or identifier, for some operations one has to select it first even with AM-code. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jul 20, 2023 Jul 20, 2023

Copy link to clipboard

Copied

Thanks for the reply! What I mean by manipulate, is to refer to, extract data from, or modify the layers directly based on the indices mentioned in AM or DOM references. I don't know how one would get information indirectly(without making a layer active) via AM, were via DOM, I had been attempting to get information via app.activeDocument.layers[id or indexItem]. Even selecting the active layer based on id or indexItem, seems to require AM, correct?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 20, 2023 Jul 20, 2023

Copy link to clipboard

Copied

You beem to be pretty vague on this. 

What data do you want to extract from the Layers? 

Which modifications do you want to perform on the Layers? 

 

Getting the seleted Layers’ bounds, blend mode, color, opacity, … can be done without selecting the Layers individually. 

An example for bounds: 

// 2023, use it at your own risk;
#target photoshop
if (app.documents.length > 0) {
    alert (collectSelectedLayersBounds ())
};
////// collect bounds of selected layers //////
function collectSelectedLayersBounds () {
// set to pixels;
    var originalRulerUnits = app.preferences.rulerUnits;
    app.preferences.rulerUnits = Units.PIXELS;
// get selected layers;
    var selectedLayers = new Array;
    var ref = new ActionReference();
    ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
    var desc = executeActionGet(ref);
    if (desc.getBoolean(stringIDToTypeID("hasBackgroundLayer")) == true) {var theAdd =0}
    else {var theAdd = 1};
    if( desc.hasKey( stringIDToTypeID( 'targetLayers' ) ) ){
    desc = desc.getList( stringIDToTypeID( 'targetLayers' ));
    var c = desc.count;
    var selectedLayers = new Array();
    // run through selected layers;
    for(var i=0;i<c;i++){
    var theIndex = desc.getReference( i ).getIndex()+theAdd;
    // get id for solid color layers;
    try {
    var ref = new ActionReference();
    ref.putIndex( charIDToTypeID("Lyr "), theIndex ); 
    var layerDesc = executeActionGet(ref);
    var theName = layerDesc.getString(stringIDToTypeID('name'));
    var theIdentifier = layerDesc.getInteger(stringIDToTypeID ("layerID"));
    var theBounds = layerDesc.getObjectValue(stringIDToTypeID("bounds"));
    var theseBounds = [theBounds.getUnitDoubleValue(stringIDToTypeID("left")), theBounds.getUnitDoubleValue(stringIDToTypeID("top")), theBounds.getUnitDoubleValue(stringIDToTypeID("right")), theBounds.getUnitDoubleValue(stringIDToTypeID("bottom"))];
    selectedLayers.push([theName, theIdentifier, theseBounds]);
    } catch (e) {};
    };
    // if only one:
    }else{
    var ref = new ActionReference();
    ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
    var layerDesc = executeActionGet(ref);
    try {
    var theName = layerDesc.getString(stringIDToTypeID('name'));
    var theIdentifier = layerDesc.getInteger(stringIDToTypeID ("layerID"));
    var theBounds = layerDesc.getObjectValue(stringIDToTypeID("bounds"));
    var theseBounds = [theBounds.getUnitDoubleValue(stringIDToTypeID("left")), theBounds.getUnitDoubleValue(stringIDToTypeID("top")), theBounds.getUnitDoubleValue(stringIDToTypeID("right")), theBounds.getUnitDoubleValue(stringIDToTypeID("bottom"))];
    selectedLayers = [[theName, theIdentifier, theseBounds]]
    } catch (e) {};
    };
// reset;
    app.preferences.rulerUnits = originalRulerUnits;
    return selectedLayers;
};

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jul 20, 2023 Jul 20, 2023

Copy link to clipboard

Copied

Sorry, not trying to be vague. I'm trying to understand if there's a simple way to reference layer 2 based on active layer 1. Originally I had hard coded 0 and 1, which limits placement within the document. The file I was working on, I wanted to get properties from itemIndex 57(selected) and 58, which seems as though you'd need AM to do that.

function psDims(theLayer) {
    /* Left, Top, Right and Bottom
    + - 1 - +
    |       |
    0       2
    |       |
    + - 3 - +
    */
    var bounds = theLayer.bounds;
    left = bounds[0];
    top = bounds[1];
    right = bounds[2];
    bottom = bounds[3];

    width = right - left;
    height = bottom - top;

    props = {
        lyr: theLayer,
        left: left,
        top: top,
        right: right,
        bottom: bottom,
        width: width,
        height: height
    };
    return props;
}
    aD = app.activeDocument;
    layer1 = psDims(aD.artLayers[0]);
    layer2 = psDims(aD.artLayers[1]);

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 20, 2023 Jul 20, 2023

Copy link to clipboard

Copied

If I understand correctly this would get the bounds-information; otherwise please post a description including meaningful screenshots including the pertinent Panels: 

 

edited; sorry, I had mixed up index and identifier

 

 

var theID = getSelectedLayerIndex();
var bounds1 = getLayerBoundsByIndex(theID);
var bounds2 = getLayerBoundsByIndex(theID+1);
alert ("\nbounds selected layer\n"+bounds1.join("\n")+"\n\nbounds higher layer\n"+bounds2.join("\n"))
// by mike hale;
// http://ps-scripts.com/bb/viewtopic.php?f=9&t=3217&p=14468&hilit=selectLayerByIndex&sid=95f9e770da5eb2014c336c0c67802704#p14468
function getSelectedLayerIndex() { 
try{
activeDocument.backgroundLayer;
var mod = 1;
}catch(e){
var mod = 0
}
var ref = new ActionReference(); 
ref.putProperty( charIDToTypeID("Prpr") , charIDToTypeID( "ItmI" )); 
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
return desc = executeActionGet(ref).getInteger(charIDToTypeID( "ItmI" ))-mod;
};
function getLayerBoundsByIndex( idx ) { 
var ref = new ActionReference(); 
ref.putProperty( charIDToTypeID("Prpr") , stringIDToTypeID( "bounds" )); 
ref.putIndex( charIDToTypeID( "Lyr " ), idx );
var desc = executeActionGet(ref).getObjectValue(stringIDToTypeID( "bounds" ));
var bounds = [];
bounds.push(desc.getUnitDoubleValue(stringIDToTypeID('left')));
bounds.push(desc.getUnitDoubleValue(stringIDToTypeID('top')));
bounds.push(desc.getUnitDoubleValue(stringIDToTypeID('right')));
bounds.push(desc.getUnitDoubleValue(stringIDToTypeID('bottom'))); 
return bounds;
};

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jul 20, 2023 Jul 20, 2023

Copy link to clipboard

Copied

Thanks for this. Is the below snippet to avoid errors because Photoshop doesn't index the background layers?

try{
activeDocument.backgroundLayer;
var mod = 1;
}catch(e){
var mod = 0
}

 The most helpful piece for me is.

function getLayerBoundsByIndex(idx) {
    var ref = new ActionReference();
    ref.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("bounds"));
    ref.putIndex(charIDToTypeID("Lyr "), idx);
    var desc = executeActionGet(ref).getObjectValue(stringIDToTypeID("bounds"));
    var bounds = [];
    bounds.push(desc.getUnitDoubleValue(stringIDToTypeID('left')));
    bounds.push(desc.getUnitDoubleValue(stringIDToTypeID('top')));
    bounds.push(desc.getUnitDoubleValue(stringIDToTypeID('right')));
    bounds.push(desc.getUnitDoubleValue(stringIDToTypeID('bottom')));
    return bounds;
};
//Added some alerts for testing
aL = app.activeDocument.activeLayer;
aLID = aL.itemIndex;
alert(getLayerBoundsByIndex(aLID));
alert(getLayerBoundsByIndex(aLID - 1));


With bounds as an example, I think what I was trying to do/ask, is if there is a way to read a layer from AM into a layer object that can be accessed through DOM. The only way I could find was to select a layer, and then access its attributes/properties via activeLayer. I don't really have a great reason for going this route other than it's easier for me to understand.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 20, 2023 Jul 20, 2023

Copy link to clipboard

Copied

LATEST

As far as I know you are correct and a Layer identified with AM-code can only be realiably addressed for DOM-code by selecting it. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
May 16, 2020 May 16, 2020

Copy link to clipboard

Copied

This works perfectly.  Many thanks.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 15, 2020 May 15, 2020

Copy link to clipboard

Copied

Hi,

This is another version of the script that you have shared above.

 

var doc = app.activeDocument;
var layers = doc.layers;

function getSelectedLayers() {
    var resultLayers = new Array();
    try {
        var idGrp = stringIDToTypeID("groupLayersEvent");
        var descGrp = new ActionDescriptor();
        var refGrp = new ActionReference();
        refGrp.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
        descGrp.putReference(charIDToTypeID("null"), refGrp);
        executeAction(idGrp, descGrp, DialogModes.NO);
        for (var i = 0; i < app.activeDocument.activeLayer.layers.length; i++) {
            resultLayers.push(app.activeDocument.activeLayer.layers[i])
        }
        var id8 = charIDToTypeID("slct");
        var desc5 = new ActionDescriptor();
        var id9 = charIDToTypeID("null");
        var ref2 = new ActionReference();
        var id10 = charIDToTypeID("HstS");
        var id11 = charIDToTypeID("Ordn");
        var id12 = charIDToTypeID("Prvs");
        ref2.putEnumerated(id10, id11, id12);
        desc5.putReference(id9, ref2);
        executeAction(id8, desc5, DialogModes.NO);
        return resultLayers;
    } catch (e) {
        alert("No Layer selected or locked layer selected");
        return resultLayers;
    }
}
var selectedLayers = getSelectedLayers();
$.writeln(selectedLayers.length);

 

This will give you an array of selected layers. I hope this helps

 

Thanks

Best regards

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Nov 07, 2020 Nov 07, 2020

Copy link to clipboard

Copied

This helped me a lot, thanks

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines