143

I would like to be able to script some things that are not exposed via arcgisscripting or ArcPy.

How do I access ArcObjects from Python?

1
  • 3
    Anyone have thoughts on using these methods in production environments? A few years ago at the UC I talked with one of the ESRI C++ devs who writes most of their Python modules and he was strongly against using IronPython in a production environment - but that was a few years ago. Oct 28, 2010 at 13:12

5 Answers 5

124

Download and install comtypes*, put the Snippets module from Mark Cederholm in PYTHONPATH, and you're all set.

from snippets102 import GetLibPath, InitStandalone
from comtypes.client import GetModule, CreateObject
m = GetModule(GetLibPath() + "esriGeometry.olb")
InitStandalone()
p = CreateObject(m.Point, interface=m.IPoint)
p.PutCoords(2,3)
print p.X, p.Y

For background see Mark Cederholm's presentations for UberPyGeeks on "Using ArcObjects in Python". There are separate ones for VBA and C++ developer perspectives. They use Visual Studio (yes Express is ok) and the Windows SDK, but these aren't required, just ArcGIS, Python and comtypes are sufficient.

Getting the Snippets module

* Note for 10.1+ you need to make a small change to automation.py in the comtypes module. See ArcObjects + comtypes at 10.1.


Next steps

...or: brain gone snaky? Looking at the c# code examples makes your eyes swim, and try as you might you just can't think like a crane? Look here:

9
  • 12
    This thread has been brought to my attention, and it seems there are some misconceptions floating around. You do NOT need Visual Studio or the MIDL compiler in order to use ArcObjects in Python; all you need is the comtypes package. My presentation included an advanced exercise in creating a COM component using Python, but that was meant for UberPyGeeks. The snippets.py file contains all the examples you need.
    – user2715
    Apr 18, 2011 at 14:39
  • 1
    @Mark, many thanks for the correction. To be clear: in the above recipe one could remove steps 1,3,4 as long as ctypes is already installed? Apr 18, 2011 at 17:15
  • 2
    Yeah. Tested it at the lab. You just need to install the comtypes.
    – R.K.
    Feb 5, 2012 at 14:34
  • @R.K., great! Thanks for the verification and bringing the answer up to date. Feb 6, 2012 at 17:09
  • 1
    Snippets module recast as installable ao module here: github.com/maphew/arcplus/tree/master/arcplus/ao (updated for 10.3, see previous versions of file for 10.2). Will update main answer once I get some bugs cleared out. Jan 7, 2015 at 23:51
35

Yes, Mark Cederholm's presentation that Matt Wilkie mentions above is a great place to start. The recipe/code that Matt presents is certainly a slick and probably the best way to go about things. I wanted to mention, though, the rather brute force method I am using in ArcGIS 10.0. I have several automation scripts (standalone, outside the application boundary) that I run this way, and they work just fine. If maximum speed is a concern, though, you might just go with Matt's solution and be done with it.

I use the comtypes package to force wrapping of all ArcObjects libraries (.olb). Then Python has access to all of ArcObjects. I got the wrapping code from Frank Perks through an ESRI forum posting. I had my own code that essentially did the same thing, but it was bloated and merely functional, while his is much prettier. So:

import sys, os
if '[path to your Python script/module directory]' not in sys.path:
    sys.path.append('[path to your Python script/module directory]')

import comtypes
#force wrapping of all ArcObjects libraries (OLBs)
import comtypes.client
# change com_dir to whatever it is for you
com_dir = r'C:\Program Files (x86)\ArcGIS\Desktop10.0\com'
coms = [os.path.join(com_dir, x) for x in os.listdir(com_dir) if os.path.splitext(x)[1].upper() == '.OLB']
map(comtypes.client.GetModule, coms)

Then, pretty much straight out of Mark Cederholm's presentation:

import comtypes.gen.esriFramework

pApp = GetApp()

def GetApp():
    """Get a hook into the current session of ArcMap"""
    pAppROT = NewObj(esriFramework.AppROT, esriFramework.IAppROT)
    iCount = pAppROT.Count

    if iCount == 0:
        print 'No ArcGIS application currently running.  Terminating ...'
        return None
    for i in range(iCount):
        pApp = pAppROT.Item(i)  #returns IApplication on AppRef
        if pApp.Name == 'ArcMap':
            return pApp
    print 'No ArcMap session is running at this time.'
    return None

def NewObj(MyClass, MyInterface):
    """Creates a new comtypes POINTER object where\n\
    MyClass is the class to be instantiated,\n\
    MyInterface is the interface to be assigned"""
    from comtypes.client import CreateObject
    try:
        ptr = CreateObject(MyClass, interface=MyInterface)
        return ptr
    except:
        return None

That's it. You should have full access to ArcObjects starting with the pApp object that is IApplication on the AppRef object. In my experience, the wrapping of the ArcObjects libraries on the first run isn't objectionably slow, and for subsequent runs the wrapping doesn't happen. The libraries are already wrapped and compiled, so things are much speedier.

Added: There is a large caution that comes with this. The NewObj function given here assumes the Python script is being run in-process. If not, this function will create objects in the Python process (ie. out-of process), and object references will be wrong. To create in process objects from an external Python script you should use IObjectFactory. See Kirk Kuykendall's comments and tips in this stackexchange post for more info.

2
  • 2
    The nice thing about this approach is that it doesn't require installing Visual Studio, which is pretty heavyweight if the only thing you'll ever do with it is register the com objects. If I'd known about this pure python method I'd likely never have tried Cederholm's route. ;-) Jan 10, 2011 at 6:14
  • 1
    I expanded on this concept a little in this answer, making the imports and wrapping the type libraries a one-step process: gis.stackexchange.com/questions/5017/…
    – blah238
    Jan 12, 2011 at 23:05
22

How do I access arcobjects from python?

If what you are looking for is specific functionality that exists and is in the C++ Arcobjects code, then your best bet would be to create C++ methods to call them.... and then create a python wrapper to access those C++ methods.

There are quite a few ways to access C++ methods from python, and lots of people that do so use a tool like SWIG to auto-generate the python classes from the C++ method signatures. It's been my experience that these autogenerated APIs get pretty nasty when passing non-native C++ types (int, floats) and are never very 'pythonic'.

My recommended solution would be to use the ctypes API. A great tutorial is here: http://python.net/crew/theller/ctypes/tutorial.html

The basic steps are:

  1. write some of your core logic in C++, the ones where you believe python performance might be an issue
  2. Compile that core logic (in this case using the ArcObject C++ API method calls) from object files into a shared (.so) or dynamic library (.dll) using whatever system compiler (nmake,make, etc.)
  3. Write a ctypes mapping between the python class and the C++ method signature [SWIG tries to automate this, but it's easy, even if using crazy object types]
  4. Import the compiled library into your python program and use the bound class/method bindings like any other python class!

This is probably more general way to reference C/C++ code from within python, it is probably going to be much simpler in the long run if you don't have to deal with COM objects. It also will allow all of the system specific functionality reside in the compilation of the linked library object (so the python will not be system/python implementation specific).

1
  • 4
    This is far and away the best method to interact with ArcObjects. The use of comtypes is great for prototyping but writing your own C extension will result in a good, Pythonic design (because you have to think about it) and better performance because you don't cross the C++/Python object barrier as much. Though for practical purposes, this is harder to design/develop/debug so the quick-and-dirty goes out the window. Feb 3, 2011 at 1:08
19

Another option is to use Python for .NET - this is very easy to set up, and can work with any .NET DLLs including ArcObjects.

I've not experienced any issues with in-process obects, and opening up an instance of ArcMap and adding and manipulating layers worked fine for me.

The only requirements are a folder containing the Python for .NET library, and a standard Python install.

More details and sample script here. The sample script can also be seen directly at http://gist.github.com/923954

Unfortunately, whilst this works without problems on a local development machine, deploying it elsewhere requires the ArcObjects SDK, and Visual Studio (including the free Express edition) to be installed. See Deploying ArcObject .NET DLLs

4
6

One approach I don't see mentioned in the other answers is to use the same methods that the arcpy libraries themselves use. E.g. in C:\Program Files\ArcGIS\Desktop10.0\arcpy\arcpy\cartography.py, we see Python calling ArcObjects tools using some fixargs and object conversion functions.

I don't how much it's OK to post about it here, since the code says "TRADE SECRETS: ESRI PROPRIETARY AND CONFIDENTIAL"; but you'll find it elsewhere on the web too. Anyway, this looks like a relatively easy way to call functions like SimplifyBuilding_cartography() without installing comtypes or any other extra libraries.

Edit:

See Jason's comments below. Sounds like doing the above won't buy you much.

6
  • I noticed that too, seems like too much voodoo though.
    – blah238
    Jan 25, 2013 at 17:39
  • 1
    It's not calling a fully exposed set of arcobjects. Jan 25, 2013 at 17:53
  • @JasonScheirer, be that as it may, it does apparently allow some greater degree of access to ArcObjects (I think) than the straight arcpy API, and it has the advantage of not requiring installation of another library. The latter can be important if you're developing tools for other people to use. I would like to know the full extent of what you can get access to through this method -- it might be enough for certain purposes. (I can't check right now though - I'm not on the company LAN.)
    – LarsH
    Jan 25, 2013 at 19:24
  • 1
    I can assure you for a fact it does not. I developed much of it. Jan 25, 2013 at 20:14
  • 1
    You cannot. "ArcObject" is a bit of a misnomer in this instance. There is no way to get to the underlying objects, and there is no COM binding exposing all of ArcObjects anywhere in arcgisscripting/arcpy. Jan 25, 2013 at 22:00

Not the answer you're looking for? Browse other questions tagged or ask your own question.