I've been racking my brain over this for a few days now. I'm attempting to write a program in Python 3.1 / PyQt 4.7.5 that will open a Powerpoint, go through every shape on every slide, determine if it is a picture and then save that picture to the specified directory with the correct name and extension. So far everything works. The GUI is ok, the logic for my code is fine and it determines the shapes as it should... the only problem is when I try to export the shapes. When it gets to actually exporting the shapes I get this traceback:

Traceback (most recent call last):
  File "C:\Documents and Settings\root\My Documents\Projects\PyQT\PPT Photo Exporter\Methods.py", line 94, in exportPhotos
    Shape.Export(self.currentOutDir.join(self.counterStr), 13)
  File "C:\Python31\lib\site-packages\win32com\MSOPPT.py", line 7643, in Export
    , Filter, ScaleWidth, ScaleHeight, ExportMode)
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2147467259), None)

The method that I'm using is:

def exportPhotos(self):
        self.currentFileName = self.view.pptPath.text()
        self.currentOutDir = self.view.outPath.text()

        g = globals()
        for c in dir(self.msoModule.constants):    g[c] = getattr(self.msoModule.constants, c)
        for c in dir(self.pptModule.constants): g[c] = getattr(self.pptModule.constants, c)
        
        if self.currentFileName == '':
            QtGui.QMessageBox.information(self.view, "Empty path",
                    "Sorry, but it looks like the file path is empty!")
            return
        
        if self.currentOutDir == '':
            QtGui.QMessageBox.information(self.view, "Empty path",
                    "Sorry, but it looks like you didn't type in the output directory!")
            return
        
        self.loadedPPT = win32com.client.Dispatch("Powerpoint.Application")
        self.loadedPPT.Visible=1
        self.presentation = self.loadedPPT.Presentations.Open(self.currentFileName)
        self.counter = 0
        for Slide in self.presentation.Slides:
            for Shape in Slide.Shapes:
                if Shape.Type == 13:  #The 13 is the enum value for ppShapeFormatJPG
                    self.counter =+ self.counter + 1
                    self.counterStr = "/"+str(self.counter)+".jpg"
                    print(self.currentOutDir + self.counterStr)
                    Shape.Export(self.currentOutDir.join(self.counterStr), 13)
                else:
                    print("not a photo")
        self.presentation.Close()
        self.loadedPPT.Quit()

The reference is from the module generated by PyWin for Powerpoint COM objects:

def Export(self, PathName=defaultNamedNotOptArg, Filter=defaultNamedNotOptArg, ScaleWidth=0, ScaleHeight=0
			, ExportMode=1):
		return self._oleobj_.InvokeTypes(2023, LCID, 1, (24, 0), ((8, 1), (3, 1), (3, 49), (3, 49), (3, 49)),PathName
			, Filter, ScaleWidth, ScaleHeight, ExportMode)

The 13 in the Shape.Export method is the enum value for ppShapeFormatJPG and currentOutDir is the directory specified by the user in the GUI. The print statements are being used to verify my logic and correct naming scheme of the exported files.
MSDN doc:http://msdn.microsoft.com/en-us/library/microsoft.office.interop.powerpoint.shape.export.aspx

Everything seems to be in the right place, but it's throwing some unnamed exception. Any help you are able to give would be fantastic. The sources for Pywin problems are few and far between and I haven't been able to find much. I've found some help on python-forum.org which helped me figure out the enum values and such, but so far no luck on this exception. Any help you are able to give would be very much appreciated.

Recommended Answers

All 6 Replies

join() interleaves a list with a constant so
self.currentOutDir.join(self.counterStr)
would produce
self.counterStr[0]+self.currentOutDir+self.counterStr[1]+self.currentOutDir+self.counterStr[2]...etc.
A simple example:

x = ["1", "2", "3"]
print "a".join(x)
#
x = "123"
print "b".join(x)

I see what you're talking about. I wonder why Python decided upon that behaviour, but I went ahead and changed my method to just concatenate the strings to produce the full directory/filename.extension. Now the only thing is I have no idea what the error messages that are produced by the COM object are.

File "C:\Python31\lib\site-packages\win32com\MSOPPT.py", line 7643, in Export
    , Filter, ScaleWidth, ScaleHeight, ExportMode)
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2147467259), None)

I think I've figured out that the last number is the actual error number, but so far googling and looking through documentation hasn't given me anything as to the error message in relation to Powerpoint.

Here is my revised method:

def exportPhotos(self):
        self.currentFileName = self.view.pptPath.text()
        self.currentOutDir = self.view.outPath.text()

        g = globals() # These lines allow access to COM Object constants
        for c in dir(self.msoModule.constants):    g[c] = getattr(self.msoModule.constants, c)
        for c in dir(self.pptModule.constants): g[c] = getattr(self.pptModule.constants, c)
        
        if self.currentFileName == '':
            QtGui.QMessageBox.information(self.view, "Empty path",
                    "Sorry, but it looks like the file path is empty!")
            return
        
        if self.currentOutDir == '':
            QtGui.QMessageBox.information(self.view, "Empty path",
                    "Sorry, but it looks like you didn't type in the output directory!")
            return
        
        self.loadedPPT = win32com.client.Dispatch("Powerpoint.Application")
        self.loadedPPT.Visible=1
        self.presentation = self.loadedPPT.Presentations.Open(self.currentFileName)
        self.counter = 0
        for Slide in self.presentation.Slides:
            for Shape in Slide.Shapes:
                if Shape.Type == 13:  #The 13 is the enum value for Shape.Type.msoPicture

                    #This uncrops the picture and set it back to original size
                    Shape.PictureFormat.CropLeft = 0
                    Shape.PictureFormat.CropRight = 0
                    Shape.PictureFormat.CropTop = 0
                    Shape.PictureFormat.CropBottom = 0
                    Shape.ScaleHeight(1, -1) # -1 = msoTrue
                    Shape.ScaleWidth(1, -1)

                    #This will create and verify the image paths (you can view console to verify)
                    self.counter =+ self.counter + 1
                    self.counterStr = "/image"+str(self.counter)+".png"
                    print(self.currentOutDir+self.counterStr)

                    #The 2 is the filter for PNG, 1000=width, 1000=height, 4=Export and scale to XY 
                    Shape.Export("C:\\"+self.counterStr, 2, 1500, 1000, 4)
                    
                else:
                    print("not a photo")
        self.presentation.Close()

See where I have the directory "C:\\" hardcoded? It works like a charm. If I set it to self.currentOutDir which is a variable that represents a string (which in turn began as a QLineEdit object) then it raises the exception. I cannot figure out how I can assign the path as the user selected one instead of having one hard coded without raising the exception.

I finally figured it out! Python interprets file directories as "C:\wherever\to\wherever" which it then feeds to the Windows API. The way I bypassed this was by forcing the slashes to convert to windows style right before I fed them to the Windows API with the following fix:

#This will create and verify the image paths (you can view console to verify)
                    self.counter =+ self.counter + 1
                    self.counterStr = "/image"+str(self.counter)+".png"
                    self.newDir = self.currentOutDir.replace('/', '\\')
                    print(self.newDir+self.counterStr)

                    #The 2 is the filter for PNG, 1500=width, 1000=height, 4=Export and scale to XY 
                    Shape.Export(self.newDir+self.counterStr, 2, 1500, 1000, 4)

Notice the new variable and the .replace method. That's all I ended up changing for it to work.

A cross platform solution is os.path.join(). The only gottcha is it sometimes gets confused when a "/" or "\" is included with the right-most string
print os.path.join("dir_1", "dir_2", "image"+str(12)+".png")

Thank you very much for the cross platform advice! I'm sure that's gonna come in handy on a future project (and now I won't ave to figure out that mystery)!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.