Verificando via ArcPy se o ArcMap está em uma sessão de edição?

11

Criei um botão de suplemento Python que ajuda a acelerar o fluxo de trabalho de meus colegas de trabalho, copiando um atributo de classe de recurso para outro. Ele usa a função arcpy.UpdateCursor para atualizar uma linha na classe de recurso de destino. Como existe agora, esse script de botão pode ser executado independentemente do modo de edição. Obviamente, quando executado em uma sessão de edição, o usuário pode optar por interromper a edição e não salvar as alterações, mas esse não é o caso quando o script é executado fora de uma sessão de edição.

Como posso adicionar uma verificação ao script que interromperá a execução do script se o ArcMap não estiver atualmente em uma sessão de edição?

Isso diz respeito ao ArcMap 10 e 10.1


Também quero verificar com outros usuários do ArcMap para verificar se as atualizações das tabelas normalmente não são permitidas sem estar em uma sessão de edição.

Então, como esse script está sendo executado fora de uma sessão de edição?

Esse script também traz outra pergunta sobre a ordem de seleção aparentemente aleatória que o ArcMap executa que só funciona para mim quando eu atualizo a 2ª tabela de classes de recursos de uma lista, mas isso é para outro dia.

Aqui está o script como funciona agora (sem nenhuma implementação do editor 10.1):

Como adicionar uma verificação para garantir que o usuário esteja em uma sessão de edição?

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
fonte
O editor do módulo de acesso a dados parece operar independentemente do editor padrão. Gostaria de receber mais ideias sobre testes para uma sessão de edição ativa. -Karl
KarlJr
Você pode fornecer um pouco mais de informação? O que levou você a essa conclusão para aqueles de nós que não exploraram o módulo?
Jay Laura

Respostas:

6

Aqui está uma função genérica baseada neste post.

Talvez isso seja um pouco mais complicado do que a solução ArcObjects, mas com certeza parece muito menos aborrecimento! Simples é melhor que complexo. Exceto quando não é.

Exemplo 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
Preço de Curtis
fonte
+1 Bom conceito, no entanto, o OP deseja parar se não estiver em uma sessão de edição e continuar se estiver em uma sessão de edição. Sua resposta parece fazer o oposto. Possivelmente não seria preciso muito para mudar isso.
Midavalo
O OP já resolveu seu problema, este post está apenas congelando com uma função mais geralmente útil. Modifiquei meu exemplo para ficar mais claro sobre como a função é usada.
Curtis Price
4

Minha solução para esse problema foi usar as extensões disponíveis para a barra de ferramentas do Arcpy Addin. Adicionei uma extensão que escuta que uma sessão de edição comece ou termine. Eu tenho todos os meus botões na barra configurados para: self.enable = False "para começar e, em seguida, esses botões são ativados ou desativados ao iniciar ou parar uma sessão de edição.

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
fonte
Parece uma solução que vale a pena tentar. Obrigado
user18412
4

Estou postando outra resposta porque aprendi um novo método para verificar o status do Editor no ArcMap usando ArcObjects e Python juntos. Minha resposta empresta muito do trabalho realizado por Mark Cederholm, conforme mencionado neste post: Como acesso o ArcObjects a partir do Python? e exemplos de código fornecidos por Matt Wilkie em seu arquivo "Snippits.py". Você precisará seguir as instruções fornecidas na primeira resposta para baixar e instalar comtypes e, em seguida, obter uma cópia do script Snippets.py. Estou postando uma cópia das funções essenciais desse script abaixo.

Quando a função ArcMap_GetEditSessionStatus () é chamada, ela verifica o estado atual do Editor no ArcMap e retorna verdadeiro ou falso. Isso permite verificar se um usuário está preparado para usar minha ferramenta ou se ele precisa ser solicitado a iniciar uma sessão de edição. A desvantagem desse método é o requisito para instalar comtypes antes que o ArcObjects possa ser usado no Python, portanto, talvez não seja possível compartilhar uma ferramenta que exija esse pacote em um ambiente de escritório para vários usuários. Com minha experiência limitada, não tenho certeza de como agrupar tudo para facilitar o compartilhamento como um complemento da ferramenta Esri Python. Sugestões de como fazer isso seriam apreciadas.

#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
fonte
1
Isso funciona muito bem. Eu sei que este é um post antigo, mas se você quiser empacotar isso para que seja mais portátil, você pode fazer o módulo snippets como um pacote python e incluir comtypes nele. Faço isso para minha empresa e coloquei todos os nossos módulos Python personalizados em um compartilhamento de rede. Toda vez que alguém instala / reinstala o software ArcGIS, solicito que eles executem um arquivo em lotes que modifique seu Desktop.ptharquivo para incluir o caminho completo para o compartilhamento de rede, para que todos possam importar tudo automaticamente.
crmackey
2

Que tal usar o módulo de acesso a dados ? Parece que você pode iniciar uma sessão de edição com este módulo.

Algumas advertências:

  1. Eu não tentei este módulo e não tenho certeza se é 10.0 compatível. (Novo no 10.1?)
  2. O exemplo 1 mostra o uso de uma withinstrução Esse é um ótimo paradigma a ser implementado, pois lida bem com possíveis exceções.
  3. Você pode testar se uma sessão de edição já está ativa ao tentar iniciar uma em uma try / exceptinstrução.
Jay Laura
fonte
Na verdade, comecei a usar a classe Editor no módulo de acesso a dados quando iniciei este projeto, mas usá-lo não parecia importar. Incluir "com arcpy.da.Editor (espaço de trabalho) como edição:" no meu script não ativou o editor e tentar stopOperation / stop.Editing não parou o editor. Mas eu poderia estar fazendo errado ...
user18412
1

Foi assim que resolvi o problema de não poder controlar se alguém que usava minha ferramenta estava ou não em uma sessão de edição:

#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

O script funciona porque tenta criar um UpdateCursor em uma camada que possui outro UpdateCursor posteriormente no script. Isso viola o comportamento do módulo de acesso a dados. De acordo com a página Recursos da ESRI em arcpy.da.UpdateCursor:

"Abrir operações de inserção e / ou atualização simultâneas no mesmo espaço de trabalho usando cursores diferentes requer o início de uma sessão de edição."

Não estou feliz com esta solução porque é mais um truque do que imagino ser um script arcpy adequado. Idéias melhores para alguém?

user18412
fonte
1
Esta é apenas uma ideia, mas você pode tentar acessar o objeto Editor no ArcObjects e verificar sua propriedade EditState, que parece ser o que está faltando no arcpy. Eu nunca tentei manipular ArcObjects a partir de python, mas este tópico fala sobre como fazê-lo?
Hornbydd