Skip to content

Layers

PHOTOSHOP HELPER FUNCTIONS

NO_DIALOG = DialogModes.DisplayNoDialogs module-attribute

FINDING LAYERS

create_new_layer(layer_name=None)

Creates a new layer below the currently active layer. The layer will be visible. @param layer_name: Optional name for the new layer @return: Newly created layer object

Source code in src/helpers/layers.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
def create_new_layer(layer_name: Optional[str] = None) -> ArtLayer:
    """
    Creates a new layer below the currently active layer. The layer will be visible.
    @param layer_name: Optional name for the new layer
    @return: Newly created layer object
    """
    # Create new layer at top of layers
    active_layer = app.activeDocument.activeLayer
    layer = app.activeDocument.artLayers.add()
    layer.name = layer_name or "Layer"

    # Name it & set blend mode to normal
    layer.blendMode = BlendMode.NormalBlend

    # Move the layer below
    layer.moveAfter(active_layer)
    return layer

duplicate_group(name)

Duplicates current active layer set without renaming contents. @param name: Name to give the newly created layer set. @return: The newly created layer set object.

Source code in src/helpers/layers.py
193
194
195
196
197
198
199
200
201
202
203
204
205
206
def duplicate_group(name: str):
    """
    Duplicates current active layer set without renaming contents.
    @param name: Name to give the newly created layer set.
    @return: The newly created layer set object.
    """
    desc241 = ActionDescriptor()
    ref4 = ActionReference()
    ref4.putEnumerated(sID("layer"), sID("ordinal"), sID("targetEnum"))
    desc241.putReference(sID("target"),  ref4)
    desc241.putString(sID("name"), name)
    desc241.putInteger(sID("version"),  5)
    app.ExecuteAction(sID("duplicate"), desc241, DialogModes.DisplayNoDialogs)
    return app.activeDocument.activeLayer

edit_smart_layer(layer)

Opens the contents of a given smart layer (as a separate document) for editing. @param layer: Smart layer to open for editing.

Source code in src/helpers/layers.py
237
238
239
240
241
242
243
244
245
def edit_smart_layer(layer: ArtLayer):
    """
    Opens the contents of a given smart layer (as a separate document) for editing.
    @param layer: Smart layer to open for editing.
    """
    desc1 = ActionDescriptor()
    desc1.putInteger(sID("documentID"), app.activeDocument.id)
    desc1.putInteger(sID("layerID"), layer.id)
    app.executeAction(sID("placedLayerEditContents"), desc1, NO_DIALOG)

getLayer(name, group=None)

Retrieve ArtLayer object from given name and group/group tree. @param name: Name of the layer @param group: Group name/object, or ordered list of group names/objects @return: Layer object requested

Source code in src/helpers/layers.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def getLayer(name: str, group: Optional[Union[str, list, tuple, LayerSet]] = None) -> Optional[ArtLayer]:
    """
    Retrieve ArtLayer object from given name and group/group tree.
    @param name: Name of the layer
    @param group: Group name/object, or ordered list of group names/objects
    @return: Layer object requested
    """
    try:
        # LayerSet provided?
        if not group:
            # LayerSet not provided
            return app.activeDocument.artLayers[name]
        elif isinstance(group, str):
            # LayerSet name given
            return app.activeDocument.layerSets[group].artLayers[name]
        elif isinstance(group, LayerContainer):
            # LayerSet object given
            return group.artLayers[name]
        elif isinstance(group, (tuple, list)):
            # Tuple or list of LayerSets
            layer_set = app.activeDocument
            for g in group:
                if isinstance(g, str):
                    # LayerSet name given
                    layer_set = layer_set.layerSets[g]
                elif isinstance(g, LayerContainer):
                    # LayerSet object given
                    layer_set = g
            return layer_set.artLayers[name]
        # ArtLayer can't be located
        raise OSError(f"ArtLayer invalid")
    except PS_EXCEPTIONS:
        # Layer couldn't be found
        print(f'Layer "{name}" could not be found!')
        if group and isinstance(group, LayerSet):
            print(f"LayerSet reference used: {group.name}")
        elif group and isinstance(group, str):
            print(f"LayerSet reference used: {group}")
    return

getLayerSet(name, group=None)

Retrieve layer group object. @param name: Name of the group @param group: Parent group name or object. @return: Group object requested.

Source code in src/helpers/layers.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
def getLayerSet(name: str, group: Optional[Union[str, list, tuple, LayerSet]] = None) -> Optional[LayerSet]:
    """
    Retrieve layer group object.
    @param name: Name of the group
    @param group: Parent group name or object.
    @return: Group object requested.
    """
    try:
        # Was LayerSet provided?
        if not group:
            # No LayerSet given
            return app.activeDocument.layerSets[name]
        elif isinstance(group, str):
            # LayerSet name given
            return app.activeDocument.layerSets[group].layerSets[name]
        elif isinstance(group, (tuple, list)):
            # Tuple or list of groups
            layer_set = app.activeDocument
            for g in group:
                if isinstance(g, str):
                    # LayerSet name given
                    layer_set = layer_set.layerSets[g]
                elif isinstance(g, LayerContainer):
                    # LayerSet object given
                    layer_set = g
            return layer_set.layerSets[name]
        elif isinstance(group, LayerContainer):
            # LayerSet object given
            return group.layerSets[name]
        # LayerSet can't be located
        raise OSError(f"LayerSet invalid")
    except PS_EXCEPTIONS:
        print(f'LayerSet "{name}" could not be found!')
        if group and isinstance(group, LayerSet):
            print(f"LayerSet reference used: {group.name}")
        elif group and isinstance(group, str):
            print(f"LayerSet reference used: {group}")
    return

group_layers(name='New Group', layers=None)

Groups the selected layers. @param name: Name of the new group. @param layers: Layers to group, will use active if not provided. @return: The newly created group.

Source code in src/helpers/layers.py
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
def group_layers(
    name: Optional[str] = "New Group",
    layers: Optional[list[Union[ArtLayer, LayerSet]]] = None,
) -> LayerSet:
    """
    Groups the selected layers.
    @param name: Name of the new group.
    @param layers: Layers to group, will use active if not provided.
    @return: The newly created group.
    """
    # Select layers if given
    if layers:
        select_layers(layers)

    # Group the layers
    desc1 = ActionDescriptor()
    ref1 = ActionReference()
    ref2 = ActionReference()
    ref1.putClass(sID("layerSection"))
    desc1.putReference(sID('null'), ref1)
    ref2.putEnumerated(cID('Lyr '), cID('Ordn'), cID('Trgt'))
    desc1.putReference(cID('From'), ref2)
    desc2 = ActionDescriptor()
    desc2.putString(cID('Nm  '), name)
    desc1.putObject(cID('Usng'), sID("layerSection"), desc2)
    desc1.putInteger(sID("layerSectionStart"), 0)
    desc1.putInteger(sID("layerSectionEnd"), 1)
    desc1.putString(cID('Nm  '), name)
    app.executeAction(cID('Mk  '), desc1, DialogModes.DisplayNoDialogs)
    return app.activeDocument.activeLayer

lock_layer(layer, protection='protectAll')

Locks the given layer. @param layer: The layer to lock. @param protection: protectAll to lock, protectNone to unlock

Source code in src/helpers/layers.py
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
def lock_layer(layer: Union[ArtLayer, LayerSet], protection: str = "protectAll") -> None:
    """
    Locks the given layer.
    @param layer: The layer to lock.
    @param protection: protectAll to lock, protectNone to unlock
    """
    desc819 = ActionDescriptor()
    ref378 = ActionReference()
    ref378.putIdentifier(cID("Lyr "), layer.id)
    desc819.putReference(sID("target"), ref378)
    desc820 = ActionDescriptor()
    desc820.putBoolean(sID(protection), True)
    idlayerLocking = sID("layerLocking")
    desc819.putObject(idlayerLocking, idlayerLocking, desc820)
    app.executeAction(sID("applyLocking"), desc819, NO_DIALOG)

merge_group(group=None)

Merges a layer set into a single layer. @param group: Layer set to merge. Merges active if not provided.

Source code in src/helpers/layers.py
209
210
211
212
213
214
215
216
def merge_group(group: Optional[LayerSet] = None):
    """
    Merges a layer set into a single layer.
    @param group: Layer set to merge. Merges active if not provided.
    """
    if group:
        app.activeDocument.activeLayer = group
    app.executeaction(sID("mergeLayersNew"), None, NO_DIALOG)

merge_layers(layers=None, name=None)

Merge a set of layers together. @param layers: Layers to be merged, uses active if not provided. @param name: Name of the newly created layer. @return: Returns the merged layer.

Source code in src/helpers/layers.py
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
def merge_layers(layers: list[ArtLayer] = None, name: Optional[str] = None) -> ArtLayer:
    """
    Merge a set of layers together.
    @param layers: Layers to be merged, uses active if not provided.
    @param name: Name of the newly created layer.
    @return: Returns the merged layer.
    """
    # Select none, then select entire list
    if layers:
        select_layers(layers)

    # Return layer if only one is present in the list
    if len(layers) == 1:
        return layers[0]

    # Merge layers and return result
    app.ExecuteAction(sID("mergeLayersNew"), ActionDescriptor(), NO_DIALOG)
    if name:
        app.activeDocument.activeLayer.name = name
    return app.activeDocument.activeLayer

select_bounds(bounds)

Create a selection using a list of bound values. @param bounds: List of bound values (left, top, right, bottom).

Source code in src/helpers/layers.py
290
291
292
293
294
295
296
297
298
299
300
301
def select_bounds(bounds: list[Union[int, float]]) -> None:
    """
    Create a selection using a list of bound values.
    @param bounds: List of bound values (left, top, right, bottom).
    """
    left, top, right, bottom = bounds
    app.activeDocument.selection.select([
        [left, top],
        [right, top],
        [right, bottom],
        [left, bottom]
    ])

select_layer(layer, add=False, make_visible=False)

Select a layer (make active) and optionally force it to be visible. @param layer: Layer to select. @param add: Add to existing selection. @param make_visible: Make the layer visible if not currently visible? Doesn't work with adding layers to selection.

Source code in src/helpers/layers.py
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
def select_layer(
    layer: Union[ArtLayer, LayerSet],
    add: bool = False,
    make_visible: bool = False
) -> None:
    """
    Select a layer (make active) and optionally force it to be visible.
    @param layer: Layer to select.
    @param add: Add to existing selection.
    @param make_visible: Make the layer visible if not currently visible?
                         Doesn't work with adding layers to selection.
    """
    desc1 = ActionDescriptor()
    ref1 = ActionReference()
    ref1.putIdentifier(sID("layer"), layer.id)
    desc1.putReference(sID("target"), ref1)
    # Add to currently selected layers?
    if add:
        desc1.putEnumerated(
            sID('selectionModifier'),
            sID('selectionModifierType'),
            sID('addToSelection')
        )
    # Force visible?
    desc1.putBoolean(sID("makeVisible"), make_visible)
    app.executeAction(sID('select'), desc1, DialogModes.DisplayNoDialogs)

select_layer_bounds(layer=None)

Select the bounding box of a given layer. @param layer: Layer to select the pixels of. Uses active layer if not provided.

Source code in src/helpers/layers.py
354
355
356
357
358
359
360
361
def select_layer_bounds(layer: ArtLayer = None):
    """
    Select the bounding box of a given layer.
    @param layer: Layer to select the pixels of. Uses active layer if not provided.
    """
    if not layer:
        layer = app.activeDocument.activeLayer
    select_bounds(layer.bounds)

select_layer_pixels(layer=None)

Select pixels of the active layer, or a target layer. @param layer: Layer to select. Uses active layer if not provided.

Source code in src/helpers/layers.py
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
def select_layer_pixels(layer: Optional[ArtLayer] = None) -> None:
    """
    Select pixels of the active layer, or a target layer.
    @param layer: Layer to select. Uses active layer if not provided.
    """
    if layer and layer.kind == LayerKind.SolidFillLayer:
        return select_vector_layer_pixels(layer)
    des1 = ActionDescriptor()
    ref1 = ActionReference()
    ref2 = ActionReference()
    ref1.putProperty(sID("channel"), sID("selection"))
    des1.putReference(sID("target"), ref1)
    ref2.putEnumerated(sID("channel"), sID("channel"), sID("transparencyEnum"))
    if layer:
        ref2.putIdentifier(sID("layer"), layer.id)
    des1.putReference(sID("to"), ref2)
    app.executeAction(sID("set"), des1, NO_DIALOG)

select_layers(layers)

Makes a list of layers active (selected) in the layer panel. @param layers: List of layers or layer sets.

Source code in src/helpers/layers.py
332
333
334
335
336
337
338
339
340
def select_layers(layers: list[ArtLayer, LayerSet]):
    """
    Makes a list of layers active (selected) in the layer panel.
    @param layers: List of layers or layer sets.
    """
    # Select none, then add all layers to selection
    select_no_layers()
    for lay in layers:
        select_layer(lay, add=True)

select_no_layers()

Deselect all layers.

Source code in src/helpers/layers.py
343
344
345
346
347
348
349
350
351
def select_no_layers() -> None:
    """
    Deselect all layers.
    """
    selectNone = ActionDescriptor()
    ref = ActionReference()
    ref.putEnumerated(sID("layer"), sID("ordinal"), sID("targetEnum"))
    selectNone.putReference(sID("target"), ref)
    app.executeAction(sID("selectNoLayers"), selectNone, NO_DIALOG)

select_vector_layer_pixels(layer=None)

Select pixels of the active vector layer, or a target layer. @param layer: Layer to select. Uses active layer if not provided.

Source code in src/helpers/layers.py
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
def select_vector_layer_pixels(layer: Optional[ArtLayer] = None) -> None:
    """
    Select pixels of the active vector layer, or a target layer.
    @param layer: Layer to select. Uses active layer if not provided.
    """
    desc1 = ActionDescriptor()
    ref1 = ActionReference()
    ref2 = ActionReference()
    ref1.putProperty(sID("channel"), sID("selection"))
    desc1.putReference(sID("target"), ref1)
    ref2.putEnumerated(sID("path"), sID("path"), sID("vectorMask"))
    if layer:
        ref2.putIdentifier(sID("layer"), layer.id)
    desc1.putReference(sID("to"), ref2)
    desc1.putInteger(sID("version"), 1)
    desc1.putBoolean(sID("vectorMaskParams"), True)
    app.executeaction(sID("set"), desc1, NO_DIALOG)

smart_layer(layer=None)

Makes the active layer or layer set a smart layer. Optionally make a given layer active first. @param layer: [Optional] Layer to make active. @return: Newly created smart layer.

Source code in src/helpers/layers.py
224
225
226
227
228
229
230
231
232
233
234
def smart_layer(layer: Union[ArtLayer, LayerSet] = None) -> ArtLayer:
    """
    Makes the active layer or layer set a smart layer.
    Optionally make a given layer active first.
    @param layer: [Optional] Layer to make active.
    @return: Newly created smart layer.
    """
    if layer:
        app.activeDocument.activeLayer = layer
    app.ExecuteAction(sID("newPlacedLayer"), None, DialogModes.DisplayNoDialogs)
    return app.activeDocument.activeLayer

unlock_layer(layer)

Unlocks the given layer. @param layer: The layer to unlock.

Source code in src/helpers/layers.py
277
278
279
280
281
282
def unlock_layer(layer: Union[ArtLayer, LayerSet]) -> None:
    """
    Unlocks the given layer.
    @param layer: The layer to unlock.
    """
    lock_layer(layer, "protectNone")

unpack_smart_layer(layer=None)

Converts a smart layer back into its separate components.

Source code in src/helpers/layers.py
248
249
250
251
252
def unpack_smart_layer(layer: Optional[ArtLayer] = None):
    """Converts a smart layer back into its separate components."""
    if layer:
        app.activeDocument.activeLayer = layer
    app.ExecuteAction(sID("placedLayerConvertToLayers"), None, NO_DIALOG)