Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

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?

share|improve this question
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. –  Chad Cooper Oct 28 '10 at 13:12

5 Answers 5

up vote 55 down vote accepted

Download and install comtypes, put the Snippets module from Mark Cederholm's presentation (link below) in PYTHONPATH, and you're all set.

from Snippets 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

Mark Cederholm's presentation for UberPyGeeks on accessing arcobjects from python. It uses Visual Studio (Express is ok) and the Windows SDK in addition to arcgis and python.

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

share|improve this answer
7  
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 '11 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? –  matt wilkie Apr 18 '11 at 17:15
1  
Yeah. Tested it at the lab. You just need to install the comtypes. –  R.K. Feb 5 '12 at 14:34
    
@R.K., great! Thanks for the verification and bringing the answer up to date. –  matt wilkie Feb 6 '12 at 17:09
1  
the Snippets module is also available here –  matt wilkie Feb 6 '12 at 17:23

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.

share|improve this answer
1  
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. ;-) –  matt wilkie Jan 10 '11 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 '11 at 23:05

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).

share|improve this answer
2  
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. –  Jason Scheirer Feb 3 '11 at 1:08

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

share|improve this answer
    
The recipe is clear, lucid and well presented. Thanks Geographika! I encourage you to include a minimal script snippet in your answer, so that in the event your site undergoes renovations the answer can stand on it's own. –  matt wilkie Jun 6 '11 at 22:55
1  
That's exactly what I did in this old blogpost (gissolved.blogspot.com/2009/06/python-toolbox-3-pythonnet.html) and it works as expected but make sure to return results that your Python script understands (strings, numbers or void). –  Samuel Dec 20 '11 at 9:06

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.

share|improve this answer
    
I noticed that too, seems like too much voodoo though. –  blah238 Jan 25 '13 at 17:39
1  
It's not calling a fully exposed set of arcobjects. –  Jason Scheirer Jan 25 '13 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 '13 at 19:24
    
I can assure you for a fact it does not. I developed much of it. –  Jason Scheirer Jan 25 '13 at 20:14
    
@Jason: Then I guess you should know. :-) After interpreting your statement a couple of different ways, I'm leaning toward the conclusion that your "does not" refers to "allow some greater degree of access to ArcObjects than the straight arcpy API." Can you confirm that I interpreted correctly? If so I will modify or delete my answer. Also I would like to know if I can use passthrough_attr() to get ArcObjects properties like IProjectedCoordinateSystem.GeographicCoordinateSystem? –  LarsH Jan 25 '13 at 20:53

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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