An invaluable help is the Photoshop Cs4 Visual basic scripting guide. Although the scripting guide does not specifically deal with Python, the majority of the VB code works straight out of the box.
Before anything else, you need to set up the COM interface! I recommend using the comtypes module. The win32com module can also do the job, but you will find it very limiting if you want to do anything with paths or selection arrays.
This is how I usually create my photoshop COM reference in my scripts:
import comtypes.client psApp = comtypes.client.CreateObject('Photoshop.Application')The psApp object that I have created is my reference to the photoshop application. From now on, I can call psApp to execute various actions. It's worth noting that if you call your script and Photoshop is not already open, the comtypes client will start the program for you.
Most of my scripts deal with the current active photoshop document- if you have more than one photoshop document open, the reference will point to whichever one is active in the layers window. This is how you create a reference to that object.
doc = psApp.activeDocument
In some instances you may need to create a new photoshop document to perform work on. Notice that I included setting the default units. If you skip this you might end up making a 1024cm x 1024cm document by mistake! (true story.)
#Set units to pixels. psApp.Preferences.RulerUnits = 1 #Make a new photoshop document doc = psApp.Documents.Add(1024, 1024, 72, "new_document", 2, 1, 1)
As you will notice, its a little more complicated and involves a few seemingly random arguments. Here is a breakdown of the arguments in order:
Add(width, height, dpi/resolution, document name, mode, initial-fill, aspect-ratio, bits per channel, color profile)
Most of these are pretty straight forward, but some need a little bit of clarification- I suggest looking at page 71 of the Photoshop Cs4 Visual basic scripting guide for more details, under Methods- Add.
Adding groups and art layers with a script is very handy. It can be a powerful tool in automatically setting up document templates. Here is how to set up a base color, create a new art layer and then fill it with your color.
#Define the color- in this case, flat normal blue baseColor = comtypes.client.CreateObject('Photoshop.SolidColor') baseColor.rgb.red = 128 baseColor.rgb.green = 128 baseColor.rgb.blue = 255 #Create a new layer to apply our color to. When you add a new layer, it becomes the active layer. new_art_layer = new_layerSet.ArtLayers.Add() new_art_layer.name = 'flatNormal' #Fill the layer with appropriate color psApp.activeDocument.selection.selectAll psApp.activeDocument.selection.Fill(baseColor)
Defining save options is also something I do alot. Depending on which format your project demands, you will find you need to use different methods of defining options. I use the TGA and PSD formats, but Adam Pletcher has a good example of PNG save options.
This is the code to define TGA and PSD save options, and how to execute it:
doc = psApp.activeDocument # Define 24bit Targa save options options = comtypes.client.CreateObject('Photoshop.TargaSaveOptions') PsTargaBitsPerPixels = 24 options.Resolution = PsTargaBitsPerPixels options.AlphaChannels = False options.RLECompression = False # Define 32bit Targa save options optionsAlpha = comtypes.client.CreateObject('Photoshop.TargaSaveOptions') PsTargaBitsPerPixels = 32 optionsAlpha.Resolution = PsTargaBitsPerPixels optionsAlpha.AlphaChannels = True optionsAlpha.RLECompression = False # Define PSD save options psdOptions = comtypes.client.CreateObject('Photoshop.PhotoshopSaveOptions') psdOptions.annotations = False psdOptions.alphaChannels = True psdOptions.layers = True psdOptions.spotColors = True psdOptions.embedColorProfile = True #Save in the different ways #PSD doc.SaveAs('c:\\yourpath\\filename.psd',psdOptions) #TGA doc.SaveAs('c:\\yourpath\\filename.tga', options) #TGA32 doc.SaveAs('c:\\yourpath\\filename.tga', optionsAlpha)I have found in photoshop Cs2 that in order to save as a TGA from a layered PSD, you have to flatten the document before saving it, otherwise it will think you want to save it as a copy, which has to be one of the most annoying things ever. Without going into all the hacky work arounds, here is a good chance to bring up one final handy thing... how to undo hacky work-arounds with history states!
Here is how you can save and recall a history state in a document using python:
#save the current setup saved_state = doc.activeHistoryState #Do some stuff, like flatten the document and copy it... doc.flatten() doc.activeLayer.Copy() #Now revert to the saved state and paste the merged copy in as a new layer doc.ActiveHistoryState = saved_state doc.Paste()
Finally, calling your script from Photoshop is pretty important if you want the artists not to hate you for making them use the console (more likely they just won't use your tool!).
I have yet to find a way to execute my Python script directly, but you can use intermediate scripts to call them from an action. I will readily admit this is a clumsy way of doing it, but if anyone has a more streamlined approach to this I would love to know!
First, make a .bat file that references your python script... the following script has the handy addition that it references the scripts directory relative to it's current location using %~dp0.
@echo off
call python %~dp0\\yourPythonScript.py
Then, you can have a Photoshop javascript call the bat file...
var script = new File("/c/scriptLocation/run_yourPythonScript.bat"); script.execute();
Finally, you have a photoshop action that calls the javascript.
Pros to this method:
Callable from within Photoshop via an action.
It works!
Cons:
-Convoluted setup.
-Calls up a cmd window on execution (which can be good for error feedback, but is also a little irritating).
To finish off with, here is a full example of a working Python script that makes use of some of the code listed above. This script creates a new document, complete with pre-named groups and art layers, ready for an artist to work on.
############################################################################## # # Photoshop doc setup script # Author: Pete Hanshaw, 2012 # http://peterhanshawart.blogspot.com.au/ # ############################################################################## # # Creates a new PSD document and sets up placeholder groups and layers # ready for texture work. # # Creates: # -'n' group for normal map # -'s' group for specular map # -'d' group for diffuse map # # Requires comtypes: # http://sourceforge.net/projects/comtypes/ # ############################################################################## #Import required modules import comtypes.client from sys import exit import pythoncom #Set up the document. More groups can be added easily by creating more dict keys group_names = {} #Group name : artLayer name group_names['d'] = 'dif_base' group_names['s'] = 'spec_base' group_names['n'] = 'nrm_base' #Begin the script if (__name__ == '__main__'): #COM dispatch for Photoshop psApp = comtypes.client.CreateObject('Photoshop.Application') #Define the fill colors #Normal base nrm_SolidColor = comtypes.client.CreateObject('Photoshop.SolidColor') nrm_SolidColor.rgb.red = 128 nrm_SolidColor.rgb.green = 128 nrm_SolidColor.rgb.blue = 255 #Define the fill colors #Spec base spec_SolidColor = comtypes.client.CreateObject('Photoshop.SolidColor') spec_SolidColor.rgb.red = 0 spec_SolidColor.rgb.green = 0 spec_SolidColor.rgb.blue = 0 #Define the fill colors #Diffuse base dif_SolidColor = comtypes.client.CreateObject('Photoshop.SolidColor') dif_SolidColor.rgb.red = 128 dif_SolidColor.rgb.green = 128 dif_SolidColor.rgb.blue = 128 #Set the default unit to pixels! psApp.Preferences.RulerUnits = 1 #w, h, res, name, mode, initial-fill, asp-ratio, Bits-P/Ch, ColProfile new_doc = psApp.Documents.Add(1024, 1024, 72, "new_source_texture", 2, 1, 1) print "Setting up a", new_doc.name for group, layer in group_names.items(): new_layerSet = new_doc.LayerSets.Add() new_layerSet.name = group if group == 'n': new_art_layer = new_layerSet.ArtLayers.Add() new_art_layer.name = layer #Fill the layer with appropriate color #Filltype, model, opacity, preserveTransparancy psApp.activeDocument.selection.selectAll psApp.activeDocument.selection.Fill(nrm_SolidColor) new_layerSet.Visible = False elif group == 's': new_art_layer = new_layerSet.ArtLayers.Add() new_art_layer.name = layer #Fill the layer with appropriate color #Filltype, model, opacity, preserveTransparancy psApp.activeDocument.selection.selectAll psApp.activeDocument.selection.Fill(spec_SolidColor) new_layerSet.Visible = False elif group == 'd': new_art_layer = new_layerSet.ArtLayers.Add() new_art_layer.name = layer #Fill the layer with appropriate color #Filltype, model, opacity, preserveTransparancy psApp.activeDocument.selection.selectAll psApp.activeDocument.selection.Fill(dif_SolidColor) else: new_art_layer = new_layerSet.ArtLayers.Add() new_art_layer.name = layer #Fill the layer with appropriate color #Filltype, model, opacity, preserveTransparancy psApp.activeDocument.selection.selectAll psApp.activeDocument.selection.Fill(dif_SolidColor) #Reorder the photoshop layers so that it reads from top down: #normal, spec, diffuse dGroup = psApp.activeDocument.layerSets['d'] #See Adobe's photoshop_cs4_vbscript_ref.pdf to make sense of this- dGroup.Move(new_doc, 2) #Deselect the fill area psApp.activeDocument.selection.deselect #Set the active layer to the diffuse psApp.activeDocument.activeLayer = (psApp.activeDocument.layerSets['d']. artLayers['dif_base']) print "All done!" exit(1)So that's just a handful of useful bits and bobs. If you try any of the code here, and it doesn't work, let me know and I'll update the post with a fix.
-Pete
This is great.
ReplyDeleteIs there a way to close PhotoShop after running a script?
psApp = comtypes.client.Close?
Hi Zack,
DeleteGlad you found the post useful! Yeah, its pretty simple to add. Just use your object reference with the quit command, for example:
#Object reference
psApp = comtypes.client.CreateObject('Photoshop.Application')
#Quit command
psApp.Quit()
Thanks for this! Very helpful. I've dug around Adobe's COM scripting manual, but haven't found what to call in order to add a drop shadow to a layer. Any idea how this would be done?
ReplyDeleteHi Daniel, I'm glad that you have found the snippets helpful. I don't think that the drop shadow function is exposed to the COM interface, but you can get around this limitation (as well as many others) by using the Photoshop script listener.
DeleteI've written a post that shows an example of how to go through script-listener output and hammer it into useful python code:
http://peterhanshawart.blogspot.com.au/2012/05/python-and-photoshop-script-listener.html
The example is for selecting a color range in an image, but the same ideas can be applied to recording the drop shadow functionality.
Good luck :)
Thanks Pete for this. I am just starting out with Python & Photoshop COM interface.
ReplyDeletePresently i am trying to open the PSD file, do some stuff and save as a TIFF file with custom settings. Almost everything worked without hassles, thanks to your snippets.
But i am stuck with, the photoshop Save options part.
Trying the following code gives error.
psdOptions = comtypes.client.CreateObject('Photoshop.PhotoshopSaveOptions')
Python is unable to Create the SaveOptions or TiffSaveOptions Object.
------------------------ERROR START----------------------------------------------
Traceback (most recent call last):
File "D:\dev\eclipse\workspace\PsAppConnection\src\ConnectionModule.py", line 14, in
psSave = PSCOM.CreateObject('Photoshop.PhotoshopSaveOptions')
File "C:\Python27\lib\site-packages\comtypes\client\__init__.py", line 224, in CreateObject
clsid = comtypes.GUID.from_progid(progid)
File "C:\Python27\lib\site-packages\comtypes\GUID.py", line 78, in from_progid
_CLSIDFromProgID(unicode(progid), byref(inst))
File "_ctypes/callproc.c", line 941, in GetResult
WindowsError: [Error -2147221005] Invalid class string
------------------------ERROR END----------------------------------------------
I am aware of the option of getting Action Descriptor Code, but wanted to know how it worked for you and why it is not working for me.
Thanks.
Priyabrata
Hi Priyabrata,
DeleteSorry for the late response! If you have not already fixed your issue, the information you need is contained in the VBScripting documentation (See the links window on the right side of my blog :) )
The reason your code was not working is because TIFF files have their own save options, separate to those used to save a PSD.
Here is a quick example of how to save a TIFF:
---
import win32com.client as w32
PS_APP = w32.Dispatch('Photoshop.Application')
# Make a new document
doc = PS_APP.Documents.Add(512, 512, 72, "SaveTest", 2, 1, 1)
fileName = "C:\\test.tif"
# Tiff save options are a unique object, with it's own methods
# Seperate from the PhotoshopSaveOptions
tiffOptions = w32.Dispatch('Photoshop.TiffSaveOptions')
tiffOptions.AlphaChannels = False
tiffOptions.EmbedColorProfile = True
tiffOptions.ImageCompression = 3
tiffOptions.Layers = False
# Save it!
doc.SaveAs(fileName, tiffOptions)
---
This comment has been removed by the author.
ReplyDeleteHeya
ReplyDeleteThanks for this great tutorial ! I'm just starting my adventure with Python for PS and this is great start!
However quick issue I found in it...
ur code is:
new_art_layer = new_layerSet.ArtLayers.Add()
should it not be
new_art_layer = doc.ArtLayers.Add()
This one works on my end at least:)
Thanks again for help !
Hey Dariusz,
DeleteI'm glad you are finding the examples useful! About the issue you found- Yes, calling new_layerSet.ArtLayers.Add() by itself will not work.
In the example above, "new_layerSet" is actually a reference to the object returned by calling "new_layerSet = new_doc.LayerSets.Add()" on line 71. Out of the context of the for loop this will not work, as the program will not know what you are referring to when you say "new_layerSet".
In the context of the script example above it's doing something along the lines of calling:
# In Photoshop's current active document, in the group called 'd', add another art layer, and assign the returned the object reference as 'new_art_layer'
new_art_layer = ps_app.activeDocument.LayerSets['d'].ArtLayers.Add()
The result will be that the script will place a new art layer in the Layer Set (or group) that is named 'd'.
Hi :)
ReplyDeleteGreat tutorial, but i have question. Why class didn't have a Adobe Fireworks?
Regards,
Robert.
Hi Robert,
DeleteGlad you found the tutorial useful! I'm not sure if I understand you about the Adobe Fireworks. I believe that Fireworks is being Phased out by Adobe, but other than that I am not too familiar with the Fireworks package.
All the best!
-Pete