Photoshop Comtypes 2020 edition

Hello! I got asked a question about smart layer manipulation and I didn't know the answer off of the top of my head, so I dug out this ye ole snippet of mine from 2012 and updated it to 2020.

Some small differences are that I had to add a couple of flags to the comtypes object creation (which may have just been a quirk of my machine, see the stackoverflow link for details) and I've added some details about manipulating smart layers at the bottom of the snippet.

Happy coding!

##############################################################################
#
# Eight years later, here is my 2020 version of how to use Comtypes to drive photoshop
#
# Here is a quick code sample showing how to manage different layers and groups  
# in a photoshop document using Python. 
#
# Pete Hanshaw, 2020
# http://peterhanshawart.blogspot.com.au/
#
##############################################################################
#
# How to make a layerSet (aka, 'group') artLayer (aka 'layer'), how to make them
# active and how to move them. 
#
# These examples use the comtypes module. Grab it here:
# http://sourceforge.net/projects/comtypes/
#
##############################################################################


#Create the application reference
import comtypes.client as ct

# https://stackoverflow.com/questions/42794530/error-pointeriunknown-when-trying-to-access-com-object-properties/42823644
psApp = ct.CreateObject('Photoshop.Application', dynamic=True)
psApp.Visible = True

#Create a new document to play with
doc = psApp.Documents.Add(256, 256, 72, 'test_bed', 2, 1, 1)

#When scripting 'groups' are called 'layerSets'. 
new_layerSet = doc.LayerSets.Add()

#Once you create a layerSet object reference, you can access it's
#'name' attribute. The same goes for other objects you can normally
#name within Photoshop.
new_layerSet.name = "I'm a layerSet"

#regular, paintable layers are called 'ArtLayers'
new_art_layer = doc.ArtLayers.Add()
new_art_layer.name = "I'm an ArtLayer"

#To add a nested art layer into a LayerSet, use our layerSet object as a reference
nested_art_layer = new_layerSet.ArtLayers.Add()
nested_art_layer.name = "I'm a nested ArtLayer"

#The same goes for adding a nested LayerSet!
nested_layerSet = new_layerSet.LayerSets.Add()
nested_layerSet.name = "I'm a nested LayerSet"

#and so on!
deep_art = nested_layerSet.ArtLayers.Add()
deep_art.name = "Deep man, deep."

#Every time a new object is made, it will become the active layer. 
#To make other layers active, you can refer to them either by their name, or 
#their index location. 

#For example:

#Making an art layer active using the layer's name:
doc.activeLayer = (doc.artLayers["I'm an ArtLayer"])

#Making an art layer active using the layer's index location:
doc.activeLayer = (doc.artLayers[-1]) #This will select the background!

#Selecting a nested art layer is a little more difficult, as you have to
#'drill down' through the hierachy in order to select it. 
doc.activeLayer = (doc.layerSets["I'm a layerSet"].
    layerSets["I'm a nested LayerSet"].
    artLayers["Deep man, deep."])

#Moving a layer in the hierachy is done using the move command.
#The arguments specify which hierachy to move in, and where to put it. 

#For example, this will move the first layerSet we made just above the background
#layer.

#Make a new layer set
mobile_layerSet = doc.LayerSets.Add()
mobile_layerSet.name = "move me"

#Move the 'mobile' layerSet to just after the 'background' layer
mobile_layerSet.Move(doc, 2)

# Smart Layer Manipulation Examples adapted from:
# https://www.photopea.com/tuts/edit-smart-objects-with-a-script/
# Create a smart object layer.

# CREATING A SMART LAYER
# select a layer that you want to work with
smart_layer = doc.ArtLayers.Add()
smart_layer.name = "Smarty pants Layer."

doc.activeLayer = smart_layer

# Convert the active layer into a smart object
psApp.executeAction(psApp.stringIDToTypeID("newPlacedLayer"));

# EDITING A SMART LAYER
# Now we can edit the smart object
psApp.executeAction(psApp.stringIDToTypeID("placedLayerEditContents"))

# now, the Smart Object is an active document, we can work with it. Rename the layer...
smartDoc = psApp.activeDocument
super_smart_layer = smartDoc.ArtLayers[0]
super_smart_layer.name = "Amazingly smart layer."

# save the smart object and close it
psApp.activeDocument.save()
psApp.activeDocument.close()

# We are now back in the root object

# QUERYING A SMART LAYER
# If we want to check if a layer is smart, we can query it...
doc = psApp.activeDocument

for layer in doc.ArtLayers:
    # 17 is a psSmartObjectLayer - see the Adobe Scripting API 'PsLayerKind' to see what each value means.
    if layer.Kind == 17:
        print "We found a smart layer named {}".format(layer.name)

2 comments:

  1. Yes! Thank you.
    Quick quesiton - What if we're not creating the layers, but accessing existing smart layers? Is it as simple as setting the:
    psApp.Application.ActiveDocument.activeLayer = layer/.name?
    and then:
    psApp.executeAction(self.psApp.stringIDToTypeID("placedLayerEditContents"))

    Cheers mate!

    ReplyDelete
    Replies
    1. Figured it out :D
      Thanks!

      I am doing everything in a class. the self.psdDict has all the different layers of the master psd stored.
      for layer in self.psdDict['smartObjects']:
      try:
      self.psdDoc.activeLayer = layer
      self.psApp.executeAction(self.psApp.stringIDToTypeID("placedLayerEditContents"))
      print layer.name, '<-'
      smartDoc = self.psApp.activeDocument
      self.psApp.activeDocument.close()
      doc = psApp.activeDocument

      except:
      print 'passed on something here.'
      pass

      Delete

Comments?