I'm trying to make a Photoshop contact sheet with the MakeContactSheet
function included in Photoshop's API. It can be accessed using 'win32com.client'.
My first line:
psApp = Dispatch("Photoshop.Application")
Creates a win32com.gen_py.E891EE9A-D0AE-4CB4-8871-F92C0109F18Ex0x1x0._Application._Application
object.
The class of this object seems to have all the available functions listed in the documentation .
I then proceed to make a list of strings with os.walk.
CSInputFiles = [path.join(r, f) for r, sd, fs in walk('C:\\Users\\chris\\Desktop\\1A') for f in fs]
Then a mixed array of options:
CSoptions = [True, psApp, False, False, 6, True, None, 0, 7, 4, 3, 300, 3, None, True]
Finally I pass these arguments:
psApp.MakeContactSheet(CSInpuFiles, CSoptions)
Which seems to be right considering the function definition in _Application
:
def MakeContactSheet(self, InputFiles=defaultNamedNotOptArg, Options=defaultNamedOptArg):
'create a contact sheet from multiple files'
# Result is a Unicode object
return self._oleobj_.InvokeTypes(1129599816, LCID, 1, (8, 0), ((12, 1), (12, 17)),InputFiles
, Options)
Alas, I get the following error:
Traceback (most recent call last):
File "C:\Users\chris\Desktop\test.py", line 17, in <module>
psApp.MakeContactSheet(CSInputFiles, CSoptions)
File "C:\Users\chris\AppData\Local\Temp\gen_py\3.9\E891EE9A-D0AE-4CB4-8871-F92C0109F18Ex0x1x0\_Application.py", line 97, in MakeContactSheet
return self._oleobj_.InvokeTypes(1129599816, LCID, 1, (8, 0), ((12, 1), (12, 17)),InputFiles
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, 'Adobe Photoshop', 'Illegal argument - argument 2\n- Object expected', None, 0, -2147220261), None)
My initial instinct was to convert all the string in my csInputFiles array to path objects with pathlib.
from pathlib import Path
CSInputFiles = [Path(path.join(r, f)) for r, sd, fs in walk('C:\\Users\\chris\\Desktop\\1A') for f in fs]
Which yielded this obscure piece of garbage when I passed the array to the function:
psApp.MakeContactSheet(CSInputFiles, CSoptions)
#RUN!
File "C:\Users\chris\Desktop\test.py", line 17, in <module>
psApp.MakeContactSheet(CSInputFiles, CSoptions)
File "C:\Users\chris\AppData\Local\Temp\gen_py\3.9\E891EE9A-D0AE-4CB4-8871-F92C0109F18Ex0x1x0\_Application.py", line 97, in MakeContactSheet
return self._oleobj_.InvokeTypes(1129599816, LCID, 1, (8, 0), ((12, 1), (12, 17)),InputFiles
TypeError: must be real number, not WindowsPath
Which doesn't make sense at all! How could it be expecting a real number? This is meant to be an array of input files!
The error specified in the question was caused by the fact that I made the options argument a list as opposed to an object. For information about the structure of the object you can look here.
However after some asking around on GitHub I found that MakeContactSheet
is a deprecated function as stated on page 19 of this document.
This lead me develop a replacement of sorts using the pillow library.
The solution is to simply create an empty png
and then paste all the images you want to put on your contact sheet onto that png
.
I will paste a simplified version of this program, for the sake of solving the problem here.
from PIL import Image
from os import walk, path
#rows = amount of rows (int)
#columns = amount of clomns (int)
#sheetWidth = the width of the contact sheet in inch (int)
#sheetHeight = the height of the contact sheet in inch (int)
#aspectRatio = the simplified height to width ratio of the images on the contact sheet (height/width) (float)
#ppi = pixels per inch
#path = the folder containing all the images you wish to make a contact sheet of (string)
def make_contact_sheet(rows, columns, sheetWidth, sheetHeight, aspectRatio, ppi, filePath):
#convert inches to pixels
sheetWidth *= ppi
sheetHeight *= ppi
#Calculate the size that images will be on the sheet
imageHeight = sheetHeight // columns
imageWidth = int(imageHeight * aspectRatio)
#Calculate how far apart the images will be spaced (I call this padding)
xRemSpace = sheetWidth - (imageWidth * columns)
xPad = xRemSpace // (columns-1)
yRemSpace = sheetHeight - (imageHeight * rows)
yPad = yRemSpace // (rows+1)
#Now make the empty png
sheet = Image.new("RGBA", (sheetWidth, sheetHeight), color=(0,0,0,255))
#Loop through paths of the images and paste the images one by one
x, y = 0, yPad
images = [Image.open(path.join(r,f)) for r,sd,fs in walk(filePath) for f in fs]
for i in range(len(images)):
#Resize the image
resized = images[i].resize((imageWidth, imageHeight))
#Paste the images
if i == 0:
sheet.paste(resized, (x,y))
elif i % columns == 0:
x = 0
y += imageHeight + yPad
sheet.paste(resized, (x,y))
else:
x += imageWidth + xPad
sheet.paste(resized, (x,y))
return sheet
if __name__ == "__main__":
sheet = make_contact_sheet(3, 5, 8, 5, (2/3), 300, r"Some path")
sheet.show()
As stated above this is a very crude example, and it is based off a more elaborate version of the final program I wrote. But I've already overstepped the bounds of relevance.