Sprawdzanie za pomocą ArcPy, czy ArcMap jest w sesji edycji?

11

Utworzyłem przycisk dodatku Python, który pomaga przyspieszyć przepływ pracy moich współpracowników, kopiując jeden atrybut klasy obiektów do drugiego. Używa funkcji arcpy.UpdateCursor do aktualizacji wiersza w docelowej klasie obiektów. Jak już istnieje, ten skrypt przycisku można uruchomić niezależnie od trybu edycji. Oczywiście, gdy jest uruchamiany w sesji edycji, użytkownik może zatrzymać edycję i nie zapisywać zmian, ale nie dzieje się tak, gdy skrypt działa poza sesją edycji.

Jak mogę dodać skrypt do skryptu, który zatrzyma działanie skryptu, jeśli ArcMap nie jest aktualnie w sesji edycji?

Dotyczy to ArcMap 10 i 10.1


Chcę również skontaktować się z innymi użytkownikami ArcMap, aby sprawdzić, czy aktualizacje tabel nie są normalnie dozwolone bez udziału w sesji edycji.

Jak ten skrypt działa poza sesją edycji?

Ten skrypt wywołuje również inne pytanie dotyczące pozornie nieoczekiwanej kolejności selekcji, którą wykonuje ArcMap, która akurat działa dla mnie, gdy aktualizuję tabelę 2. klasy obiektów z listy, ale to na inny dzień.

Oto skrypt, który działa teraz (bez żadnej implementacji edytora 10.1):

Jak dodać czek, aby upewnić się, że użytkownik jest w sesji edycji?

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
użytkownik18412
źródło
Wydaje się, że edytor modułu dostępu do danych działa niezależnie od standardowego edytora. Z zadowoleniem przyjmuję wszelkie dodatkowe pomysły dotyczące testowania w ramach aktywnej sesji edycji. -Karl
KarlJr
Czy możesz podać trochę więcej informacji? Co doprowadziło cię do tego wniosku dla tych z nas, którzy nie badali modułu?
Jay Laura

Odpowiedzi:

6

Oto ogólna funkcja oparta na tym poście.

Być może jest to nieco bardziej nieprzyzwoite niż rozwiązanie ArcObjects, ale z pewnością wydaje się, że jest o wiele mniej kłopotów! Prosty jest lepszy niż złożony. Z wyjątkiem sytuacji, gdy tak nie jest.

Przykładowe użycie:

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

kod:

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
Cena Curtis
źródło
+1 Dobra koncepcja, jednak OP chce się zatrzymać, jeśli nie w sesji edycji, i kontynuować, jeśli jest w sesji edycji. Twoja odpowiedź wydaje się odwrotna. Być może jednak niewiele by to zmieniło.
Midavalo
OP już rozwiązał swój problem, ten post jest tylko lukrem z bardziej ogólnie użyteczną funkcją. Zmodyfikowałem mój przykład, aby był bardziej zrozumiały, jak ta funkcja jest używana.
Curtis Price
4

Moim rozwiązaniem tego problemu było użycie rozszerzeń dostępnych dla paska narzędzi Arcpy Addin Toolbar. Dodałem rozszerzenie, które nasłuchuje na początku lub na końcu sesji edycji. Mam wszystkie moje przyciski na pasku ustawione na: self.enable = False "na początek, a następnie te przyciski są następnie włączane lub wyłączane przez rozpoczęcie lub zatrzymanie sesji edycji.

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
źródło
To wygląda na rozwiązanie, które warto wypróbować. Dziękuję
user18412,
4

Wysyłam inną odpowiedź, ponieważ nauczyłem się nowej metody sprawdzania statusu edytora w ArcMap przy użyciu ArcObjects i Pythona razem. Moja odpowiedź mocno zapożycza się z pracy wykonanej przez Marka Cederholma, o której mowa w tym poście: Jak uzyskać dostęp do ArcObjects z Pythona? oraz przykłady kodu dostarczone przez Matta Wilkie w jego pliku „Snippits.py”. Będziesz musiał postępować zgodnie z instrukcjami podanymi w pierwszej odpowiedzi, aby pobrać i zainstalować typy komputerów, a następnie uzyskać kopię skryptu Snippets.py. Poniżej zamieszczam kopię podstawowych funkcji z tego skryptu.

Gdy wywoływana jest funkcja ArcMap_GetEditSessionStatus (), sprawdzi bieżący stan edytora w ArcMap i zwróci true lub false. To pozwala mi sprawdzić, czy użytkownik jest przygotowany do korzystania z mojego narzędzia lub czy trzeba go poprosić o rozpoczęcie sesji edycji. Minusem tej metody jest wymóg instalowania typów plików, zanim ArcObjects będzie mógł być używany w Pythonie, więc udostępnienie narzędzia wymagającego tego pakietu w środowisku biurowym dla wielu użytkowników może nie być możliwe. Z moim ograniczonym doświadczeniem nie jestem pewien, jak to wszystko połączyć w celu łatwego udostępniania jako dodatek do narzędzia Esri Python. Docenione zostaną sugestie, jak to zrobić.

#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
użytkownik18412
źródło
1
To działa świetnie. Wiem, że to stary post, ale jeśli chcesz to spakować, aby był bardziej przenośny, możesz zrobić moduł snippets jako pakiet python i zawierać w nim typy komiksów. Robię to dla mojej firmy i umieściłem wszystkie nasze niestandardowe moduły Python w udziale sieciowym. Za każdym razem, gdy ktoś instaluje / ponownie instaluje oprogramowanie ArcGIS, każę mu uruchomić plik wsadowy, który modyfikuje jego Desktop.pthplik, tak aby zawierał pełną ścieżkę do udziału sieciowego, aby każdy mógł automatycznie zaimportować wszystko.
crmackey,
2

Co powiesz na korzystanie z modułu dostępu do danych ? Wygląda na to, że możesz rozpocząć sesję edycji za pomocą tego modułu.

Kilka zastrzeżeń:

  1. Nie próbowałem tego modułu i nie jestem pewien, czy jest on zgodny z wersją 10.0. (Nowy w 10.1?)
  2. Przykład 1 pokazuje użycie withinstrukcji. Jest to świetny paradygmat do wdrożenia, ponieważ dobrze radzi sobie z potencjalnymi wyjątkami.
  3. Możesz być w stanie sprawdzić, czy sesja edycji jest już aktywna, próbując uruchomić ją w try / exceptinstrukcji.
Jay Laura
źródło
Właściwie zacząłem od użycia klasy Editor w module dostępu do danych, kiedy rozpocząłem ten projekt, ale użycie go nie miało znaczenia. Dołączenie „z arcpy.da.Editor (obszar roboczy) jako edit:” w moim skrypcie nie aktywowało edytora i próba stopOperation / stop.Editing nie zatrzymał edytora. Ale mógłbym robić to źle ...
user18412
1

W ten sposób naprawiłem problem braku możliwości kontrolowania, czy ktoś korzystający z mojego narzędzia był w sesji edycji, czy nie:

#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

Skrypt działa, ponieważ próbuje utworzyć UpdateCursor na warstwie, która ma innego UpdateCursor później w skrypcie. Narusza to zachowanie modułu dostępu do danych. Według strony Zasoby ESRI na arcpy.da.UpdateCursor:

„Otwieranie jednoczesnych operacji wstawiania i / lub aktualizacji w tym samym obszarze roboczym przy użyciu różnych kursorów wymaga rozpoczęcia sesji edycji”.

Nie jestem zadowolony z tego rozwiązania, ponieważ jest to bardziej hack niż to, co uważam za właściwe arkanowe skrypty. Lepsze pomysły ktoś?

użytkownik18412
źródło
1
To tylko pomysł, ale możesz spróbować uzyskać dostęp do obiektu Editor w ArcObjects i sprawdzić jego właściwość EditState, która wydaje się brakować w Arcpy? Nigdy nie próbowałem manipulować ArcObjects z python, ale ten wątek mówi o tym, jak to zrobić?
Hornbydd,