9 Replies Latest reply on Nov 15, 2017 12:20 PM by Davide_Barranca

    Script I made: Iterate Action Over Layers

    EnsilZah_ Level 1

      Hey, just cobbled together this script that I'm already finding useful in my own work, so I figured I'd share, and maybe see if anyone has suggestions for improvement.

      What it does, is apply a selected action to each of the selected layers individually.

      So for example, if you had 50 layers that you wanted to each rotate by 90 degrees in place, you'd record a rotate-by-90-degrees action, select all the layers you want to apply it to and run the script with the action selected.



      #target photoshop
      var scriptName = "NinjaScript IterateActionLayers";
      var scriptVersion = "0002";
      function cID (inVal) { return charIDToTypeID(inVal);}
      function sID (inVal) { return stringIDToTypeID(inVal);}
      var currentActionSets = getActionSets();
      function main()
      function optionsDialog()
          var ButtonWidth = 100;
        OpenOptionsDialog = new Window("dialog", scriptName + " v" + scriptVersion);
        OpenOptionsDialog.orientation = 'column';
        OpenOptionsDialog.alignChildren = 'left';
          mainGroup = OpenOptionsDialog.add("group");
          mainGroup.orientation = 'column';
          mainGroup.alignChildren = 'left';
          mainGroup.alignment = 'left';     
          var actionSetGroup = mainGroup.add("group");
          actionSetGroup.orientation = 'row';
          actionSetGroup.add("statictext",undefined, "ActionSet: ")
          var DDActionSet = actionSetGroup.add("dropdownlist",undefined, "")
          DDActionSet.preferredSize.width = 150;
          for (var i = 0; i < currentActionSets.length; i++)
                  DDActionSet.add("item", currentActionSets[i]);
          DDActionSet.selection = 0;
          var actionGroup = mainGroup.add("group");
          actionGroup.orientation = 'row';
          actionGroup.add("statictext",undefined, "Action:      ")
          DDActions = actionGroup.add("dropdownlist",undefined, "")
          DDActions.preferredSize.width = 150;
          function populateDDActions (inSet)
              for (var i = 0; i < currentActionSets[inSet].actions.length; i++)
                  DDActions.add("item", currentActionSets[inSet].actions[i]);
              DDActions.selection = 0;
          DDActionSet.onChange = function()
          mainGroup.add("statictext", undefined, "");
          ButtonGroup = mainGroup.add("group");
          ButtonGroup.orientation = 'row';
          ButtonGroup.alignChildren = 'center';
          ButtonGroup.alignment = 'top';     
          buttonRun= ButtonGroup.add("button",undefined, "Run")
          buttonRun.preferredSize.width = ButtonWidth;
          buttonRun.onClick = function()
             var SelectedLayers = getSelectedLayers();
              for (var i = 0; i < SelectedLayers.length; i++)
                  activeDocument.activeLayer = SelectedLayers[i];
                  doAction(DDActions.selection.text, DDActionSet.selection.text);
          buttonClose= ButtonGroup.add("button",undefined, "Exit")
          buttonClose.preferredSize.width = ButtonWidth;
          buttonClose.onClick = function() {OpenOptionsDialog.close()}       
          //Show window
        var result = OpenOptionsDialog.show();
      function getActionSets() 
        var i = 1;  
        var sets = [];  
        while (true) {  
          var ref = new ActionReference();  
          ref.putIndex(cID("ASet"), i);  
          var desc;  
          var lvl = $.level;  
          $.level = 0;  
          try {  
            desc = executeActionGet(ref);  
          } catch (e) {  
            break;    // all done  
          } finally {  
            $.level = lvl;  
          if (desc.hasKey(cID("Nm  "))) {  
            var set = {};  
            set.index = i;  
            set.name = desc.getString(cID("Nm  "));  
            set.toString = function() { return this.name; };  
            set.count = desc.getInteger(cID("NmbC"));  
            set.actions = [];  
            for (var j = 1; j <= set.count; j++) {  
              var ref = new ActionReference();  
              ref.putIndex(cID('Actn'), j);  
              ref.putIndex(cID('ASet'), set.index);  
              var adesc = executeActionGet(ref);  
              var actName = adesc.getString(cID('Nm  '));  
        return sets;  
      function getSelectedLayers()
        var resultLayers=new Array();
          var descGrp = new ActionDescriptor();
          var refGrp = new ActionReference();
          refGrp.putEnumerated(cID( "Lyr " ),cID( "Ordn" ),cID( "Trgt" ));
          descGrp.putReference(cID( "null" ), refGrp );
          executeAction( sID( "groupLayersEvent" ), descGrp, DialogModes.NO );
          for (var ix=0;ix<app.activeDocument.activeLayer.layers.length;ix++){resultLayers.push(app.activeDocument.activeLayer.layers[ix])}
          var desc5 = new ActionDescriptor();
          var ref2 = new ActionReference();
          ref2.putEnumerated( cID( "HstS" ), cID( "Ordn" ), cID( "Prvs" ) );
          desc5.putReference( cID( "null" ), ref2 );
          executeAction( cID( "slct" ), desc5, DialogModes.NO );
        } catch (err) { }
        return resultLayers;
        • 1. Re: Script I made: Iterate Action Over Layers
          Level 1

          I like your sharing

          but I prefer that after starting the action the panel remains open

          so I can apply other effects.

          • 2. Re: Script I made: Iterate Action Over Layers
            Jacksoneon Level 1

            Hi EnsilZah_,


            Thanks a lot for posting this script. I am looking to loop an action and I used the code you provided. It almost worked for what I need, but the action did not analyze each layer individually, rather took the first reading (Color Range) from the first layer and applied it to all of the following layers below it. If I "play" the action on each layer individually it works fine. I already have "select next sequential layer" at the end of the action, so I just need my action to loop so that each layer is individually analyzed by the action. Do you know how I can repeat / loop an action?





            • 3. Re: Script I made: Iterate Action Over Layers
              JJMack Most Valuable Participant

              I Recorded an action to load the current layer transparence as a selection then fill that selection with a random color. I was hoping that the script would make each layer the active layer before playing the action.  I ran the script and only the background layer which was the current layer was filled.  I took a quick look at the script and saw the script would only play the action on  selected Layers. So I select all the layers and each layer retain its transparency and the non transparent pixels were filled.  So each layer selection worked.


              It may be your action at fault.  You wrote that you used Color range and the first layers color range  selection  was used on all layers. The would happen If your action did a color range and saved that selection did other stuff than loaded the saved selection and used it.


              After using the save selection the action needs to delete the saved selection alpha channel.  For the next time the Action is played It would save the new color range selection and save it This would not replace the first save it would be an additional  alpha channel with the same name or different name.   The Action is recorded to either load the first alpha channel or the alpha channel with the name you used. Which would load the first one with that name.  If you had more than 53 selected layers selected the Action would  also fail with an error Photoshop only  supports up to 53 alpha channels.



              The script also has problems.  If a select the background layer and layers in a layer group for example the script does not work.

              • 4. Re: Script I made: Iterate Action Over Layers
                Jacksoneon Level 1

                Thanks JJMack,


                I tried to recreate the action as you mentioned, and make sure to deselect the color range selection at the end of the action. The issue continued to persist, but I noticed that as the script was running, it kept all of the layers visible as it went down the layers (including the top layer, which it was sampling). I added a "hide layer" command at the end of the action, so now the "Color Range" can see the current layer to sample it. It then works.


                Thanks so much for your help!



                • 5. Re: Script I made: Iterate Action Over Layers
                  JJMack Most Valuable Participant

                  If your action save the selection and later  use the save selection you need to DELETE the saved selection after loading and using it, Not just deselect.


                  I do not create script that let uses add there action to the process.  Action may do things that change the document in some way that will cause the script to fail.  For example delete the current layer add some number of layers changes the to document like that may mess up what the script is doing.


                  If you have selected five layer and a script like this one note the layers to be processed like create as list  layer Name and the layer index number. Then proceeds to make a layer in the list  the active layer a plays the user action.   If the action changes the layer stack will the scripts layer list still be valid? Layer name may mot be unique.


                  There are a couple of well known scripts the allow user to add Action into the Script process. The Image Processor scripts the let users add actions.  However, the action is player after the Image Processors processed a dupe of the current Image being process for outputting a image file.  The document is ready to be  saveas a a file.  All the script does after the playing and the action completes is save the active document  and close it or resize,  save and  close the active document. The image processor then move on the processing the next output file by duping the current source image or then next soource imat or the processing is done.


                  I may write a script like this one for my own use. However  I would not make it public for actions could easily mess with the layer stack.

                  • 6. Re: Script I made: Iterate Action Over Layers
                    JJMack Most Valuable Participant

                    In the other thread you wrote you wanted to play you action on all layers,  The Script posted here is not designed to do that.  It is hard for me to believe an action could be recorderdto work on all layers on any layered  document.  There are too many types of layers action can not use logic to find out the kind of layer the active layer is.  An action could be written for a specific document the action is recorded for..


                    It would be very easy for an action to mess Up a simple script that was playing an action on each layer by looping through the layer changing the active layer and playing.


                    A script like that is very simple to write pulling off the action may not be simple. I'll post the script.  You will need the change the first two lines to set the ActionName and the ActionSet name to your action set and action.  I will not make it a dialog.  IMO the script is not generally useful.  Us at you own risk....

                    var ActionName = "RandonFillColor";
                    var ActionSet = "temp";
                    if (!documents.length) { alert('There are no documents open.', 'No Document');}
                    else {processArtLayers(activeDocument);}
                    function processArtLayers(obj) {
                        for( var i = obj.artLayers.length-1; 0 <= i; i--) { activeDocument.activeLayer = obj.artLayers[i]; doAction(ActionName, ActionSet);}
                        for( var i = obj.layerSets.length-1; 0 <= i; i--) {processArtLayers(obj.layerSets[i]); } // Process Layer Set Layers
                    • 7. Re: Script I made: Iterate Action Over Layers
                      Jacksoneon Level 1

                      Hi JJMack,


                      Thank you for your help and expertise. I see what you're saying that you can't simply loop an action, because faults may come into play.


                      Fortunately, I was able to resolve the "Color Range" issue, by adding a "Hide Current Layer" at the end of the action. Then when the action samples each layer, it hides the top layer revealing the lower one, so that the correct sample is taken (instead of just the top layer).


                      I will see if I can use your script as another way.

                      • 8. Re: Script I made: Iterate Action Over Layers



                        My apologies for bumping this back up. But does anybody know how I'd edit this script to just run a pre-chosen action, rather than giving the dialog box to choose? Or would I be better off writing something from scratch? Basically, I don't know how to make a for loop run only on selected layers.


                        I want to integrate this into a much broader set of scripts/actions that runs start to finish without the need for any human input, but my scripting knowledge is still pretty rudimentary. Specifically, I want to delete the layer masks of all selected layers, and replace them with a layer mask of the current marquee selection. So maybe it doesn't even need to run an action, but rather the 'replace mask' instruction is just built into the script.


                        If anyone can help, I'd appreciate this so much!

                        • 9. Re: Script I made: Iterate Action Over Layers
                          Davide_Barranca Level 1

                          @tssee: just comment out line 97 to prevent the panel from closing on Run button click.