¿Accediendo a ArcObjects desde Python?

132

Me gustaría poder escribir algunas cosas que no están expuestas a través de arcgisscriptingArcPy.

¿Cómo accedo a ArcObjects desde Python?

wilkie mate
fuente
3
¿Alguien tiene pensamientos sobre el uso de estos métodos en entornos de producción? Hace unos años, en la UC, hablé con uno de los desarrolladores de ESRI C ++ que escribe la mayoría de sus módulos de Python y estaba en contra del uso de IronPython en un entorno de producción, pero eso fue hace unos años.
Chad Cooper

Respuestas:

113

Descargue e instale comtypes *, coloque el Snippetsmódulo de Mark Cederholm en PYTHONPATH y listo.

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

Para obtener más información, consulte las presentaciones de Mark Cederholm para UberPyGeeks sobre "Uso de ArcObjects en Python" . Hay otros para las perspectivas de desarrollador de VBA y C ++. Usan Visual Studio (sí, Express está bien) y el SDK de Windows , pero no son necesarios, solo ArcGIS, Python y los tipos son suficientes.

Obteniendo el módulo Snippets

* Nota para 10.1+ que necesita hacer un pequeño cambio automation.pyen el módulo de comtypes. Ver ArcObjects + comtypes en 10.1 .


Próximos pasos

... o: ¿el cerebro se ha vuelto loco ? Mirar los ejemplos del código C # hace que sus ojos naden, e intente como no puede pensar como una grúa . Mira aquí:

wilkie mate
fuente
10
Este hilo me ha llamado la atención, y parece que hay algunas ideas falsas flotando. NO necesita Visual Studio ni el compilador MIDL para usar ArcObjects en Python; todo lo que necesitas es el paquete de tipos. Mi presentación incluyó un ejercicio avanzado para crear un componente COM usando Python, pero eso fue para UberPyGeeks. El archivo snippets.py contiene todos los ejemplos que necesita.
1
@ Mark, muchas gracias por la corrección. Para ser claros: en la receta anterior, ¿se podrían eliminar los pasos 1,3,4 siempre que ctypes ya esté instalado?
matt wilkie
2
Sí. Probado en el laboratorio. Solo necesita instalar los tipos.
RK
@RK, genial! Gracias por la verificación y por actualizar la respuesta.
Matt Wilkie
1
Módulo de fragmentos de refundición como instalable ao módulo aquí: github.com/maphew/arcplus/tree/master/arcplus/ao (actualizado el 10,3, ver las versiones anteriores del archivo para 10.2). Actualizará la respuesta principal una vez que elimine algunos errores.
Matt Wilkie
32

Sí, la presentación de Mark Cederholm que Matt Wilkie menciona arriba es un gran lugar para comenzar. La receta / código que presenta Matt es sin duda una buena forma y probablemente la mejor manera de hacer las cosas. Sin embargo, quería mencionar el método de fuerza bruta que estoy usando en ArcGIS 10.0. Tengo varios scripts de automatización (independientes, fuera del límite de la aplicación) que ejecuto de esta manera, y funcionan bien. Sin embargo, si la velocidad máxima es una preocupación, puede optar por la solución de Matt y terminar con ella.

Utilizo el paquete comtypes para forzar el ajuste de todas las bibliotecas de ArcObjects (.olb). Entonces Python tiene acceso a todos los ArcObjects. Recibí el código de Frank Perks a través de una publicación en el foro de ESRI . Tenía mi propio código que esencialmente hacía lo mismo, pero estaba hinchado y meramente funcional, mientras que el suyo es mucho más bonito. Entonces:

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)

Luego, casi directamente de la presentación de Mark Cederholm:

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

Eso es. Debe tener acceso completo a ArcObjects comenzando con el objeto pApp que es IApplication en el objeto AppRef. En mi experiencia, el ajuste de las bibliotecas de ArcObjects en la primera ejecución no es objetivamente lento, y para las ejecuciones posteriores, el ajuste no ocurre. Las bibliotecas ya están envueltas y compiladas, por lo que las cosas son mucho más rápidas.

Agregado: hay una gran precaución que viene con esto. La función NewObj dada aquí asume que el script Python se está ejecutando en proceso. De lo contrario, esta función creará objetos en el proceso de Python (es decir, fuera del proceso), y las referencias a objetos serán incorrectas. Para crear objetos en proceso a partir de un script externo de Python, debe usar IObjectFactory. Vea los comentarios y consejos de Kirk Kuykendall en esta publicación de intercambio de pila para obtener más información.

celta
fuente
1
Lo bueno de este enfoque es que no requiere la instalación de Visual Studio, que es bastante pesado si lo único que harás es registrar los objetos com. Si hubiera sabido sobre este método de pitón puro, probablemente nunca habría probado la ruta de Cederholm. ;-)
matt wilkie
1
Expandí
blah238
20

¿Cómo accedo a los arcobjects desde Python?

Si lo que está buscando es una funcionalidad específica que existe y está en el código de C ++ Arcobjects, entonces su mejor opción sería crear métodos C ++ para llamarlos ... y luego crear un contenedor de Python para acceder a esos métodos C ++.

Hay bastantes maneras de acceder a los métodos C ++ desde Python, y muchas personas que lo hacen usan una herramienta como SWIG para generar automáticamente las clases de Python a partir de las firmas del método C ++. Según mi experiencia, estas API autogeneradas se vuelven bastante desagradables al pasar tipos C ++ no nativos (int, flotantes) y nunca son muy ' pitónicos '.

Mi solución recomendada sería utilizar la API de ctypes. Un gran tutorial está aquí: http://python.net/crew/theller/ctypes/tutorial.html

Los pasos básicos son:

  1. escriba parte de su lógica central en C ++, aquellas en las que cree que el rendimiento de Python podría ser un problema
  2. Compile esa lógica central (en este caso utilizando las llamadas al método API ArcObject C ++) de los archivos de objetos en una biblioteca compartida (.so) o dinámica (.dll) usando cualquier compilador del sistema (nmake, make, etc.)
  3. Escriba un mapeo de ctypes entre la clase python y la firma del método C ++ [SWIG intenta automatizar esto, pero es fácil, incluso si usa tipos de objetos locos]
  4. ¡Importe la biblioteca compilada a su programa de python y use los enlaces de clase / método enlazados como cualquier otra clase de python!

Esta es probablemente la forma más general de hacer referencia al código C / C ++ desde Python, probablemente será mucho más simple a largo plazo si no tiene que lidiar con objetos COM. También permitirá que toda la funcionalidad específica del sistema resida en la compilación del objeto de la biblioteca vinculada (por lo que el Python no será específico de la implementación del sistema / Python).

tmarthal
fuente
3
Este es, de lejos, el mejor método para interactuar con ArcObjects. El uso de comtypes es excelente para la creación de prototipos, pero escribir su propia extensión C dará como resultado un buen diseño Pythonic (porque tiene que pensarlo) y un mejor rendimiento porque no cruza tanto la barrera de objetos C ++ / Python. Aunque para fines prácticos, esto es más difícil de diseñar / desarrollar / depurar, por lo que lo rápido y sucio se va por la ventana.
Jason Scheirer
18

Otra opción es usar Python para .NET : es muy fácil de configurar y puede funcionar con cualquier DLL de .NET, incluidos ArcObjects.

No he experimentado ningún problema con los obedecimientos en proceso, y abrir una instancia de ArcMap y agregar y manipular capas funcionó bien para mí.

Los únicos requisitos son una carpeta que contenga la biblioteca Python para .NET y una instalación estándar de Python.

Más detalles y script de muestra aquí . El script de muestra también se puede ver directamente en http://gist.github.com/923954

Desafortunadamente, si bien esto funciona sin problemas en una máquina de desarrollo local, la implementación en otro lugar requiere la instalación de ArcObjects SDK y Visual Studio (incluida la edición Express gratuita). Consulte Implementación de archivos DLL de ArcObject .NET

geographika
fuente
La receta es clara, lúcida y bien presentada. Gracias Geographika! Le recomiendo que incluya un fragmento de script mínimo en su respuesta, de modo que en caso de que su sitio sufra renovaciones, la respuesta pueda ser independiente.
Matt Wilkie
1
Eso es exactamente lo que hice en este viejo blog ( gissolved.blogspot.com/2009/06/python-toolbox-3-pythonnet.html ) y funciona como se esperaba, pero asegúrese de devolver los resultados que su script Python entiende (cadenas, números o nulo)
Samuel
5

Un enfoque que no veo mencionado en las otras respuestas es usar los mismos métodos que usan las bibliotecas arcpy. Por ejemplo, en C: \ Archivos de programa \ ArcGIS \ Desktop10.0 \ arcpy \ arcpy \ cartography.py, vemos a Python llamando a las herramientas de ArcObjects usando algunos fixargs y funciones de conversión de objetos.

No sé cuánto está bien publicar al respecto aquí, ya que el código dice "SECRETOS COMERCIALES: ESRI PROPIETARIO Y CONFIDENCIAL"; pero también lo encontrarás en otra parte de la web. De todos modos, esto parece una forma relativamente fácil de llamar a funciones como SimplifyBuilding_cartography()sin instalar comtypes o cualquier otra biblioteca adicional.

Editar:

Vea los comentarios de Jason a continuación. Parece que hacer lo anterior no te comprará mucho.

LarsH
fuente
También me di cuenta de que parece demasiado vudú.
blah238
1
No está llamando a un conjunto completamente expuesto de arcobjects.
Jason Scheirer el
@JasonScheirer, sea como sea, aparentemente permite un mayor grado de acceso a ArcObjects (creo) que la API de arcpy directo, y tiene la ventaja de no requerir la instalación de otra biblioteca. Esto último puede ser importante si está desarrollando herramientas para que otras personas las usen. Me gustaría saber en qué medida puede acceder a través de este método; puede ser suficiente para ciertos fines. (Sin embargo, no puedo comprobarlo ahora, no estoy en la LAN de la empresa)
LarsH
1
Les puedo asegurar que no es así. Desarrollé mucho de eso.
Jason Scheirer el
1
No se puede. "ArcObject" es un nombre poco apropiado en este caso. No hay forma de llegar a los objetos subyacentes, y no hay enlace COM que exponga todos los ArcObjects en cualquier parte de arcgisscripting / arcpy.
Jason Scheirer el