[python-win32] Autocad automation via COM: Passing coordinates as arguments (suggested fix within)

Dan Glassman dan.glassman at charter.net
Wed Jan 25 20:28:37 CET 2006


>  From: wccppp <wccppp@[...].com>
>  Subject: [python-win32] question about COM again: variable type?
>
>  [code]
>      ms.AddPoint([0.0, 0.0, 0.0])   # this line gives the problem
>  [/code]
>
>  # Result is of type IAcadPoint
>  def AddPoint(self, Point=defaultNamedNotOptArg):
>      """Creates a Point object at a given location"""
>      ret = self._oleobj_.InvokeTypes(1562, LCID, 1, (9, 0), ((12,
>  1),),Point)
>      if ret is not None:
>          ret = Dispatch(ret, 'AddPoint',
>  '{35AF3AB5-755E-4AC9-8BAF-31B532870751}', UnicodeToString=0)
>      return ret
>
Sorry for the long reply.

The type library for AutoCad 2006 says that coordinates should be
passed as variants, so makepy's output is correct.  But you can change
the makepy-generated file to process the argument as an array of doubles
instead of a variant and it will work.  I'm not sure if that's because
the interface will also accept a 'raw' array, or if pythoncom is
magically wrapping the array in a variant.

Taking from the AddPoint method of the IAcadModelSpace class that you've
included:

[code]
ret = self._oleobj_.InvokeTypes(1562, LCID, 1, (9, 0), ((12, 1),),Point)
[/code]

The (12, 1) part describes the Point argument as an (Variant, Input),
roughly.  This should be changed to (8197, 1), which is (Array of
Doubles, Input):

[code]
ret = self._oleobj_.InvokeTypes(1562, LCID, 1, (9, 0),((8197,1),),Point)
[/code]

Unfortunately, this happens all over the place -- not just the AddPoint
method.  It would be very tedious to go through makepy's output and
make the >1500 changes. (12, 1) cannot be changed globally in there.  It 
also happens for more than just coordinate arguments; the Select methods 
of SelectionSet objects have filters which are also arrays wrapped in a 
variant.  I haven't run across any others; the code below fixes 
everything I've found.

My solution was to change build.py (does some of makepy's work; located
in %pythoninstalldir%\lib\site-packages\win32com\client\) to apply this
Variant -> Array change for me when processing the type library.  The
line numbers I reference are from pywin32 2.07; I tried to include
enough context to find the right parts of build.py in case yours is
different.

My understanding of COM is not good, and these changes to build.py don't
seem suitably robust.  It does a small check to see if it's processing
an Autocad library, but I suggest restoring build.py to its original
state after processing your Autocad type libraries.  You'll lose these
fixes for dynamic dispatch in that case.

I would be grateful if somebody could point out any red flags or suggest
a better approach.

Near the top of build.py (~line 52):

[code]
NoTranslateMap = {}
for v in NoTranslateTypes:
         NoTranslateMap[v] = None

#My addition starts here
AutocadTranslateMap = {
      ('alignpoint','anglevertex','arccenter','arcpoint','axisdir',
       'axispoint','basepoint','boundry','center','centerpoint',
       'chordpoint','controlpoint','controlpoints','coordinates',
       'definitionpoint','dimlinelocation','direction',
       'directionvector','endpoint','endtangent','extline1point',
       'extline1startpoint','extline2endpoint','extline2point',
       'extline2startpoint','farchordpoint','firstendpoint','fitpoint',
       'fitpoints','frompoint','insertionpoint','inspoint','jogpoint',
       'knots','knotvalues','leader1point','leader2point',
       'leaderendpoint','limits','lowerleft','lowleft','majoraxis',
       'normal','origin','overridecenter','overridecenterpos',
       'plotorigin','point','point1','point2','point3','point4',
       'pointsarray','pointslist','pointsmatrix','porigin',
       'secondendpoint','secondpoint','snapbasepoint','startpoint',
       'starttangent','target','targetpoint','textalignmentpoint',
       'textpoint','textpos','textposition','topoint',
       'transformationmatrix','upperright','vertex','vertexlist',
       'vertices','verticeslist','weights','wpt','wpt1','wpt2',
       'xaxispoint','xline1point','xline2point','xvector',
       'yaxispoint','yvector'): 8197,
      ('filtertype',): 8193,
      ('filterdata',): 8204
}
#My addition ends here

class MapEntry:
         "Simple holder for named attibutes - items in a map."
         def __init__(self, desc_or_id, names=None, doc=None,
resultCLSID=pythoncom.IID_NULL, resultDoc = None, hidden=0):
[/code]

Then, in the _AddFunc_ method of class DispatchItem (~line 175):

Note that the code below has been stripped of its indentation to
hopefully make it more readable in email.  Indentation within the posted
code is correct, but the entire code block needs to be indented to match
its context in build.py.

[code]
fdesc.rettype = typerepr, flag, defval, resultCLSID
# Translate any Alias or Enums in argument list.
argList = []

#Changes begin here;
#for argDesc in fdesc.args:
for index, argDesc in enumerate(fdesc.args):
      typerepr, flag, defval = argDesc

      #Catch only if reasonably sure this is Autocad
      if self.python_name[:5] == 'IAcad':
          #Catch (VT_VARIANT, FIN) and (VT_VARIANT, FIN|FOPT)
          #Outputs seem to translate into tuples just fine already
          if typerepr == 12 and (flag == 1 or flag == 17):
              if len(fdesc.args) == len(names): #???Properties???
                  replace = [key for key in AutocadTranslateMap.keys() \
                             if names[index].lower() in key]
                  if replace:
                      typerepr = AutocadTranslateMap[replace[0]]
              else: #names[0] is method name; names[1:] is arg names
                  replace = [key for key in AutocadTranslateMap.keys() \
                             if names[index+1].lower() in key]
                  if replace:
                      typerepr = AutocadTranslateMap[replace[0]]
#Changes end here;

      arg_type, arg_clsid, arg_doc = _ResolveType(typerepr, typeinfo)

      argDesc = arg_type, flag, defval, arg_clsid
#    sys.stderr.write("%s\n" % (argDesc[0],))
      argList.append(argDesc)
fdesc.args = tuple(argList)
[/code]


More information about the Python-win32 mailing list