11

I've created a Python add-in button that helps speed up my coworkers workflow by copying one feature class attribute to another. It uses the arcpy.UpdateCursor function to update a row in the target feature class. As it exists now, this button script can be run regardless of the editing mode. Obviously when its run in an edit session, the user can choose to stop editing and not save changes, but this is not the case when the script runs outside of an edit session.

How can I add a check to the script that will stop the script from running if ArcMap is not currently in an edit session?

This concerns ArcMap 10 & 10.1


I also want to check with other ArcMap users to verify that updates to tables is not normally allowed without being in an edit session.

So how is this script running outside of an edit session?

This script also brings up another question about the seemingly serendipitous selection order ArcMap performs that just happens to work for me when I update the 2nd feature class table from a list, but that's for another day.

Here's the script as it works now (without any 10.1 editor implementation):

How to add a check to ensure the user is in an edit session?

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
2
  • The data access module editor seems to operate independent of the standard Editor. I would welcome any additional ideas on testing for an active edit session. -Karl
    – KarlJr
    Commented May 31, 2013 at 14:06
  • Can you provide a little more info? What led you to this conclusion for those of us that have not explored the module?
    – Jay Laura
    Commented May 31, 2013 at 14:15

5 Answers 5

6

Here's a generic function based on this post.

Maybe this is a little more kludgy than the ArcObjects solution, but it sure seems like a lot less hassle! Simple is better than complex. Except when it isn't.

Example usage:

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
4
  • +1 Nice concept, however the OP wants to stop if not in an edit session, and continue if it is in an edit session. Your answer appears to do the opposite. Possibly wouldn't take much to turn that around though.
    – Midavalo
    Commented Oct 8, 2016 at 21:34
  • The OP solved his problem already, this post is just icing with a more generally useful function. I modified my example to be more clear as to how the function is used. Commented Oct 8, 2016 at 22:31
  • Just chiming in that this function won't work against a feature class that requires an edit session to be edited, such as a geometric network feature class or one that participates in a topology.
    – J. Flann
    Commented Apr 6, 2022 at 18:36
  • Just wanted to comment that I used this script today and it worked great!
    – Midavalo
    Commented Dec 27, 2023 at 15:31
4

My solution to this problem was to use the extensions available for the Arcpy Addin Toolbar. I added an extension that listens for an edit session to begin or end. I have all of my buttons on the bar set to :self.enable = False" to start with and then these buttons are then either enable or disabled by starting or stop an edit session.

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):
        ......
1
  • This looks like a solution worth trying. Thank you
    – user18412
    Commented Nov 8, 2013 at 14:53
4

I'm posting another answer because I've learned a new method for checking the status of the Editor in ArcMap using ArcObjects and Python together. My answer borrows heavily from the work done by Mark Cederholm as referenced in this post: How do I access ArcObjects from Python?, and code examples provided by Matt Wilkie in his "Snippits.py" file. You will need to follow the instructions provided in the first answer to download and install comtypes and then get a copy of the Snippets.py script. I am posting a copy of the essential functions from that script below.

When the function ArcMap_GetEditSessionStatus() is called, it will check the current state of the Editor in ArcMap and return true or false. This lets me check if a user is prepared to use my tool or if they need to be prompted to start an edit session. The downside to this method is the requirement to install comtypes before ArcObjects can be used in Python, so sharing a tool that requires this package in a multi-user office environment might not be possible. With my limited experience I'm not sure how to bundle it all together for easy sharing as an Esri Python tool add-in. Suggestions for how to do this would be appreciated.

#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
1
  • 1
    This works great. I know this is an old post, but if you want to package this up so it is more portable, you can make the snippets module as a python package, and include comtypes within it. I do this for my company and I have placed all our custom Python modules on a network share. Every time someone installs/re-installs the ArcGIS software, I have them run a batch file that modifies their Desktop.pth file to include the full path to the network share, so everyone can automatically import everything.
    – crmackey
    Commented Jul 21, 2016 at 13:55
2

How about using the data access module? It looks like you can start an edit session with this module.

A few caveats:

  1. I have not tried this module and the am not sure if it is 10.0 compatible. (New in 10.1?)
  2. Example 1 shows the use of a with statement. This is a great paradigm to implement as it handles potential exceptions well.
  3. You might be able to test if an edit session is already live by attempting to launch one in a try / except statement.
1
  • I actually started with using the Editor class in the data access module when I began this project but using it didn't seem to matter. Including "with arcpy.da.Editor(workspace) as edit:" in my script didn't activate the editor, and trying stopOperation/stop.Editing didn't stop the editor. But I could be doing it wrong...
    – user18412
    Commented May 23, 2013 at 21:18
1

So this is how I fixed my issue of not being able to control if someone using my tool was in an edit session or not:

#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

The script works because it tries to create an UpdateCursor on a layer that has another UpdateCursor later in the script. This violates the behavior of the data access module. According to the ESRI Resources page on arcpy.da.UpdateCursor:

"Opening simultaneous insert and/or update operations on the same workspace using different cursors requires the start of an edit session."

I'm not happy with this solution because it's more of a hack than what I imagine is proper arcpy scripting. Better ideas anyone?

1
  • 1
    This is just an idea but you could try accessing the Editor object in ArcObjects and check its property EditState which seems to be what is missing from arcpy? I've never tried to manipulate ArcObjects from python but this thread talks about how to do it?
    – Hornbydd
    Commented Nov 8, 2013 at 13:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.