Über ArcPy prüfen, ob sich ArcMap in einer Bearbeitungssitzung befindet?

11

Ich habe eine Python-Add-In-Schaltfläche erstellt, die den Workflow meiner Mitarbeiter beschleunigt, indem ein Feature-Class-Attribut in ein anderes kopiert wird. Es verwendet die Funktion arcpy.UpdateCursor, um eine Zeile in der Ziel-Feature-Class zu aktualisieren. Wie es jetzt existiert, kann dieses Schaltflächenskript unabhängig vom Bearbeitungsmodus ausgeführt werden. Natürlich kann der Benutzer beim Ausführen in einer Bearbeitungssitzung die Bearbeitung beenden und keine Änderungen speichern. Dies ist jedoch nicht der Fall, wenn das Skript außerhalb einer Bearbeitungssitzung ausgeführt wird.

Wie kann ich dem Skript eine Prüfung hinzufügen, die die Ausführung des Skripts verhindert, wenn sich ArcMap derzeit nicht in einer Bearbeitungssitzung befindet?

Dies betrifft ArcMap 10 und 10.1


Ich möchte auch mit anderen ArcMap-Benutzern überprüfen, ob Aktualisierungen von Tabellen normalerweise nicht zulässig sind, ohne sich in einer Bearbeitungssitzung zu befinden.

Wie läuft dieses Skript außerhalb einer Bearbeitungssitzung?

Dieses Skript wirft auch eine andere Frage zu der scheinbar zufälligen Auswahlreihenfolge auf, die ArcMap ausführt und die zufällig für mich funktioniert, wenn ich die Tabelle der zweiten Feature-Class aus einer Liste aktualisiere, aber das ist für einen anderen Tag.

Hier ist das Skript, wie es jetzt funktioniert (ohne Implementierung eines 10.1-Editors):

Wie füge ich eine Prüfung hinzu, um sicherzustellen, dass sich der Benutzer in einer Bearbeitungssitzung befindet?

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
quelle
Der Datenzugriffsmodul-Editor scheint unabhängig vom Standard-Editor zu arbeiten. Ich würde zusätzliche Ideen zum Testen für eine aktive Bearbeitungssitzung begrüßen. -Karl
KarlJr
Können Sie uns etwas mehr Informationen geben? Was hat Sie zu dieser Schlussfolgerung für diejenigen von uns geführt, die das Modul nicht untersucht haben?
Jay Laura

Antworten:

6

Hier ist eine generische Funktion, die auf diesem Beitrag basiert.

Vielleicht ist dies etwas klobiger als die ArcObjects-Lösung, aber es scheint sicher viel weniger stressig zu sein! Einfach ist besser als komplex. Außer wenn es nicht ist.

Anwendungsbeispiel:

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
Curtis Price
quelle
+1 Nettes Konzept, aber das OP möchte anhalten, wenn es sich nicht in einer Bearbeitungssitzung befindet, und fortfahren, wenn es sich in einer Bearbeitungssitzung befindet. Ihre Antwort scheint das Gegenteil zu bewirken. Möglicherweise würde es nicht viel dauern, um das zu ändern.
Midavalo
Das OP hat sein Problem bereits gelöst, dieser Beitrag ist nur ein Sahnehäubchen mit einer allgemein nützlichen Funktion. Ich habe mein Beispiel geändert, um klarer zu machen, wie die Funktion verwendet wird.
Curtis Price
4

Meine Lösung für dieses Problem bestand darin, die für die Arcpy Addin-Symbolleiste verfügbaren Erweiterungen zu verwenden. Ich habe eine Erweiterung hinzugefügt, die darauf wartet, dass eine Bearbeitungssitzung beginnt oder endet. Ich habe alle meine Schaltflächen in der Leiste auf "self.enable = False" gesetzt und diese Schaltflächen werden dann entweder aktiviert oder deaktiviert, indem eine Bearbeitungssitzung gestartet oder gestoppt wird.

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
quelle
Dies scheint eine Lösung zu sein, die es wert ist, ausprobiert zu werden. Vielen Dank
user18412
4

Ich poste eine weitere Antwort, weil ich eine neue Methode zum Überprüfen des Status des Editors in ArcMap mithilfe von ArcObjects und Python zusammen gelernt habe. Meine Antwort basiert stark auf der Arbeit von Mark Cederholm, auf die in diesem Beitrag verwiesen wird: Wie greife ich von Python aus auf ArcObjects zu? und Codebeispiele von Matt Wilkie in seiner Datei "Snippits.py". Sie müssen den Anweisungen in der ersten Antwort folgen, um comtypes herunterzuladen und zu installieren und dann eine Kopie des Skripts Snippets.py zu erhalten. Ich poste eine Kopie der wesentlichen Funktionen aus diesem Skript unten.

Wenn die Funktion ArcMap_GetEditSessionStatus () aufgerufen wird, überprüft sie den aktuellen Status des Editors in ArcMap und gibt true oder false zurück. Auf diese Weise kann ich überprüfen, ob ein Benutzer bereit ist, mein Tool zu verwenden, oder ob er aufgefordert werden muss, eine Bearbeitungssitzung zu starten. Der Nachteil dieser Methode ist die Anforderung, comtypes zu installieren, bevor ArcObjects in Python verwendet werden kann. Daher ist die Freigabe eines Tools, für das dieses Paket erforderlich ist, in einer Büroumgebung für mehrere Benutzer möglicherweise nicht möglich. Aufgrund meiner begrenzten Erfahrung bin ich mir nicht sicher, wie ich alles zusammenfassen soll, um es einfach als Esri Python-Tool-Add-In zu teilen. Vorschläge, wie dies zu tun ist, wären willkommen.

#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
quelle
1
Das funktioniert super. Ich weiß, dass dies ein alter Beitrag ist, aber wenn Sie dies so verpacken möchten, dass es portabler ist, können Sie das Snippets-Modul als Python-Paket erstellen und darin Comtypes einfügen. Ich mache das für mein Unternehmen und habe alle unsere benutzerdefinierten Python-Module auf einer Netzwerkfreigabe platziert. Jedes Mal, wenn jemand die ArcGIS-Software installiert / neu installiert, muss er eine Batchdatei ausführen, die seine Desktop.pthDatei so ändert , dass sie den vollständigen Pfad zur Netzwerkfreigabe enthält, sodass jeder automatisch alles importieren kann.
Crmackey
2

Wie wäre es mit dem Datenzugriffsmodul ? Es sieht so aus, als könnten Sie mit diesem Modul eine Bearbeitungssitzung starten.

Ein paar Einschränkungen:

  1. Ich habe dieses Modul nicht ausprobiert und bin mir nicht sicher, ob es 10.0-kompatibel ist. (Neu in 10.1?)
  2. Beispiel 1 zeigt die Verwendung einer withAnweisung. Dies ist ein großartiges Paradigma, da es potenzielle Ausnahmen gut handhabt.
  3. Möglicherweise können Sie testen, ob eine Bearbeitungssitzung bereits aktiv ist, indem Sie versuchen, eine in einer try / exceptAnweisung zu starten .
Jay Laura
quelle
Ich habe tatsächlich mit der Verwendung der Editor-Klasse im Datenzugriffsmodul begonnen, als ich dieses Projekt gestartet habe, aber die Verwendung schien keine Rolle zu spielen. Das Einfügen von "mit arcpy.da.Editor (Arbeitsbereich) als Bearbeitung:" in mein Skript hat den Editor nicht aktiviert, und der Versuch stopOperation / stop.Editing hat den Editor nicht gestoppt. Aber ich könnte es falsch machen ...
user18412
1

Auf diese Weise habe ich mein Problem behoben, dass ich nicht steuern konnte, ob sich jemand, der mein Tool verwendet, in einer Bearbeitungssitzung befand oder nicht:

#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

Das Skript funktioniert, weil es versucht, einen UpdateCursor auf einer Ebene zu erstellen, die später im Skript einen anderen UpdateCursor enthält. Dies verletzt das Verhalten des Datenzugriffsmoduls. Laut der ESRI-Ressourcenseite auf arcpy.da.UpdateCursor:

"Das Öffnen von gleichzeitigen Einfüge- und / oder Aktualisierungsvorgängen im selben Arbeitsbereich mit unterschiedlichen Cursorn erfordert den Start einer Bearbeitungssitzung."

Ich bin mit dieser Lösung nicht zufrieden, weil sie eher ein Hack ist als das, was ich mir als richtiges Arcpy-Scripting vorstelle. Bessere Ideen jemand?

user18412
quelle
1
Dies ist nur eine Idee, aber Sie können versuchen, auf das Editor-Objekt in ArcObjects zuzugreifen und dessen Eigenschaft EditState zu überprüfen, die anscheinend in arcpy fehlt. Ich habe noch nie versucht, ArcObjects von Python aus zu manipulieren, aber in diesem Thread wird erläutert, wie es geht.
Hornbydd