I am trying to translate sample VBA code from Inventor over to python.
Most things, like modifying parameters work really well.
But unfortunately, export to STL is failing.
It seems, that methods of the "STL Exporter Plugin" that should be there are just missing.
In my case: HasSaveCopyAsOptions
and SaveCopyAs
.
(This even seems to happen for other plugins..)
Error message:
AttributeError: '<win32com.gen_py.Autodesk Inventor Object Library.ApplicationAddIn instance at 0x1940552744240>' object has no attribute 'HasSaveCopyAsOptions'
Library.ApplicationAddIn
that I only see default methods?I already set a breakpoint before calling SaveCopyAs
and tried to dir(oSTLTranslator)
.
The resulting list looks quite empty for my expectations.
# -*- coding: utf-8 -*-
import win32com.client
from win32com.client import gencache, Dispatch, constants, DispatchEx
import os
# !!MINIFIED VERSION OF CLASS!!
class InventorAPI:
def loadInventorApp(self):
self.oApp = win32com.client.Dispatch('Inventor.Application')
self.oApp.Visible = True
self.mod = gencache.EnsureModule('{D98A091D-3A0F-4C3E-B36E-61F62068D488}', 0, 1, 0)
self.oApp = self.mod.Application(self.oApp)
# self.oApp.SilentOperation = True
def loadInventorDoc(self):
oDoc = self.oApp.ActiveDocument
self.oDoc = self.mod.PartDocument(oDoc)
def listAllPlugins(self):
no = self.oApp.ApplicationAddIns.Count
print(f"Listing {no} plugins:")
i=0
while (i<no):
i+=1 #Increments first, as index is 1-based
name = self.oApp.ApplicationAddIns.Item(i).ShortDisplayName
id = self.oApp.ApplicationAddIns.Item(i).ClientId
print(f"{i:02}: {id} - {name}")
def saveStl(self, Name):
oSTLTranslator = self.oApp.ApplicationAddIns.ItemById("{533E9A98-FC3B-11D4-8E7E-0010B541CD80}")
if oSTLTranslator is None:
raise( "Could not access STL translator." )
oContext = self.oApp.TransientObjects.CreateTranslationContext()
oOptions = self.oApp.TransientObjects.CreateNameValueMap()
if oSTLTranslator.HasSaveCopyAsOptions(self.oApp.ActiveDocument, oContext, oOptions):
#Set accuracy.
# 0 = High
# 1 = Medium
# 2 = Low
oOptions.SetValue("Resolution", 0)
oContext.Type = kFileBrowseIOMechanism
oData = self.oApp.TransientObjects.CreateDataMedium()
oData.FileName = Name
oSTLTranslator.SaveCopyAs(self.oApp.ActiveDocument, oContext, oOptions, oData)
inv = InventorAPI()
inv.loadInventorApp()
#inv.listAllPlugins()
#exit()
inv.loadInventorDoc()
inv.saveStl("Mount_75mm.stl")
exit()
UPDATE:
[No longer relevant - Check post history, if interested]
UPDATE 2:
As pointed ot by Michael, the issue is indeed with the types!
I created a custom ItemById
method to cast to TranslatorAddIn:
# Result is of type CLSID (default: ApplicationAddIn)
# The method ItemById is actually a property, but must be used as a method to correctly pass the arguments
import pythoncom
defaultNamedNotOptArg=pythoncom.Empty
LCID = 0x0
def ItemByIdEx(oApplicationAddIns, ClientId=defaultNamedNotOptArg, CLSID='{A0481EEB-2031-11D3-B78D-0060B0F159EF}'):
'Retrieves an ApplicationAddIn object based on the Client Id'
ret = oApplicationAddIns._oleobj_.InvokeTypes(50335746, LCID, 2, (9, 0), ((8, 1),),ClientId
)
if ret is not None:
ret = win32com.client.Dispatch(ret, 'ItemById', CLSID) #<== CHANGED THIS, original code has default ID of ApplicationAddIn
return ret
With this, my object oSTLTranslator has the missing methods available. I can now call
#oSTLTranslator = self.oApp.ApplicationAddIns.ItemById("{533E9A98-FC3B-11D4-8E7E-0010B541CD80}")
oSTLTranslator = ItemByIdEx(self.oApp.ApplicationAddIns, "{533E9A98-FC3B-11D4-8E7E-0010B541CD80}", "{6ECCBC87-A50D-11D4-8DE4-0010B541CAA8}")
UPDATE 3:
Custom method is not even needed!
The purpose of self.mod
is to serve as type-caster.
Just do: oSTLTranslator = self.mod.TranslatorAddIn(oSTLTranslator)
Final function:
def saveStl(self, Name):
oSTLTranslator = self.oApp.ApplicationAddIns.ItemById("{533E9A98-FC3B-11D4-8E7E-0010B541CD80}")
if oSTLTranslator is None:
raise ValueError("Could not access STL translator." )
oSTLTranslator = self.mod.TranslatorAddIn(oSTLTranslator)
oContext = self.oApp.TransientObjects.CreateTranslationContext()
oOptions = self.oApp.TransientObjects.CreateNameValueMap()
if oSTLTranslator.HasSaveCopyAsOptions(self.oApp.ActiveDocument, oContext, oOptions):
#Set accuracy.
# 2 = High
# 1 = Medium
# 0 = Low
#For completeness - usually no need to set all of these
oOptions.SetValue("Resolution", 0)
oOptions.SetValue("SurfaceDeviation", 5.0)
oOptions.SetValue("NormalDeviation", 1000.0)
oOptions.SetValue("MaxEdgeLength", 100000.0)
oOptions.SetValue("AspectRatio", 2150.0)
oOptions.SetValue("OutputFileType", 0)
oContext.Type = win32com.client.constants.kFileBrowseIOMechanism
oData = self.oApp.TransientObjects.CreateDataMedium()
oData.FileName = Name
oSTLTranslator.SaveCopyAs(self.oApp.ActiveDocument, oContext, oOptions, oData)
I'm sorry I'm not familiar with python. But the issue is in used types. Method ApplicationAddIns.ItemById returns object of type ApplicationAddIn. But translators are of type TranslatorAddIn which is derived from ApplicationAddIn.
Try to re-type variable oSTLTranslator
to TranslatorAddIn and I expect it will work.