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?
-
3Anyone 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– Chad Cooper2010年10月28日 13:12:17 +00:00Commented Oct 28, 2010 at 13:12
5 Answers 5
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
- 9.3, 9.4 - http://pierssen.com/arcgis/upload/misc/python_arcobjects.zip
- 10.0 - http://www.pierssen.com/arcgis10/upload/python/snippets100.py
- 10.1 - http://www.pierssen.com/arcgis10/upload/python/snippets101.py
- 10.2 - http://www.pierssen.com/arcgis10/upload/python/snippets102.py
* 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:
-
12This 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– user27152011年04月18日 14:39:29 +00:00Commented 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?matt wilkie– matt wilkie2011年04月18日 17:15:33 +00:00Commented Apr 18, 2011 at 17:15
-
2Yeah. Tested it at the lab. You just need to install the comtypes.R.K.– R.K.2012年02月05日 14:34:50 +00:00Commented Feb 5, 2012 at 14:34
-
@R.K., great! Thanks for the verification and bringing the answer up to date.matt wilkie– matt wilkie2012年02月06日 17:09:48 +00:00Commented Feb 6, 2012 at 17:09
-
1Snippets 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.matt wilkie– matt wilkie2015年01月07日 23:51:59 +00:00Commented Jan 7, 2015 at 23:51
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.
-
2The 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– matt wilkie2011年01月10日 06:14:45 +00:00Commented Jan 10, 2011 at 6:14
-
1I 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– blah2382011年01月12日 23:05:39 +00:00Commented Jan 12, 2011 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:
- write some of your core logic in C++, the ones where you believe python performance might be an issue
- 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.)
- 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]
- 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).
-
4This 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– Jason Scheirer2011年02月03日 01:08:16 +00:00Commented Feb 3, 2011 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
-
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– matt wilkie2011年06月06日 22:55:15 +00:00Commented Jun 6, 2011 at 22:55
-
1That'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– Samuel2011年12月20日 09:06:09 +00:00Commented Dec 20, 2011 at 9:06
-
Link geographika.co.uk/using-arcobjects-and-net-in-python is broken.Learner– Learner2015年08月04日 06:12:40 +00:00Commented Aug 4, 2015 at 6:12
-
geographika.net/archive/using-arcobjects-and-net-in-python.htmlgeographika– geographika2021年02月25日 12:08:37 +00:00Commented Feb 25, 2021 at 12:08
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.
-
I noticed that too, seems like too much voodoo though.blah238– blah2382013年01月25日 17:39:38 +00:00Commented Jan 25, 2013 at 17:39
-
1It's not calling a fully exposed set of arcobjects.Jason Scheirer– Jason Scheirer2013年01月25日 17:53:49 +00:00Commented 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– LarsH2013年01月25日 19:24:45 +00:00Commented Jan 25, 2013 at 19:24
-
1I can assure you for a fact it does not. I developed much of it.Jason Scheirer– Jason Scheirer2013年01月25日 20:14:48 +00:00Commented Jan 25, 2013 at 20:14
-
1You 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.Jason Scheirer– Jason Scheirer2013年01月25日 22:00:17 +00:00Commented Jan 25, 2013 at 22:00