Problems understanding code aimed at turning layers on/off

547
6
06-26-2014 03:12 AM
ChristopherEarley
New Contributor II
Hi

I am still relatively new to Python and have trouble understanding lines/parts of certain scripts. I am interested in developing a tool that can turn certain layers off and would appreciate a little help understanding a script I found on another forum.

In the script shown below I can understand most sections apart from lines 5 and 6. I don't really get what the [0] stands for or is referring to, I do not know what the "*" is for, and more importantly I don't fully understand how these two lines interact with the rest of the script.

names = [x,y,z,etc]

mxd = arcpy.mapping.MapDocument("current")
df = arcpy.mapping.ListDataFrames(, mxd, "Layers")[0]
layers = arcpy.mapping.ListLayers(mxd, "*", df)

for layer in layers:
  if layer.name in names:
    layer.visible = False

arcpy.RefreshTOC()
arcpy.RefreshActiveView()


I would appreciate any guidance anyone can provide.

Regards,

Geord359
Tags (2)
0 Kudos
6 Replies
AnthonyGiles
Frequent Contributor II
Chris,

ListDataFrames always returns a Python list object even if only one data frame is returned. In order to return a DataFrame object, an index value must be used on the list (e.g., df = arcpy.mapping.ListDataFrames(mxd)[0]

The [0] means return the first item in the array.

The * (wildcard) means return all layers, eg:

Wildcards are used on the name property and are not case sensitive. A wildcard string of "so*" will return a layer with the name Soils. Wildcards can be skipped in the scripting syntax simply by passing an empty string (""), an asterisk (*), or entering wildcard=None, or nothing at all if it is the last optional parameter in the syntax.

So essentially the code is creating an array of all the layer names in the data frame called "Layers". It then loops through each of these to see it the name matches one of those you wish to turn off.

I'm not sure why there is an extra , ListDataFrames function

df = arcpy.mapping.ListDataFrames(, mxd, "Layers")[0]

It should probably read:

df = arcpy.mapping.ListDataFrames(mxd, "Layers")[0]

Regards

Anthony
0 Kudos
markdenil
Regular Contributor II
question 1:
df = arcpy.mapping.ListDataFrames(, mxd, "Layers")[0]

ListDataFrames, as the name implies, returns a list
in this case of all data frames named "Layers"
The [0] assigns only the first item in the list (list index 0) to the variable df
(By the way, the initial comma in the ListDataFrames parenthesis is a stray, and should not be there)

question 2:
layers = arcpy.mapping.ListLayers(mxd, "*", df)

The double quoted star is a wildcard, just like "Layers" in the command before,
only this time you want to assign the entire list (as a list)
of layers in data frame df in document mxd to the variable layers

You need this list of layer objects to iterate through all the layers in that data frame
for layer in layers:

you check each layer's name against your list 
and the ones on the naughty list get turned off.

As you finish, you make sure the display reflects the changes you made.
0 Kudos
ChristopherEarley
New Contributor II
Thanks Anthony/Mark,

You've helped me alot with this. I'm still a little puzzled by the [0] though, if as you say the [0] only assigns the first item on the list would this not restrict the code to searching for a single keyword such as 'x' as above and 'link' as below? But I do feel I have a reasonable understanding of how the script works now. So thanks.

I have had a go at modifying the above using my own data. It still doesn't work. When I run the script in the python window there are no error messages and the script appears to complete but the layers that I want to turn off are unaffected and stay 'on'. Below is the script I have used. Any ideas as to why it hasn't worked?

import arcpy

link = "C:/MyArcGIS/EDRN_LINK.shp"
node = "C:/MyArcGIS/EDRN_NODE.shp"
lcc_link = "C:/MyArcGIS/EDRN_LCC_LINK.shp"
lcc_node = "C:/MyArcGIS/EDRN_LCC_NODE.shp"
yw_link = "C:/MyArcGIS/EDRN_YW_LINK.shp"

names = [link, node, lcc_link, lcc_node, yw_link]

mxd = arcpy.mapping.MapDocument("current")
df = arcpy.mapping.ListDataFrames(mxd, "Layers")[0]
layers = arcpy.mapping.ListLayers(mxd, "*", df)

for layer in layers:
    if layer.name in names:
        layer.visible = False

arcpy.RefreshTOC()
arcpy.RefreshActiveView()
0 Kudos
AdamCox1
Occasional Contributor II
Hello,

Regarding the [0]: this is the index number for the first item of the list.  If I have a list

example_list = ["item 1","item 2","item 3","item 4"]

then example_list[0] = "item 1", and example_list[1] = "item 2".  What you're seeing in your script is the creation of a list of data frame objects (all of the data frames named "Layers", as you noted) and then the index [0] which sets the df variable as the first (and most likely only) item in the list.

EDIT:  Here's a more succinct way to illustrate the list concept:

mxd = arcpy.mapping.MapDocument("current")
list_of_data_frames = arcpy.mapping.ListDataFrames(mxd, "Layers")
first_df = list_of_data_frames [0]
layers = arcpy.mapping.ListLayers(mxd, "*", first_df)

Again, most likely, Layers is the only data frame in the list, but even if it is a one-item list you must retrieve the data frame as a data frame, not as a one-item list of data frames.

You're having trouble with the code because you are using the layer.name property (name in the TOC), and testing whether it equals the data source path for the layer, which is what you have in the list.  If you use the layer.dataSource property, it should work.


for layer in layers:
    if layer.dataSource in names:
        layer.visible = False


(I would recommend changing the name of the list to something like "paths" instead of "names", just for clarity's sake.)

Good practice for troubleshooting stuff like this is to add messages to the script output.  Something like:


for layer in layers:
    arcpy.AddMessage(layer) #this should be a geoprocessing layer object
    arcpy.AddMessage(layer.name) #this should be the name in the TOC
    arcpy.AddMessage(layer.dataSource) #this should be the path
    if layer.dataSource in names:
        layer.visible = False
0 Kudos
ChristopherEarley
New Contributor II
Hi Adam,

I tried what you suggested but I'm still getting the same result. No error messages either. The program seems to work for a little while then it stops and nothing happens.

Chris
0 Kudos
AdamCox1
Occasional Contributor II
Hi Chris, if you're running this from the python console you'll have to use

print layer.name
#etc

instead of

arcpy.AddMessage(layer.name)
#etc
0 Kudos