Vérifier via ArcPy si ArcMap est en session d'édition?

11

J'ai créé un bouton de complément Python qui permet d'accélérer le flux de travail de mes collègues en copiant un attribut de classe d'entités dans un autre. Il utilise la fonction arcpy.UpdateCursor pour mettre à jour une ligne dans la classe d'entités cible. Tel qu'il existe maintenant, ce script de bouton peut être exécuté quel que soit le mode d'édition. Évidemment, lors de son exécution dans une session d'édition, l'utilisateur peut choisir d'arrêter l'édition et de ne pas enregistrer les modifications, mais ce n'est pas le cas lorsque le script s'exécute en dehors d'une session d'édition.

Comment puis-je ajouter une vérification au script qui arrêtera l'exécution du script si ArcMap n'est pas actuellement dans une session de modification?

Cela concerne ArcMap 10 et 10.1


Je souhaite également vérifier auprès d'autres utilisateurs d'ArcMap pour vérifier que les mises à jour des tables ne sont normalement pas autorisées sans être dans une session de modification.

Alors, comment ce script s'exécute-t-il en dehors d'une session d'édition?

Ce script soulève également une autre question sur l'ordre de sélection apparemment fortuit qu'ArcMap exécute et qui se produit pour moi lorsque je mets à jour la deuxième table de classe d'entités à partir d'une liste, mais c'est pour un autre jour.

Voici le script tel qu'il fonctionne maintenant (sans implémentation de l'éditeur 10.1):

Comment ajouter une vérification pour s'assurer que l'utilisateur est dans une session d'édition?

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
user18412
la source
L'éditeur du module d'accès aux données semble fonctionner indépendamment de l'éditeur standard. Je serais ravi de toute idée supplémentaire sur les tests pour une session d'édition active. -Karl
KarlJr
Pouvez-vous fournir un peu plus d'informations? Qu'est-ce qui vous a amené à cette conclusion pour ceux d'entre nous qui n'ont pas exploré le module?
Jay Laura

Réponses:

6

Voici une fonction générique basée sur ce post.

C'est peut-être un peu plus compliqué que la solution ArcObjects, mais cela semble certainement beaucoup moins compliqué! Simple, c'est mieux que complexe. Sauf quand ce n'est pas le cas.

Exemple d'utilisation:

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

code:

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
Prix ​​Curtis
la source
+1 Beau concept, cependant l'OP veut s'arrêter s'il n'est pas dans une session d'édition, et continuer s'il est dans une session d'édition. Votre réponse semble faire le contraire. Cela ne prendrait peut-être pas grand-chose pour changer cela.
Midavalo
L'OP a déjà résolu son problème, ce poste n'est que de la cerise avec une fonction plus généralement utile. J'ai modifié mon exemple pour être plus clair sur la façon dont la fonction est utilisée.
Curtis Price
4

Ma solution à ce problème a été d'utiliser les extensions disponibles pour la barre d'outils Arcpy Addin. J'ai ajouté une extension qui écoute le début ou la fin d'une session d'édition. J'ai tous mes boutons sur la barre définis sur: self.enable = False "pour commencer, puis ces boutons sont ensuite activés ou désactivés en démarrant ou en arrêtant une session d'édition.

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
la source
Cela ressemble à une solution qui mérite d'être essayée. Merci
user18412
4

Je poste une autre réponse, car j'ai appris une nouvelle méthode pour vérifier l'état de l'éditeur dans ArcMap en utilisant ArcObjects et Python ensemble. Ma réponse s'inspire fortement du travail effectué par Mark Cederholm comme référencé dans cet article: Comment accéder à ArcObjects à partir de Python? et des exemples de code fournis par Matt Wilkie dans son fichier "Snippits.py". Vous devrez suivre les instructions fournies dans la première réponse pour télécharger et installer les comtypes, puis obtenir une copie du script Snippets.py. Je poste une copie des fonctions essentielles de ce script ci-dessous.

Lorsque la fonction ArcMap_GetEditSessionStatus () est appelée, elle vérifie l'état actuel de l'éditeur dans ArcMap et renvoie vrai ou faux. Cela me permet de vérifier si un utilisateur est prêt à utiliser mon outil ou s'il doit être invité à démarrer une session d'édition. L'inconvénient de cette méthode est la nécessité d'installer des comtypes avant que ArcObjects puisse être utilisé en Python, donc le partage d'un outil qui nécessite ce package dans un environnement de bureau multi-utilisateurs peut ne pas être possible. Avec mon expérience limitée, je ne sais pas comment tout regrouper pour un partage facile en tant que complément d'outil Esri Python. Des suggestions sur la façon de procéder seraient appréciées.

#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
user18412
la source
1
Cela fonctionne très bien. Je sais que c'est un vieux post, mais si vous voulez le compiler pour qu'il soit plus portable, vous pouvez créer le module d'extraits de code en tant que package python et y inclure des comtypes. Je le fais pour mon entreprise et j'ai placé tous nos modules Python personnalisés sur un partage réseau. Chaque fois que quelqu'un installe / réinstalle le logiciel ArcGIS, je lui demande d'exécuter un fichier de commandes qui modifie son Desktop.pthfichier pour inclure le chemin d'accès complet au partage réseau, afin que tout le monde puisse tout importer automatiquement.
crmackey
2

Que diriez-vous d'utiliser le module d'accès aux données ? Il semble que vous puissiez démarrer une session d'édition avec ce module.

Quelques mises en garde:

  1. Je n'ai pas essayé ce module et je ne suis pas sûr qu'il soit compatible 10.0. (Nouveau dans 10.1?)
  2. L'exemple 1 montre l'utilisation d'une withinstruction. C'est un excellent paradigme à mettre en œuvre car il gère bien les exceptions potentielles.
  3. Vous pourrez peut-être tester si une session d'édition est déjà en direct en essayant d'en lancer une dans une try / exceptinstruction.
Jay Laura
la source
En fait, j'ai commencé à utiliser la classe Editor dans le module d'accès aux données lorsque j'ai commencé ce projet, mais son utilisation ne semblait pas avoir d'importance. Inclure "avec arcpy.da.Editor (espace de travail) en tant que edit:" dans mon script n'a pas activé l'éditeur, et essayer stopOperation / stop.Editing n'a pas arrêté l'éditeur. Mais je peux me tromper ...
user18412
1

Voici donc comment j'ai résolu mon problème de ne pas pouvoir contrôler si quelqu'un utilisant mon outil était ou non en session d'édition:

#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

Le script fonctionne car il essaie de créer un UpdateCursor sur une couche qui a un autre UpdateCursor plus tard dans le script. Cela viole le comportement du module d'accès aux données. Selon la page des ressources ESRI sur arcpy.da.UpdateCursor:

"L'ouverture d'opérations d'insertion et / ou de mise à jour simultanées sur le même espace de travail à l'aide de curseurs différents nécessite le démarrage d'une session d'édition."

Je ne suis pas satisfait de cette solution car c'est plus un hack que ce que j'imagine être un bon script arcpy. De meilleures idées n'importe qui?

user18412
la source
1
Ce n'est qu'une idée, mais vous pouvez essayer d'accéder à l'objet Editor dans ArcObjects et vérifier sa propriété EditState qui semble être ce qui manque à arcpy? Je n'ai jamais essayé de manipuler ArcObjects à partir de python, mais ce fil explique comment le faire?
Hornbydd