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'
- Is there something like a "refresh" method for win32com?
- Is it due to the instance type being
Library.ApplicationAddInthat 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)
win32comwrapper files are being generated. See this: stackoverflow.com/questions/52889704/…. The first step is to find yourgen_pyfolder and and delete all the folders under your Python version (eg3.9). This will force a regeneration of thegencachewrapper .py files (in a CLSID folder) and try your code again. If you still get the error, find the wrapper file and check (a) your function is there, and (b) you have spelled it correctly (case-sensitive).3.10btw). 1st run afterwards failed, 2nd+ runs: Same behaviour as before. Let me add some info per EDIT