Проверка через ArcPy, находится ли ArcMap в сеансе редактирования?

11

Я создал кнопку надстройки Python, которая помогает ускорить рабочий процесс моего коллеги путем копирования одного атрибута класса объектов в другой. Он использует функцию arcpy.UpdateCursor для обновления строки в целевом классе объектов. Так как он существует сейчас, этот скрипт кнопки можно запускать независимо от режима редактирования. Очевидно, что при запуске в сеансе редактирования пользователь может выбрать остановку редактирования и не сохранять изменения, но это не тот случай, когда скрипт выполняется вне сеанса редактирования.

Как я могу добавить в скрипт проверку, которая остановит его выполнение, если ArcMap в данный момент не находится в сеансе редактирования?

Это касается ArcMap 10 & 10.1


Я также хочу проверить у других пользователей ArcMap, что обновления таблиц обычно не допускаются без участия в сеансе редактирования.

Так как же этот скрипт работает вне сеанса редактирования?

Этот сценарий также поднимает еще один вопрос о, казалось бы, случайном порядке выбора, который выполняет ArcMap, и он мне подходит, когда я обновляю 2-ю таблицу классов объектов из списка, но это на другой день.

Вот скрипт, как он работает сейчас (без какой-либо реализации редактора 10.1):

Как добавить проверку, чтобы убедиться, что пользователь находится в сеансе редактирования?

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
источник
Редактор модуля доступа к данным работает независимо от стандартного редактора. Я бы приветствовал любые дополнительные идеи по тестированию для активного сеанса редактирования. -Карл
KarlJr
Можете ли вы предоставить немного больше информации? Что привело вас к такому выводу для тех из нас, кто еще не исследовал модуль?
Джей Лора

Ответы:

6

Вот общая функция, основанная на этом посте.

Может быть, это немного сложнее, чем решение ArcObjects, но, похоже, намного меньше хлопот! Простое лучше, чем сложное. За исключением случаев, когда это не так.

Пример использования:

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

код:

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
Кертис Прайс
источник
+1 Хорошая концепция, однако ОП хочет остановиться, если не в сеансе редактирования, и продолжить, если он находится в сеансе редактирования. Ваш ответ, кажется, делает обратное. Возможно, не потребовалось бы много времени, чтобы все изменить.
Мидавало
ОП уже решил свою проблему, этот пост - просто более полезная функция. Я изменил свой пример, чтобы было более понятно, как используется функция.
Кертис Прайс
4

Моим решением этой проблемы было использование расширений, доступных для панели инструментов Arcpy Addin. Я добавил расширение, которое прослушивает сеанс редактирования, чтобы начать или закончить. У меня все мои кнопки на панели установлены для: self.enable = False "для начала, а затем эти кнопки либо включаются, либо отключаются при запуске или остановке сеанса редактирования.

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
источник
Это похоже на решение, которое стоит попробовать. Спасибо
user18412
4

Я публикую еще один ответ, потому что я узнал новый метод проверки статуса Редактора в ArcMap с использованием ArcObjects и Python вместе. Мой ответ во многом заимствован из работы, проделанной Марком Седерхольмом, о которой говорится в этом посте: Как получить доступ к ArcObjects из Python? и примеры кода, предоставленные Мэттом Вилки в его файле «Snippits.py». Вам нужно будет следовать инструкциям, приведенным в первом ответе, чтобы загрузить и установить комтипы, а затем получить копию сценария Snippets.py. Я публикую копию основных функций из этого скрипта ниже.

Когда вызывается функция ArcMap_GetEditSessionStatus (), она проверяет текущее состояние редактора в ArcMap и возвращает значение true или false. Это позволяет мне проверить, готов ли пользователь использовать мой инструмент или ему нужно предложить начать сеанс редактирования. Недостатком этого метода является требование устанавливать комтипы перед использованием ArcObjects в Python, поэтому совместное использование инструмента, для которого требуется этот пакет, в многопользовательской офисной среде может оказаться невозможным. Имея свой ограниченный опыт, я не уверен, как объединить все это для удобного совместного использования в качестве надстройки инструмента Esri Python. Предложения о том, как это сделать, будут оценены.

#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
источник
1
Это прекрасно работает. Я знаю, что это старый пост, но если вы хотите упаковать его, чтобы он был более переносимым, вы можете сделать модуль snippets как пакет python и включить в него комтипы. Я делаю это для своей компании, и я разместил все наши собственные модули Python в сетевой папке. Каждый раз, когда кто-то устанавливает / переустанавливает программное обеспечение ArcGIS, я заставляю его запускать командный файл, который изменяет его Desktop.pthфайл, чтобы включить полный путь к общему сетевому ресурсу, чтобы каждый мог автоматически импортировать все.
crmackey
2

Как насчет использования модуля доступа к данным ? Похоже, вы можете начать сеанс редактирования с этим модулем.

Несколько предостережений:

  1. Я не пробовал этот модуль и не уверен, что он 10.0 совместим. (Новое в 10.1?)
  2. Пример 1 показывает использование withоператора. Это отличная парадигма для реализации, поскольку она хорошо обрабатывает потенциальные исключения.
  3. Возможно, вы сможете проверить, является ли сеанс редактирования уже активным, попытавшись запустить его в try / exceptвыражении.
Джей Лаура
источник
На самом деле я начал с использования класса Editor в модуле доступа к данным, когда начал этот проект, но, похоже, его использование не имело значения. Включение «с arcpy.da.Editor (рабочая область) как edit:» в моем скрипте не активировал редактор, и попытка stopOperation / stop. Редактирование не остановило редактор. Но я могу делать это неправильно ...
user18412
1

Вот как я исправил проблему, связанную с невозможностью контролировать, был ли кто-то, использующий мой инструмент, в сеансе редактирования или нет:

#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

Сценарий работает, потому что он пытается создать UpdateCursor на слое, у которого есть другой UpdateCursor позже в сценарии. Это нарушает поведение модуля доступа к данным. Согласно странице ресурсов ESRI на arcpy.da.UpdateCursor:

«Открытие одновременных операций вставки и / или обновления в одной рабочей области с использованием разных курсоров требует начала сеанса редактирования».

Я не доволен этим решением, потому что это скорее хак, чем то, что я представляю себе, как надлежащий arcpy сценарий. У кого есть идеи получше?

user18412
источник
1
Это всего лишь идея, но вы можете попробовать получить доступ к объекту Editor в ArcObjects и проверить его свойство EditState, которое, по-видимому, является тем, чего не хватает в arcpy? Я никогда не пытался манипулировать ArcObjects из python, но этот поток говорит о том, как это сделать?
Хорнбидд