Background
I've built a simple Python module to wrap the SolidWorks API for a streamlined scripting workflow. I've created a first-pass working system via COM (win32com.client & pythoncom), but it's a seriously tedious and error-prone process; I haven't included the SolidWorks type (sldworks.tlb) and constants libraries, so I can't take advantage of type hinting or IntelliSense. I'm just now discovering these libraries are even a thing!
I'm building out a second more robust iteration, and I'd like to implement the SolidWorks type library for a more intuitive, safer, and easier development process. This is my first go at using makepy and working with COM, and I'm having quite a difficult time wrapping (no pun intended) my head around how exactly it all fits together.
Problem
I used makepy to import the SolidWorks type library (as solidtypes.py) into my project. Now, I can make direct type calls as I build out the code. Sweet!
I'm absolutely stumped, though, with actually making use of the API. I've tried a number of variations for the arguments in open_model(), for example, because my hunch is that the way those are defined is no longer appropriate for this revised approach. I'm struggling to find and interpret resources to understand how to handle these function arguments and return types.
Some useful things I've come across include this blog post, Microsoft's own documentation of VARIANT Type Constants, as well as some other resources that got me started with all of this. Most of what I'm seeing online does not seem to make use of sldworks.tlb, and tends to reflect what is shown in my original approach below.
Original Approach
The following snippet is a condensed excerpt of a working set of functions from version 1.0 of the module. These two methods belong to a core class (not shown here) that must be instantiated by the client's main() call. The connect() method is called upon instantiation, and a call to open_model() can be made anytime thereafter:
# Module imports
import win32com.client as win
import pythoncom as pycom
def connect(self):
"""Establishes connection to a SolidWorks client."""
if client := win.Dispatch("SldWorks.Application.%d" % (int(self.version)-2012+20)):
return client
def open_model(self, filepath: str) -> Model:
"""Opens a model using a complete file path"""
# Define arguments as COM VARIANTs
arg1 = win.VARIANT(pycom.VT_BSTR, filepath)
arg2 = win.VARIANT(pycom.VT_I4, 1)
arg3 = win.VARIANT(pycom.VT_I4, 1)
arg4 = win.VARIANT(pycom.VT_BSTR, "")
arg5 = win.VARIANT(pycom.VT_BYREF | pycom.VT_I4, 2)
arg6 = win.VARIANT(pycom.VT_BYREF | pycom.VT_I4, 128)
# Execute SW-API function call
return self.client.OpenDoc6(arg1, arg2, arg3, arg4, arg5, arg6)
From what I've gathered, Pythonic types (ints, strings, etc.) must be converted to COM-compatible objects such that they can interact with SolidWorks' COM-compatible API. Or... something along those lines.
New Approach
Here's how the connect() function has evolved in version 2.0, now leveraging sldworks.tlb (as solidtypes):
import solidtypes
def connect(self):
"""Establishes connection to a SolidWorks client."""
# Instantiate SolidWorks application via COM using concrete CLSID
if client := solidtypes.ISldWorks(win.Dispatch(solidtypes.ISldWorks.coclass_clsid)):
return client
This seems to perform appropriately; IntelliSense will now reveal type names like ISldWorks and its associated methods.
But, attempting to recycle the original open_model() method yields the following error (skipping some of the upstream Traceback calls):
File "d:\Programming\My Files\Development\SolidWorks Automation\SolidWrap\vendor\solidtypes.py", line 57113, in OpenDoc6
return self._ApplyTypes_(167, 1, (9, 0), ((8, 1), (3, 1), (3, 1), (8, 1), (16387, 3), (16387, 3)), 'OpenDoc6', '{B90793FB-EF3D-4B80-A5C4-99959CDB6CEB}',FileName
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\Programming\App Data\Python\Python3\Lib\site-packages\win32com\client\__init__.py", line 574, in _ApplyTypes_
self._oleobj_.InvokeTypes(dispid, 0, wFlags, retType, argTypes, *args),
^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\Programming\App Data\Python\Python3\Lib\site-packages\win32com\client\dynamic.py", line 638, in __getattr__
raise AttributeError("%s.%s" % (self._username_, attr))
AttributeError: <unknown>.InvokeTypes
I'm having a hell of a time identifying what exactly is causing this.
For reference, here is what the makepy-generated OpenDoc6() (SolidWorks API's method for opening models) looks like:
# Result is of type IModelDoc2
def OpenDoc6(self, FileName=defaultNamedNotOptArg, Type=defaultNamedNotOptArg, Options=defaultNamedNotOptArg, Configuration=defaultNamedNotOptArg
, Errors=defaultNamedNotOptArg, Warnings=defaultNamedNotOptArg):
'Opens an existing document'
return self._ApplyTypes_(167, 1, (9, 0), ((8, 1), (3, 1), (3, 1), (8, 1), (16387, 3), (16387, 3)), 'OpenDoc6', '{B90793FB-EF3D-4B80-A5C4-99959CDB6CEB}',FileName
, Type, Options, Configuration, Errors, Warnings)
And just for kicks, here's what a different makepy-generated function looks like (which I am also failing to implement correctly):
def NewPart(self):
'Creates a new part document'
ret = self._oleobj_.InvokeTypes(8, LCID, 1, (9, 0), (),)
if ret is not None:
ret = Dispatch(ret, 'NewPart', None)
return ret
At present, I'm only focused on the more fundamental tasks like opening / saving / closing models. If I can just figure out the missing element here I should be cruising. On top of it all, I'm relatively new to Python (I have a C++ background). Any help is greatly appreciated!