Select to view content in your preferred language

Use Arcpy to Unselect the currently selected Features in all layers

12427
14
10-15-2013 04:42 AM
OLANIYANOLAKUNLE
Frequent Contributor
I have a script that unselects all the selected features in my Map view but it loops through each layer in the dataframe and this takes a lot of time. I want to write a script that would be as fast as the Unselect the currently selected Features in all layers within ArcMap itself? This is my code below;

mxd = arcpy.mapping.MapDocument("Current")
df = arcpy.mapping.ListDataFrames(mxd)[0]
for lyr in arcpy.mapping.ListLayers(mxd):
...     arcpy.SelectLayerByAttribute_management(lyr, "CLEAR_SELECTION")
...     
Runtime error  Traceback (most recent call last):   File "<string>", line 2, in <module>   File "C:\Program Files (x86)\ArcGIS\Desktop10.1\arcpy\arcpy\management.py", line 6461, in SelectLayerByAttribute     raise e ExecuteError: Failed to execute. Parameters are not valid. ERROR 000840: The value is not a Table View. ERROR 000840: The value is not a Raster Layer. ERROR 000840: The value is not a Mosaic Layer. Failed to execute (SelectLayerByAttribute).


Any Suggestions? Thanks
Tags (2)
14 Replies
ChrisSnyder
Honored Contributor
Not sure if ArcObjects/ComTypes is totally neccesssary here. Also, going back to the original post... The 3rd line should be:

for lyr in arcpy.mapping.ListLayers(df):

not
for lyr in arcpy.mapping.ListLayers(mxd):



In arcpy-land, the trick is to turn the layers' visibility off 1st, clear the selection(s), then turn the visible layer back on. Otherwise it want's to redraw every loop... which is really slow.

A little cluncky and verbose (might take out the .fidSet part - not sure if it would help that much). For me, this code takes 2 seconds to do the actual unselecting for ~6 layers with 20k features selcted... And then a redraw, which also takes time. The ArcMap unselect tool takes like 0.25 seconds... And DOESN'T have to redraw!

mxd = arcpy.mapping.MapDocument("CURRENT")
df = arcpy.mapping.ListDataFrames(mxd)[0]
lyrList = arcpy.mapping.ListLayers(df)
lyrDict = {}
for lyr in lyrList:
    lyrDict[lyr] = [lyr.visible, False]
    if len(arcpy.Describe(lyr).fidSet) > 0:
         lyrDist[lyr][1] = True
for lyr in lyrDict: #turn any visible layers off
    if lyrDict[lyr][0] == True:
        lyr.visible = False
arcpy.RefreshActiveView()
for lyr in lyrDict: #clear selections
    if lyrDict[lyr][1] == True:
        arcpy.SelectLayerByAttribute_management(lyr, "CLEAR_SELECTION")
for lyr in lyrDict: #turn any previously visible layers back on
    if lyrDict[lyr][0] == True: 
        lyr.visible = True
arcpy.RefreshActiveView()


EDIT: Fewer lines - with comprehensions!
mxd = arcpy.mapping.MapDocument("CURRENT")
df = arcpy.mapping.ListDataFrames(mxd)[0]
lyrDict = {}
for lyr in arcpy.mapping.ListLayers(df):
    lyrDict[lyr] = [lyr.visible, False]
    if len(arcpy.Describe(lyr).fidSet) > 0:
         lyrDist[lyr][1] = True
for lyr in [lyr for lyr in lyrDict if lyrDict[lyr][0] == True]:  #turn any visible layers off
    lyr.visible = False
arcpy.RefreshActiveView()
for lyr in [lyr for lyr in lyrDict if lyrDict[lyr][1] == True]: #clear selections
    arcpy.SelectLayerByAttribute_management(lyr, "CLEAR_SELECTION")
for lyr in [lyr for lyr in lyrDict if lyrDict[lyr][0] == True]: #turns layers back on 
    lyr.visible = True
arcpy.RefreshActiveView()
0 Kudos
ChrisSnyder
Honored Contributor
It'd be nice if the layer object from arcpy.mapping had some sort of 'selected set' property via arcpy.mapping. Seems all the other layer properties (like .definitionQuery, .name, etc) make it across from the good ole' Describe object ... Why no .fidSet!?!?!

for lyr in arcpy.mapping.ListLayers(df):
    if len(lyr.fidSet) > 0: #BTW: Wish the .fidSet property returned a native Python list!
        blah blah


Maybe even:

for lyr in arcpy.mapping.ListLayers(df):
    if lyr.hasSelection == True:
        blah blah


Hoping for some extra features for arcpy in the future!
0 Kudos
T__WayneWhitley
Honored Contributor
Ah, thank you Chris, you got me thinking about performance of the display refresh - this can be a subtlety greatly impacting performance.  So what I needed access to was PartialRefresh (IActiveView)... I was banging my head against this for awhile, and had to go back to the basics to understand w/ VBA.  So this VBA code more precisely mimics the 'Clear Selected Features' button (without redrawing everything):
Dim pMxDocument As IMxDocument
Dim pMap As IMap
Dim pActiveView As IActiveView

Set pMxDocument = ThisDocument
Set pMap = pMxDocument.FocusMap
Set pActiveView = pMap

pActiveView.PartialRefresh esriViewGeoSelection, Nothing, Nothing
pMap.ClearSelection


So, with that, I pared down my comtypes code to do essentially the same thing (please bear in mind, I'm still learning to apply this thing called comtypes):
import Snippets
import comtypes.gen.esriArcMapUI as esriArcMapUI
import comtypes.gen.esriCarto as esriCarto

pApp = Snippets.GetApp()
pDoc = pApp.Document
pMxDoc = Snippets.CType(pDoc, esriArcMapUI.IMxDocument)
pMap = pMxDoc.FocusMap
pAV = Snippets.CType(pMap, esriCarto.IActiveView)

pAV.PartialRefresh(esriCarto.esriViewGeoSelection, None, None)
pMap.ClearSelection()


The 1st time running in ArcMap, it takes a moment to load but nothing really limiting, and successive executions...uh, greased lightning (pardon the pun).

One more thing I may have omitted... since the script has been pared down, I admit I am not sure yet about what the comtypes is doing in the GetModule function, but it's getting called from the facilitating Snippets.py (also called GetModule) with:

from comtypes.client import GetModule

It has to do with getting the COM library refs...I think the 2 code lines below 'bypass' Snippets to get specifically to the interfaces needed (IActiveView and IMxDocument):

import comtypes.gen.esriArcMapUI as esriArcMapUI
import comtypes.gen.esriCarto as esriCarto

Not sure I fully understand that just yet - anyway, if I cut out too much in the 'initialization', you can make the Snippet.GetModule call to build what you need....


Enjoy,
Wayne
0 Kudos
T__WayneWhitley
Honored Contributor
...quick update about the comtypes installation - the above previous testing was with 10.0, which the companion module Snippets.py was designed to run with; I also have a 10.1 server installation where I installed comtypes (same version) today and got that running successfully, although haven't fully tested it yet.

However, I'll have to update Snippets if I intend to use it...I have both ArcServer and Desktop running on Windows Server 2012 and apparently the registry keys are different - the GetLibPath function that uses the python _winreg module does not find the HKEY_CLASSES_ROOT with the provided TypeLib key {866AE5D3-530C-11D2-A2BD-0000F8774FB5}....

>>> import _winreg
>>> keyESRI = _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, \
...                               r"TypeLib\{866AE5D3-530C-11D2-A2BD-0000F8774FB5}\a.0\HELPDIR")

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
WindowsError: [Error 2] The system cannot find the file specified
>>>  


That function just makes it more universal to find the root COM directory -- so for now I 'hardwired' it in this fashion (bypassing Snippets until I can get it fixed):

>>> from comtypes.client import GetModule
>>> GetModule(r'C:\Program Files (x86)\ArcGIS\Desktop10.1\com\esriArcMapUI.olb')
# Generating comtypes.gen._40499F24_596F_45D2_ACE1_A251E2990017_0_10_1
# Generating comtypes.gen._00020430_0000_0000_C000_000000000046_0_2_0
# Generating comtypes.gen.stdole
# Generating comtypes.gen._866AE5D3_530C_11D2_A2BD_0000F8774FB5_0_10_1
# Generating comtypes.gen._59FCCD31_434C_4017_BDEF_DB4B7EDC9CE0_0_10_1
# Generating comtypes.gen._C4B094C2_FF32_4FA1_ABCB_7820F8D6FB68_0_10_1
# Generating comtypes.gen._5E1F7BC3_67C5_4AEE_8EC6_C4B73AAC42ED_0_10_1
# Generating comtypes.gen.esriSystem
# Generating comtypes.gen.esriGeometry
# Generating comtypes.gen._4ECCA6E2_B16B_4ACA_BD17_E74CAE4C150A_0_10_1
# Generating comtypes.gen.esriSystemUI
# Generating comtypes.gen.esriDisplay
# Generating comtypes.gen.esriFramework
# Generating comtypes.gen._4A9C9ED7_F7DB_4614_B480_A5D265C961FC_0_10_1
# Generating comtypes.gen._0475BDB1_E5B2_4CA2_9127_B4B1683E70C2_0_10_1
# Generating comtypes.gen._18F2FC71_6B30_45B9_B101_037A8B868B66_0_10_1
...
...
...
(etc.)


If I'm using the correct terminology, the GetModule function makes a one-time call to create the 'wrapper' output to the Python install location for site pkgs, in this case comtypes makes a gen output folder (e.g., C:\Python27\ArcGIS10.1\Lib\site-packages\comtypes\gen).  That's what I was referring to in my last post about 'if you need it call the GetModule function' and the output gen will be built - on successive calls the olb has already been 'wrapped', and you just need to do something like this:

import comtypes.gen.esriArcMapUI as esriArcMapUI


...and, since having problems with the Snippet helper module, until that's fixed the comtypes CreateObject method can be called directly, for example to get the current ArcMap app (only 1 ArcGIS app was running at the time):
>>> pAppROT = comtypes.client.CreateObject(esriFramework.AppROT, interface=esriFramework.IAppROT)
>>> pAppROT.count
1

>>> pApp = pAppROT.Item(0)


Hope this clarifies a little how Snippets facilitates comtypes.  Maybe I haven't looked hard enough yet, but if someone knows the registry key fix for the GetLibPath function at 10.1 (Windows Server 2012), please let me know... I appreciate it.

Thanks,
Wayne


EDIT:
ah, looks like Luke has posted a modified GetLibPath function here he calls a 'kludge' but looks promising to me - I will test this tomorrow:
http://gis.stackexchange.com/questions/50714/are-there-any-python-scripts-for-creating-mxd-files/507...
0 Kudos
GeorgeRiner
Occasional Contributor

I find this confusion over what seem to be various flavors of 'Layer' objects.  Once you get arcpy to hand you a layer object from ListLayers and you loop through them, try using the name property of the lyr instead of the layer object itself. E.g.:

this seems to work for me:

arcpy.SelectLayerByAttribute_management(lyr.name, "CLEAR_SELECTION")

And, as many others note, this is really slow. And if going to ArcObjects is not in your game, there is a python code through arcpy that clears selections really fast:

lyr.setSelectionSet("NEW",[])

Note: this is calling a method of the layer object, it is not an arcpy function.

0 Kudos