¿Verificando a través de ArcPy si ArcMap está en sesión de edición?

11

Creé un botón de complemento de Python que ayuda a acelerar el flujo de trabajo de mis compañeros de trabajo al copiar un atributo de clase de entidad en otro. Utiliza la función arcpy.UpdateCursor para actualizar una fila en la clase de entidad de destino. Tal como existe ahora, este script de botón se puede ejecutar independientemente del modo de edición. Obviamente, cuando se ejecuta en una sesión de edición, el usuario puede optar por detener la edición y no guardar los cambios, pero este no es el caso cuando el script se ejecuta fuera de una sesión de edición.

¿Cómo puedo agregar una comprobación al script que detendrá el script si ArcMap no está actualmente en una sesión de edición?

Esto concierne a ArcMap 10 y 10.1


También quiero consultar con otros usuarios de ArcMap para verificar que las actualizaciones de las tablas normalmente no están permitidas sin estar en una sesión de edición.

Entonces, ¿cómo se ejecuta este script fuera de una sesión de edición?

Esta secuencia de comandos también plantea otra pregunta sobre el orden de selección aparentemente fortuito que realiza ArcMap que simplemente funciona para mí cuando actualizo la segunda tabla de clase de entidad de una lista, pero eso es para otro día.

Aquí está el script tal como funciona ahora (sin ninguna implementación del editor 10.1):

¿Cómo agregar un cheque para asegurar que el usuario esté en una sesión de edición?

def onClick(self):
    #Reference mxd
    mxd = arcpy.mapping.MapDocument("CURRENT")
    #Reference the main Data frame
    mm = arcpy.mapping.ListDataFrames(mxd, "MainMap")[0]
    #Reference the Water System Valve feature class
    waterValves = arcpy.mapping.ListLayers(mxd, "Water System Valve", mm)[0]
    #Reference the fire hydrant feature class
    fireHydrants = arcpy.mapping.ListLayers(mxd, "Water Hydrant", mm)[0]

    #Use the extent of the main DF to select all valves in the current view
    dfAsFeature = arcpy.Polygon(arcpy.Array([mm.extent.lowerLeft, mm.extent.lowerRight, mm.extent.upperRight, mm.extent.upperLeft]), mm.spatialReference)
    arcpy.SelectLayerByLocation_management(waterValves, "WITHIN", dfAsFeature,"", "NEW_SELECTION")

    arcpy.SelectLayerByAttribute_management(waterValves, "SUBSET_SELECTION", "LOCATIONID IS NULL")

    fields = ["LOCATIONID"]

    row, rows = None, None
    rows = arcpy.UpdateCursor(waterValves,fields)
    row = rows.next()
    valveList = []
    append = valveList.append

    #Loop through the valves table to update LocationID
    while row:
        builder = str(row.QSNO)+"-"+ str(row.VALVESEQNO)
        row.setValue("LOCATIONID", builder)
        append(builder)
        rows.updateRow(row)
        row = rows.next()

    del row, rows

    #New selection for fire hydrants
    arcpy.SelectLayerByLocation_management(fireHydrants, "WITHIN", dfAsFeature,"", "NEW_SELECTION")
    arcpy.SelectLayerByAttribute_management(fireHydrants, "SUBSET_SELECTION", "LOCATIONID IS NULL")

    row, rows = None, None
    rows = arcpy.UpdateCursor(fireHydrants,fields)
    row = rows.next()

    #Loop through fire hydrant table to update LocationID
    while row:
        for locID in valveList:
            construct = str(locID) + "-FH"
            #print construct
            row.setValue("LOCATIONID", construct)
            rows.updateRow(row)
            row = rows.next()

    del row, rows, valveList, mxd
usuario18412
fuente
El editor del módulo de acceso a datos parece funcionar independientemente del editor estándar. Agradecería cualquier idea adicional sobre las pruebas para una sesión de edición activa. -Karl
KarlJr
¿Puedes darnos un poco más de información? ¿Qué te llevó a esta conclusión para aquellos de nosotros que no hemos explorado el módulo?
Jay Laura

Respuestas:

6

Aquí hay una función genérica basada en esta publicación.

Tal vez esto sea un poco más complejo que la solución ArcObjects, ¡pero seguro que parece mucho menos complicado! Simple es mejor que complejo. Excepto cuando no lo es.

Ejemplo de uso:

if CheckEditSession(tbl):
    print("An edit session is currently open.")

código:

def CheckEditSession(lyr):
    """Check for an active edit session on an fc or table.
    Return True of edit session active, else False"""
    edit_session = True
    row1 = None
    try:
        # attempt to open two cursors on the input
        # this generates a RuntimeError if no edit session is active
        OID = arcpy.Describe(lyr).OIDFieldName
        with arcpy.da.UpdateCursor(lyr, OID) as rows:
            row = next(rows)
            with arcpy.da.UpdateCursor(lyr, OID) as rows2:
                row2 = next(rows2)
    except RuntimeError as e:
        if e.message == "workspace already in transaction mode":
            # this error means that no edit session is active
            edit_session = False
        else:
            # we have some other error going on, report it
            raise
    return edit_session
Precio Curtis
fuente
+1 Buen concepto, sin embargo, el OP quiere detenerse si no está en una sesión de edición y continuar si está en una sesión de edición. Su respuesta parece hacer lo contrario. Sin embargo, posiblemente no tomaría mucho para cambiar eso.
Midavalo
El OP ya resolvió su problema, esta publicación es solo una función más útil en general. Modifiqué mi ejemplo para ser más claro en cuanto a cómo se usa la función.
Curtis Precio
4

Mi solución a este problema fue usar las extensiones disponibles para la barra de herramientas Arcpy Addin. Agregué una extensión que escucha una sesión de edición para comenzar o finalizar. Tengo todos mis botones en la barra configurados en: self.enable = False "para comenzar y luego estos botones se habilitan o deshabilitan al iniciar o detener una sesión de edición.

class Active_Edit_Session(object):
"""Implementation for NEZ_EDITS_addin.Listen_for_Edit_Session (Extension)"""
def __init__(self):
    self.enabled = True
def onStartEditing(self):
    button_3100.enabled=True    
def onStopEditing(self, save_changes):
    button_3100.enabled=False

class LFM_3100(object):
    """Implementation for LFM_3100.button_3100 (Button)"""
    def __init__(self):
        self.enabled = False
        self.checked = False
    def onClick(self):
        ......
F_Kellner
fuente
Parece una solución que vale la pena probar. Gracias
user18412
4

Estoy publicando otra respuesta porque aprendí un nuevo método para verificar el estado del Editor en ArcMap usando ArcObjects y Python juntos. Mi respuesta se basa en gran medida en el trabajo realizado por Mark Cederholm como se menciona en esta publicación: ¿Cómo accedo a ArcObjects desde Python? y ejemplos de código proporcionados por Matt Wilkie en su archivo "Snippits.py". Deberá seguir las instrucciones proporcionadas en la primera respuesta para descargar e instalar comtypes y luego obtener una copia del script Snippets.py. Estoy publicando una copia de las funciones esenciales de ese script a continuación.

Cuando se llama a la función ArcMap_GetEditSessionStatus (), verificará el estado actual del Editor en ArcMap y devolverá verdadero o falso. Esto me permite verificar si un usuario está preparado para usar mi herramienta o si necesita que se le solicite que inicie una sesión de edición. La desventaja de este método es el requisito de instalar comtypes antes de que ArcObjects pueda usarse en Python, por lo que podría no ser posible compartir una herramienta que requiera este paquete en un entorno de oficina multiusuario. Con mi experiencia limitada, no estoy seguro de cómo agruparlo todo para compartirlo fácilmente como un complemento de la herramienta Esri Python. Se agradecerán sugerencias sobre cómo hacer esto.

#From the Snippits.py file created by Matt Wilkie
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

def CType(obj, interface):
    """Casts obj to interface and returns comtypes POINTER or None"""
    try:
        newobj = obj.QueryInterface(interface)
        return newobj
    except:
        return None

def CLSID(MyClass):
    """Return CLSID of MyClass as string"""
    return str(MyClass._reg_clsid_)

def GetApp(app="ArcMap"):
    """app must be 'ArcMap' (default) or 'ArcCatalog'\n\
    Execute GetDesktopModules() first"""
    if not (app == "ArcMap" or app == "ArcCatalog"):
        print "app must be 'ArcMap' or 'ArcCatalog'"
        return None
    import comtypes.gen.esriFramework as esriFramework
    import comtypes.gen.esriArcMapUI as esriArcMapUI
    import comtypes.gen.esriCatalogUI as esriCatalogUI
    pAppROT = NewObj(esriFramework.AppROT, esriFramework.IAppROT)
    iCount = pAppROT.Count
    if iCount == 0:
        return None
    for i in range(iCount):
        pApp = pAppROT.Item(i)
        if app == "ArcCatalog":
            if CType(pApp, esriCatalogUI.IGxApplication):
                return pApp
            continue
        if CType(pApp, esriArcMapUI.IMxApplication):
            return pApp
    return None


def GetModule(sModuleName):
    """Import ArcGIS module"""
    from comtypes.client import GetModule
    sLibPath = GetLibPath()
    GetModule(sLibPath + sModuleName)


def GetDesktopModules():
    """Import basic ArcGIS Desktop libraries"""
    GetModule("esriFramework.olb")
    GetModule("esriArcMapUI.olb")

#My added function for checking edit session status
def ArcMap_GetEditSessionStatus():

    GetDesktopModules()
    GetModule("esriEditor.olb")
    import comtypes.gen.esriSystem as esriSystem
    import comtypes.gen.esriEditor as esriEditor
    pApp = GetApp()
    pID = NewObj(esriSystem.UID, esriSystem.IUID)
    pID.Value = CLSID(esriEditor.Editor)
    pExt = pApp.FindExtensionByCLSID(pID)
    pEditor = CType(pExt, esriEditor.IEditor)
    if pEditor.EditState == esriEditor.esriStateEditing:
        print "Edit session active"
        return True
    else:
        print "Not in an edit session"
        return False
usuario18412
fuente
1
Esto funciona muy bien. Sé que esta es una publicación antigua, pero si desea empaquetar esto para que sea más portátil, puede hacer que el módulo de fragmentos sea un paquete de Python e incluir comtypes dentro de él. Hago esto para mi empresa y he colocado todos nuestros módulos personalizados de Python en un recurso compartido de red. Cada vez que alguien instala / reinstala el software ArcGIS, les pido que ejecuten un archivo por lotes que modifica su Desktop.ptharchivo para incluir la ruta completa al recurso compartido de red, para que todos puedan importar automáticamente todo.
crmackey
2

¿Qué tal usar el módulo de acceso a datos ? Parece que puede comenzar una sesión de edición con este módulo.

Algunas advertencias:

  1. No he probado este módulo y no estoy seguro de si es compatible con 10.0. (¿Nuevo en 10.1?)
  2. El ejemplo 1 muestra el uso de una withdeclaración. Este es un gran paradigma para implementar, ya que maneja bien las posibles excepciones.
  3. Es posible que pueda probar si una sesión de edición ya está activa al intentar iniciar una en una try / exceptdeclaración.
Jay Laura
fuente
De hecho, comencé a usar la clase Editor en el módulo de acceso a datos cuando comencé este proyecto, pero usarlo no parecía importar. Incluyendo "con arcpy.da.Editor (espacio de trabajo) como edit:" en mi script no activó el editor, e intentando stopOperation / stop.Editing no detuvo el editor. Pero podría estar haciéndolo mal ...
user18412
1

Así es como solucioné mi problema de no poder controlar si alguien que usaba mi herramienta estaba en una sesión de edición o no:

#Reference to mxd and layers script here. Then...
try:
    fields = ("OBJECTID")
    upCursor = arcpy.da.UpdateCursor(waterValves, fields)
    with upCursor as cursor:
        for row in cursor:
            pass
except:
    pythonaddins.MessageBox('You are not in an edit session', 'Warning', 0)

else:
#Rest of script

El script funciona porque intenta crear un UpdateCursor en una capa que tiene otro UpdateCursor más adelante en el script. Esto viola el comportamiento del módulo de acceso a datos. De acuerdo con la página de recursos de ESRI en arcpy.da.UpdateCursor:

"Abrir operaciones simultáneas de inserción y / o actualización en el mismo espacio de trabajo utilizando diferentes cursores requiere el inicio de una sesión de edición".

No estoy contento con esta solución porque es más un truco de lo que imagino es un script de arcpy adecuado. ¿Mejores ideas para alguien?

usuario18412
fuente
1
Esto es solo una idea, pero podría intentar acceder al objeto Editor en ArcObjects y verificar su propiedad EditState, que parece ser lo que falta en arcpy. Nunca he tratado de manipular ArcObjects desde Python, pero este hilo habla sobre cómo hacerlo.
Hornbydd