Hi all
I am trying to write a arcpy script that will turn off all NULL and empty fields. The script runs for a while and then gives a run time error at fields
import arcpy
from arcpy import env
arcpy.env.overwriteOutput = True
arcpy.env.addOutputsToMap = False
arcpy.env.workspace = "C:/temp"
out_layer = "temp.lyr"
mxd = arcpy.mapping.MapDocument('CURRENT')
df = arcpy.mapping.ListDataFrames(mxd, '*')[0] # need to find way to get list of layers there can be more than one data frame
listfields = []
thebadlist = []
for LayerNeedsFieldsTurnedOff in arcpy.mapping.ListLayers(mxd):
if LayerNeedsFieldsTurnedOff.isGroupLayer:
print "group print"
elif LayerNeedsFieldsTurnedOff.isFeatureLayer:
fields = dict((f.name, []) for f in arcpy.ListFields(LayerNeedsFieldsTurnedOff) if not f.required)
rows = arcpy.SearchCursor(LayerNeedsFieldsTurnedOff,"","","","")
for row in rows:
for f in fields.keys():
fields
#print row.getValue(f)
for field, values in fields.iteritems():
#print field
if field == "Shape":
listfields.append(field)
elif all(map(lambda s: s is None or not str(s).strip(), values)):
thebadlist.append(field)
else:
listfields.append(field)
# LayerNeedsFieldsTurnedOff = arcpy.mapping.ListLayers(mxd, 'exampledata', df)[0]
# fill in your desired fields to remain visible
desiredFields = listfields
field_info = arcpy.Describe(LayerNeedsFieldsTurnedOff).fieldInfo # I need this for field count
for i in range(field_info.count):
if field_info.getfieldname(i) not in desiredFields:
if field_info.getfieldname(i) == "SHAPE":
field_info.setvisible(i, 'VISIBLE')
elif field_info.getfieldname(i) == "Shape":
field_info.setvisible(i, 'VISIBLE')
else:
field_info.setvisible(i, 'HIDDEN')
arcpy.SaveToLayerFile_management(LayerNeedsFieldsTurnedOff, out_layer)
arcpy.MakeFeatureLayer_management(LayerNeedsFieldsTurnedOff, 'temp_layer', '', '', field_info)
refLyr = arcpy.mapping.Layer('temp_layer')
# rename the ref layer the same as your target layer
refLyr.name = LayerNeedsFieldsTurnedOff.name
lyrfile = "C:/temp/temp.lyr"
print LayerNeedsFieldsTurnedOff.name
arcpy.ApplySymbologyFromLayer_management(refLyr, lyrfile)
arcpy.mapping.UpdateLayer(df, LayerNeedsFieldsTurnedOff, refLyr, False)
# mxd.save()
print 'cleaning up-'
if arcpy.Exists('temp_layer'):
print '\'temp_layer\' still in memory...deleting now...'
arcpy.Delete_management('temp_layer')
print 'deleting obj refs...'
del LayerNeedsFieldsTurnedOff, refLyr, fields
print 'done.'
I am trying print statements
Regarding the comment in your code around line 8:
# need to find way to get list of layers there can be more than one data frame
Consider the following:
>>> mxd = arcpy.mapping.MapDocument('CURRENT')
>>> for df in arcpy.mapping.ListDataFrames(mxd):
... print df.name
... for layer in arcpy.mapping.ListLayers(mxd, data_frame=df):
... print layer.name
...
DataFrame1
LayerInDF1
DataFrame2
LayerInDF2
>>> for layer in arcpy.mapping.ListLayers(mxd):
... print layer.name
...
LayerInDF1
LayerInDF2
>>>
Your use of ListLayers without specifying a data_frame should process all layers in all data frames. You may need to activate the data frames as you loop through them.
Dan, are you using multiple data frames in your map document? I think your code might be making it through the active data frame and getting hung up in an inactive data frame. It seems that some elements of a layer don't exist (or can't be accessed) if they are in an inactive data frame. I've been testing the following code in 10.5 without error. But the map document becomes a bit unstable until I manually activate the first data frame.
mxd = arcpy.mapping.MapDocument('CURRENT')
for df in arcpy.mapping.ListDataFrames(mxd):
mxd.activeView = df.name # data frames should have unique names
print df.name
for layer in arcpy.mapping.ListLayers(mxd, data_frame=df):
if layer.isGroupLayer:
print "Group Layer: {}".format(layer.name)
elif layer.isFeatureLayer:
print "Feature Layer: {}".format(layer.name)
fields = dict((f.name, []) for f in arcpy.ListFields(layer) if not f.required)
for row in arcpy.SearchCursor(layer):
for f in fields.keys():
fields[f].append(row.getValue(f))
del row
print fields
del layer
del df
del mxd
Part of what this does is specify a data frame in ListLayers (line 6). Since this active view is set by using a data frame name, it is important that all data frames have unique names. When fields is printed (line 17) it appears as I would expect. The code above may not be switching active views in the most efficient manner, but I have not seen any examples where it is done differently.
I had previously thought that the problem might have been with SearchCursor needing a field list, but it appears that it defaults to a list of all fields (with some exceptions).
The script is still failing at line 21 the more than two data frame is just incase. I will try that and see if it helps.
If you print the "fields" variable between lines 17 and 18, what do you get? Something like:
{u'Field1': [], u'Field2': [], u'Field3': [], u'Field4': []}
your commented out print statement should be before line 21 if it is failing on line 21
thank you
I was able to generate the exact same error as the one you mentioned in a previous post by adding a nonexistent field to the fields dictionary:
fields = dict((f.name, []) for f in arcpy.ListFields(layer) if not f.required) # your line 17
fields['bull'] = [] # nonexistant field
When using a wild card for the field list in SearchCursor (or not specifying a field list), SearchCursor will return all fields with a few exceptions (raster and BLOB fields are excluded). You will need to exclude these fields when you create your dictionary. This code might work to exclude rasters and blobs from the fields dictionary:
fields = dict((f.name, []) for f in arcpy.ListFields(layer) if not (f.required or f.type == "Blob" or f.type=="Raster"))
Another option would be to create a try/except block around lines 20-21, perhaps:
for f in fields.keys():
try:
fields.append(row.getValue(f))
except:
print "{} not in field list".format(f)
If these suggestions fail, you should print the fields dictionary and compare it to field list in your feature.
Thank you I will try this