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

Get all groups and layers with "ActionReference()"

Community Beginner ,
Feb 14, 2015 Feb 14, 2015

Copy link to clipboard

Copied

Hi all!

I know to get all layers and groups with a recursive function, but it's slow. It's possible get the same with the ActionRefencen() code? (I think it will be faster).

Thnx!

TOPICS
Actions and scripting

Views

1.2K

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
Adobe
Enthusiast ,
Feb 15, 2015 Feb 15, 2015

Copy link to clipboard

Copied

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 ,
Feb 15, 2015 Feb 15, 2015

Copy link to clipboard

Copied

// thanks to mike hale and paul riggott;

// 2015, use it at your own risk;

#target "photoshop-70.032"

if (app.documents.length > 0) {

app.activeDocument.suspendHistory("replace", "main ()");

};

////////////////////////////////////

function main () {

// the file;

var myDocument = app.activeDocument;

// get number of layers;

var ref = new ActionReference();

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

var applicationDesc = executeActionGet(ref);

var theNumber = applicationDesc.getInteger(stringIDToTypeID("numberOfLayers"));

// process the layers;

var theLayers = new Array;

for (var m = 0; m <= theNumber; m++) {

try {

var ref = new ActionReference();

ref.putIndex( charIDToTypeID( "Lyr " ), m);

var layerDesc = executeActionGet(ref);

var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));

var isBackground = layerDesc.getBoolean(stringIDToTypeID("background"));

// if not layer group collect values;

//if (layerSet != "layerSectionEnd" && layerSet != "layerSectionStart" && isBackground != true) {

var theName = layerDesc.getString(stringIDToTypeID('name'));

var theID = layerDesc.getInteger(stringIDToTypeID('layerID'));

theLayers.push([theName, theID])

//};

}

catch (e) {};

};

alert (theLayers.join("\n"));

// based on code by mike hale, via paul riggott;

function selectLayerByID(id,add){

add = undefined ? add = false:add

var ref = new ActionReference();

    ref.putIdentifier(charIDToTypeID("Lyr "), id);

    var desc = new ActionDescriptor();

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

       if(add) desc.putEnumerated( stringIDToTypeID( "selectionModifier" ), stringIDToTypeID( "selectionModifierType" ), stringIDToTypeID( "addToSelection" ) );

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

   try{

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

}catch(e){

alert(e.message);

}

};

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 Beginner ,
Feb 17, 2015 Feb 17, 2015

Copy link to clipboard

Copied

Thanks you very much! it's really really fast!

Anyway I want the layer's objects instead layer's name or his id, I suppose I can set activeLayer with that ID and 'push' the activeLayer into an array, but I think that will slow down the process.

with:

theLayers.push(setActiveLayerByID(theID));

instead of:

theLayers.push([theName, theID]);

unless exists a way with action manager

var theObject = layerDesc.getObject(bla bla bla...)

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 ,
Feb 17, 2015 Feb 17, 2015

Copy link to clipboard

Copied

There is a function called "selectLayerByID" in the code with which you could select Layers as needed by their indices, but as you already concluded applying that for all Layers would probably be a considerable drag performance-wise.

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
Advisor ,
Feb 17, 2015 Feb 17, 2015

Copy link to clipboard

Copied

What he said. The performance penalty in recursing to collect the layer objects instead of IDs is the creation of the layer objects, not in the recursion itself.

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 Beginner ,
Feb 18, 2015 Feb 18, 2015

Copy link to clipboard

Copied

If the penalty is the object creation, I prefer the typical recursive function (without AM) in favour of clarity

What do you think?

Thanks so much!

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
Advisor ,
Feb 18, 2015 Feb 18, 2015

Copy link to clipboard

Copied

What do you think?

Unless I'm dealing with 50+ layers or actually need the objects, the recursive Layer object method is what I use.

Below is how I do it in xtools/xlib/stdlib.js

//
// Traverse the all layers, including nested layers, executing
// the specified function. Traversal can happen in both directions.
//
Stdlib.traverseLayers = function(doc, ftn, reverse, layerSets) {

  function _traverse(doc, layers, ftn, reverse, layerSets) {
    var ok = true;
    for (var i = 1; i <= layers.length && ok != false; i++) {
      var index = (reverse == true) ? layers.length-i : i - 1;
      var layer = layers[index];

      if (layer.typename == "LayerSet") {
        if (layerSets) {
          ok = ftn(doc, layer);
        }
        if (ok) {
          ok = _traverse(doc, layer.layers, ftn, reverse, layerSets);
        }
      } else {
        ok = ftn(doc, layer);
        try {
          if (app.activeDocument != doc) {
            app.activeDocument = doc;
          }
        } catch (e) {
        }
      }
    }
    return ok;
  };

  return _traverse(doc, doc.layers, ftn, reverse, layerSets);
};

Stdlib.getLayersList = function(doc, reverse, layerSets) {
  function _ftn(doc, layer) {
    _ftn.list.push(layer);
    return true;
  };

  _ftn.list = [];
  Stdlib.traverseLayers(doc, _ftn, reverse, layerSets);

  var lst = _ftn.list;
  _ftn.list = undefined;
  return lst;
};

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
Feb 24, 2015 Feb 24, 2015

Copy link to clipboard

Copied

"but it's slow"

I've always wanted to pursue this based on facts, I have some timing tests, but I can tell you a few things.

#1: The Photoshop DOM (docLayers.length) does not cache or track state. That is as designed. When you ask a Photoshop document for the layer count, it actually goes and checks the layers count, this is even worse when you are dealing with layer groups. You want this var a = activeDocument.layers.length; for (var i = 0; i < a; i ++) { ... } and NOT this for (var i = 0; i < activeDocument.layers.length; i ++) { ... }

#2: If you go with the Action* routines and do not use the DOM to solve the above problem DO NOT ASK FOR ALL KEYS. In the below code, if I_like_to_go_fast is true then we ask only for the "tool" information from the application. You can see results by timing this once with true and once with false. You MUST use the true case first or you must restart Photoshop to get proper timing and it depends on what property you are asking for. But always ask for one property at a time to avoid initializing code you don't need. Pulling in all the text information on a layer is the best example. In older versions we had a huge hit when you started up the video libraries we needed.

        var ref = new ActionReference();

if (I_like_to_go_fast) {

        var keyTool = stringIDToTypeID( "tool" );

        var classProperty = charIDToTypeID( "Prpr" );

        ref.putProperty( classProperty, keyTool );

}

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

I use this Object for timing. var t = new Timer(); ... myLogFunction("Some interesting timing: " + t.getElapsed()); Remember you are altering the timing by monitoring the timing! But bottom line is you need to measure it before you can improve it.

// Library for timing things in JavaScript

function Timer() {

  // member properties

  this.startTime = new Date();

  this.endTime = new Date();

  // member methods

  // reset the start time to now

  this.start = function () { this.startTime = new Date(); }

  // reset the end time to now

  this.stop = function () { this.endTime = new Date(); }

  // get the difference in seconds between start and stop

  this.getTime = function () { return (this.endTime.getTime() - this.startTime.getTime()) / 1000; }

  // get the current elapsed time from start to now, this sets the endTime, in seconds

  this.getElapsed = function () { this.stop(); return this.getTime(); }

}

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
Advisor ,
Feb 24, 2015 Feb 24, 2015

Copy link to clipboard

Copied

The Photoshop DOM (docLayers.length) does not cache or track state

Thanks for this, Tom. I'll get my code modified to reflect this. I've learned my one knew fact for the day.

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 ,
Feb 27, 2015 Feb 27, 2015

Copy link to clipboard

Copied

But always ask for one property at a time to avoid initializing code you don't need.

Is it even possible to ask for more than one property that way?

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
Advocate ,
Apr 10, 2015 Apr 10, 2015

Copy link to clipboard

Copied

LATEST

I haven't been able to get more than one property myself (say the "tool" and the "serialString"), or nested properties (e.g. a descriptor within a descriptor); any trick to perform the magic or it's just not possible?

Davide

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 Beginner ,
Feb 26, 2015 Feb 26, 2015

Copy link to clipboard

Copied

I hope someday we'll have a full (and fast) API, without using AM.

Meanwhile.. any web, docs, vídeos etc. to learn the basisc of AM?

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