0

I am using the following code to determine if something is selected within a layer:

if arcpy.Describe("layername").FIDSet:

It is working fine and running the following code when I first open ArcMap and run the script, but once I change MXD it stops working, and the FIDset returns empty, even though there are polygons selected as before.

Has anybody run into this/a similar problem before?

Edit:

Full syntax (on an Addin button):

mxd = arcpy.mapping.MapDocument('current')
desc=arcpy.Describe("PDS Temp")

if desc.FIDSet:
    lyrs = arcpy.mapping.ListLayers(mxd,"",arcpy.mapping.ListDataFrames(mxd)[0])
    fileName1 = mxd.filePath.split("\\")[-1]
    fileName = fileName1.split(".")[0]
    for layer in lyrs:
        if layer.longName == 'PDS Temp':
            fields = arcpy.ListFields(layer, "","String")
            rows = arcpy.UpdateCursor(layer) 
            for row in rows:
                    for field in fields:
                            if field.name == "Search_Reference":
                                row.setValue(field.name,fileName)
                    rows.updateRow(row)
                    print "Search Ref Updated Successfully"
else:
    print "nothing selected"
del mxd
Hornbydd
  • 43,380
  • 5
  • 41
  • 81
James
  • 29
  • 3
  • 1
    A "layer" is a property of a map document. Changing the map document would naturally refresh the layer list. This is a bit like saying the silverware drawer is missing when you visit a neighbor's house and look to the left of the stove and only find pencils, coupons, and rubber bands. – Vince Dec 03 '15 at 11:49
  • Can you edit and post more of the code, showing how you are getting layer reference and mxd reference? – artwork21 Dec 03 '15 at 12:52
  • Added sytax above. I don't see how refreshing the layer list should matter if im reading the MXD each time – James Dec 03 '15 at 14:09
  • Just found a key thing... the Python interactive window does not reset when you load a new map document. So, all of your variables are still defined when you load the new map. I am going to look into addin buttons... but I suspect that your desc variable is having issues and possibly your mxd variable. – blord-castillo Dec 03 '15 at 14:11
  • 1
    Can you post the entire python add-in class for your button? I suspect that add-ins have a similar issue to the interactive window of keeping their entire variable state when a new map is loaded. There is a chance that even the layer references stay referencing the last layer, not the current layer so even though you redefine desc, it still ends up referencing the old 'PDS Temp' instead of the new 'PDS Temp' – blord-castillo Dec 03 '15 at 14:23
  • That is pretty much the whole class (minus the declaration), i've added a 'del desc' line on the end before the 'pass' but that hasn't helped me. – James Dec 03 '15 at 14:25
  • 1
    So this is the onClick function ? I would actually move the mxd declaration to the init function and use self.mxd instead of mxd. From some quick tests, if you use keyword "current" it always references the current document even across loads. Don't delete mxd at the end of your function. If this is wrapped in the onClick function, then you should not need to do any deleting of variables at the end. – blord-castillo Dec 03 '15 at 14:37
  • 1
    For each map doc. you are running this against, is there a layer called PDS Temp? Also, are you setting the env. workspace variable somewhere before your desc=arcpy.Describe("PDS Temp") statement? – artwork21 Dec 03 '15 at 14:40
  • Sorry yes, there's a layer called PDS temp in each of the documents, they're all saved from the same template. And yes in the OnClick method. – James Dec 03 '15 at 14:59

1 Answers1

1

I think this is what you are trying to achieve based on what you posted. I switched to using os.path to parse your filename and using an arcpy.da cursor using a with statement instead of the cursor you were using (and not cleaning up appropriately at the end, which might have kept a handle to the old PDS Temp active). You also do not need to iterate over field names, just give it the one field name that you are changing since you change it for every row anyway. I added in a test for the presence of the field name, just in case.

I suspect that your cursor not being closed was doing something weird with the PDS Temp reference, so that you are somehow still referencing the PDS Temp in the last map document. Not sure though, since I do not know the behavior of Add Ins very well.

The code sample below is untested (I don't have the python add-in wizard installed). As I noted with my comment, your one print statement should print a whole bunch. I have no idea where print statements in add-ins output to. One other thing to consider is defining self.desc in __init__ and then clearing out self.desc at the end of onClick so that desc is not reinitialized with every click.

Note: Change ButtonClass to be the appropriate class name for your button that you got from the python add-in wizard.

import arcpy
from os.path import basename
from os.path import splitext

class ButtonClass(object):
    def __init__(self):
        self.enabled = True
        self.checked = False
        self.mxd = arcpy.mapping.MapDocument('current')
    def onClick(self):
        desc = arcpy.Describe("PDS Temp")
        if desc.FIDSet:
            lyrs = arcpy.mapping.ListLayers(self.mxd, '', arcpy.mapping.ListDataFrames(self.mxd)[0])
            fileName = splitext(basename(self.mxd.filePath))[0]
            for layer in lyrs:
                if layer.longName == 'PDS Temp' and len(arcpy.ListFields(layer, 'Search_Reference')):
                    edit = arcpy.da.Editor(layer.workspacePath)
                    edit.startEditing(False, False) #Assumes unversioned data
                    edit.startOperation()
                    try:
                        with arcpy.da.UpdateCursor(layer, ['Search_Reference']) as cursor:
                            for row in cursor:
                                row[0] = fileName
                                cursor.updateRow(row)
                                #The next line will print for every row you update!
                                print('Search_Reference updated successfully.')
                    except:
                        raise
                    finally:
                        edit.stopOperation()
                        edit.stopEditing(True)
        else:
            print('Nothing selected.')

EDIT: An alternate way to do this, with the button being enabled and disabled based on there being an edit selection available.

import arcpy
import pythonaddins
from os.path import basename
from os.path import splitext

class ButtonClass(object):
    def __init__(self):
        self.enabled = False
        self.checked = False
        self.mxd = arcpy.mapping.MapDocument('current')

    def onClick(self):
        fileName = splitext(basename(self.mxd.filePath))[0]
        lyrs = arcpy.mapping.ListLayers(self.mxd, '', arcpy.mapping.ListDataFrames(self.mxd)[0])
        for layer in lyrs:
            if layer.longName == 'PDS Temp' and len(arcpy.ListFields(layer, 'Search_Reference')):
            arcpy.CalculateField_management(layer, 'Search_Reference', '"{0}"'.format(fileName, "PYTHON")
        else:
            print('Nothing selected.')

class ExtensionClass(object):
    def __init__(self):
        self.enabled = True

    def enableCheck(self):
        # tool1 is the tool ID (without the namespace prefix) for your button add-in
        # Should probably use self.currentLayer as well to make sure you have the right layer
        if len(self.editSelection) == 0:
            tool1.enabled = False
        else:
            tool1.enabled = True
            arcpy.RefreshActiveView()
        return

    def onStartEditing(self):
        self.enableCheck(self)
        return

    def onStopEditing(self):
        # tool1 is the tool ID (without the namespace prefix) for your button add-in
        tool1.enabled = False
        arcpy.RefreshActiveView()
        return

    def onEditorSelectionChanged(self):
        self.enableCheck(self)
        return
blord-castillo
  • 6,447
  • 22
  • 42
  • Thanks for taking the time. Apparently that method isn't supported in an edit session? 'RuntimeError: The operation is not supported within an edit session.' I need to be editing to have a selection though surely? – James Dec 03 '15 at 15:28
  • No, you don't at all. Hold on and I will fix that. – blord-castillo Dec 03 '15 at 15:29
  • Okay, I added in an edit session with arcpy.da.Editor. This will only work for unversioned datasets. If you are working with versioned datasets, you need to change edit.startEditing(False,False) to edit.startEditing(False,True). If you have a mix of verisoned and unversioned, it gets really complicated. – blord-castillo Dec 03 '15 at 15:37
  • Wait, 'not supported within an edit session'? Okay, that's weird if that's what it said. Do you have an edit session open while you are trying to do this? If so, then your cursors are not going to work. (And my changes above will not help you, though they also should not hurt anything.) – blord-castillo Dec 03 '15 at 15:40
  • I do yes, have an edit session open for the GDB in which the feature class 'PDS Temp' is. My previous code definately works the first time, thereafter it doesn't error, it just doesnt edit anything because it doesnt get anything from arcpy.Describe("PDS Temp") the second time around. – James Dec 03 '15 at 15:44
  • It shouldn't be working with an edit session open, with either cursor type. I am stumped why it worked once with the standard cursor type. Anyway, if you are going to always have an edit session open, you should just use arcpy.CalculateField_management instead of a cursor. That will use your open edit session and honor selections. (Realistically you are just making a shortcut for a calculate field operation anyway then.) – blord-castillo Dec 03 '15 at 16:01
  • Ah I see - thanks, definately would have been simpler to do this from the beginning. It still updates every row in the table if nothing is selected (albeit less disasterously because I can simply discard the edits). I will still need to use the arcpy.Describe("PDS Temp").FIDSet to see if anything i selected. Unless there is another way of telling if anything is selected – James Dec 03 '15 at 16:50
  • @blord-castillo you can check if edit session curently exist How to check via Arcpy if ArcMap is currently in an edit session or not? – GeoStoneMarten Dec 03 '15 at 20:08
  • @GeoStoneMarten Thanks! That will be useful for other scripts. – blord-castillo Dec 03 '15 at 20:13
  • @James That is the best way I know to check for a selection. Just use that to drop out of the tool if there is no selection (or could even pop up a choice dialog to continue or not). – blord-castillo Dec 03 '15 at 20:13
  • @blord-castillo but it dont detect if editing can work or not. if dataset, or featureclass is in same workspace you can already modify data. I think other check is needed to return warning or raise. 1 don't create editing session if exist in same workspace. 2. check is editing session is in the same workspace of your data or proposes to close this before open new session – GeoStoneMarten Dec 03 '15 at 20:31
  • @GeoStoneMartin Oh man, you just reminded me of the Application Extension Class for add-ins https://desktop.arcgis.com/en/desktop/latest/guide-books/python-addins/extension-class.htm. This totally solves this problem. – blord-castillo Dec 03 '15 at 21:02
  • That looks great - thanks guys. Has anyone got any experience in using onEditorSelectionChanged()? I can't seem to get it to run at all – James Dec 04 '15 at 09:59
  • @James the subject has been treated here . Look at end of post – GeoStoneMarten Dec 04 '15 at 12:31