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?
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.
* Note for 10.1+ you need to make a small change to
automation.py in the comtypes module.
See ArcObjects + comtypes at 10.1.
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).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.
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:
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).
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.
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
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.
See Jason's comments below. Sounds like doing the above won't buy you much.