if layer.isGroupLayer: pass # skip else: #replace data path
import arcpy mxd = arcpy.mapping.MapDocument(r"C:\Project\Project.mxd") for lyr in arcpy.mapping.ListLayers(mxd): if lyr.supports("DATASOURCE"): if lyr.dataSource == r"C:\Project\Data\Parcels.gdb\MapIndex": lyr.findAndReplaceWorkspacePath(r"Data", r"Data2") mxd.saveACopy(r"C:\Project\Project2.mxd") del mxd
This was just what I was looking for. Well commented too. Great work!
Hi
I am publishing this example script even though the question is marked answered because as a novice python programmer I needed the full complete picture to help me understand what is happening. It's true that the issue above is that the group layer file cannot be treated as the individual layer files. The answers above addressed that issue - I just had a hard time understanding how to handle it differently. In this script, the group layer file is identified and then the layers inside it are handled as a new layer object.
This is the script I used to process a directory of folders and layer files numbering over 700 when we switched SDE servers.
The script fails on some things (it doesn't like representations, and a few other odd ducks) but it processed well over 90% just fine, and that was good enough for us. The remaining will be dealt with individually.
This script also incorporates logging which reports actions to the screen and to a log file, so that you have a record of what was processed and what failed. I then wrote another script which read the log file and pull out into a summary page what failed. That is optional obviously.
I do not take credit for coming up with this myself. I found another post at gis.stackexchange.com that got me quite far by Tim Barnes, also located here: replaceDataSource for layers within a group layer
The zip file attached contains the logging config file referenced in the script below. "Logging" is a standard python module.
#!/usr/bin/env python #******************************************************************************************************************** # Name: FixLayerFileSourcePaths.py # Created: 3/10/2016 # Version: ArcGIS 10.4 # Author: Ann Stark, City of Bellingham # Modifications: # Dependencies: logging config file # Description: Reads a directory and all sub directories, replacing the source data path for layer files. # In the example below an SDE connection was being moved from one server to another server. # Richmond # Called by: # Arguments: None #********************************************************************************************************************* # to use this script # 1. copy this script and the logging config file to a location # 2. modify the logging file config path to point to your config file. # 3. modify the "sourceString" and "replacementString" variables to be the old and new paths you want to replace. # 4. modify the "folder" variable to be the highest level folder that you want to search for .lyr files. This script runs through the folder listed and all folders within it. #********************************************************************************************************************* import time tic = time.clock() import os, sys, string, logging import arcpy import arcpy.mapping from arcpy import env import logging import logging.config # Where is the logging config file. logging.config.fileConfig(r"N:\scripts\arcgis\speedsongPathFixes\logging\logging.conf", disable_existing_loggers=False) #modify this path to point to your config file. logging.info('--> STARTED Script') # Folder- the folder where you want to search for the layer files folder = r"N:\LayerFiles_Speedsong\water utilities" #directory to search for lyr files logging.info('PROCESSING {}'.format(folder)) #folder = arcpy.GetParameterAsText(0) sourceString = r"Richmond_" #oldpath replacementString = r"Speedsong_" #newpath logging.info("Replacing {} with {}".format(sourceString, replacementString)) for path, dirs, files in os.walk(folder): #walk through all sub folders logging.info(" Now processing {}".format(path)) arcpy.env.workspace = path try: for layerFile in arcpy.ListFiles("*.lyr"): try: # find the actual layer files in the folder lyr = arcpy.mapping.Layer(path + "\\" + layerFile) # make layer object from layer file for lyrObj in arcpy.mapping.ListLayers(lyr): try: # a layer file may have multiple layers within it so work through those... if lyr.isGroupLayer: #logging.info(layerFile + " is a group layer.") #arcpy.AddMessage(layerFile + " is a group layer.") #arcpy.AddMessage(lyrObj.longName +":"+ lyrObj.name) if lyrObj.name.find('Build') >= 0: pass #this skips our buildings representation elif lyrObj.longName !=str(lyr.name): # just update the layers within the group layer #arcpy.AddMessage(layerFile + " contains a layer object named: " + lyrObj.name) logging.info(layerFile + " contains a layer object named: " + lyrObj.name) newWS = lyrObj.workspacePath.replace(sourceString, replacementString) newSource =lyrObj.dataSource.replace(sourceString, replacementString) if arcpy.Exists(newSource): #arcpy.AddMessage("Updating " + lyrObj.name + " with replacement GDB.") #lyrObj.replaceDataSource(newWS, "FILEGDB_WORKSPACE") lyrObj.findAndReplaceWorkspacePath(sourceString, replacementString, True) lyr.save() logging.info(" Updated " + lyrObj.name) else: logging.info(newSource + " DOES NOT exist. Will not update this layer.") elif lyr.isGroupLayer == 0: newWS = lyr.workspacePath.replace(sourceString, replacementString) newSource = lyr.dataSource.replace(sourceString, replacementString) if arcpy.Exists(newSource): logging.info("Updating {} with replacement source path.".format(lyr.name)) #lyr.replaceDataSource (newWS, "FILEGDB_WORKSPACE") #use this when changing source types (shp to fgdb for example.) lyrObj.findAndReplaceWorkspacePath(sourceString, replacementString, True) lyr.save() else: logging.info(newSource + " DOES NOT exist. Will not update this layer.") except Exception as e: logging.error(str(e)) except Exception as e: logging.error(str(e)) except Exception as e: import traceback map(arcpy.AddError, traceback.format_exc().split("\n")) arcpy.AddError(str(e)) logging.error(str(e)) del arcpy, os, sys, lyr logging.info("--> FINISHED script") toc = time.clock() ElapsedTime = round((toc - tic)/60, 2) logging.info(" ElapsedTime is {0} minutes\n\n".format(ElapsedTime)) # uncomment the last two lines if you want to open the log file automatically after the script runs. # change the path to your log file. #import webbrowser #webbrowser.open(r"c:\your\path\to\the\SpeedsongPathFix.log")
Thanks Ciaran, i tried your suggestion
As soon as it hits Group Layer, it prints below and exits.
"
MainLayer.lyr is a group layer
"Layer1" is a layer inside MainLayer.lyr
name: Layer1
long name: Layer1
Layer1 is a single layer
Error: Layer: Unexpected error
i am pretty sure you are getting that error because you are trying to save layer when in fact the object you have at the point is not a layer - but is in fact a layer within a group layer. You need to be saving the group layer
the below change might work.........
#This comparison test for layers in a group layer vs those not in. The .longName property includes all group layers as part of path if singlelayer.name != singlelayer.longName: outputFile.write(pathfull + "\t" + singlelayer.longName + "\t" + singlelayer.name + "\t" + dataSourceOrig + "\t" + dataSourceNew + "\t" + "GROUP LAYER SOURCE" "\n") singlelayer.findAndReplaceWorkspacePath(sourceString, replacementString, True) print "%s group source changed" % singlelayer #singlelayer.save()
layer.save() print "%s saved" % singlelayer
I am having the same issue. Is this a new bug that a previously working script now fails?
I'm having the exact same issues and cannot figure out why I keep getting the error 'no filename set'. It is discouraging that ESRI would allow you to create group .lyr files but not allow you to modify them the same way.
I am also attempting to re-source a bunch of layer files, some of which are group layers, in a folder. I started with the same script found above, which the OP also posted to GIS Stack Exchange and someone there added a recursive function to parse through nested group layers. Seemed like the perfect solution. Except the script errors out as soon as it hits a group layer. More specifically: as soon as it tries to save the first layer within a group layer, it returns an error: "No filename set". I believe this is because the .name and .longName attributes of the layer variable are the same even though they shouldn't be if the layer is inside a group layer. Thus, the conditional statement on line 27 (if singlelayer.name != singlelayer.longName:) is never met and all layers move on to "else". Something in this script is causing .name and .longName to become identical because they are just fine if I check for them in a separate, simpler script. I am using ArcGIS 10.2, but I believe the original script was written for 10.0. Any ideas what could be causing this?
import arcpy, datetime, os, io, sys def walknestedgroups(layerlist,srcString,repString,pathfull,outputFile): if layerlist.isGroupLayer: #Cycle through grouplayer print "%s is a group layer" % layerlist for layer in layerlist: print "%s is a layer inside %s" % (layer,layerlist) print "name: " + layer.name print "long name: " + layer.longName #This will continue to cycle through and test for group layers walknestedgroups(layer,srcString,repString,pathfull,outputFile) else: print "%s is a single layer" % layerlist #Once a non-group layer is returned, head through the rest of original script singlelayer = layerlist if singlelayer.supports("DATASOURCE"): dataSourceOrig = str(singlelayer.dataSource) if dataSourceOrig.find(srcString) >= 0: dataSourceNew = dataSourceOrig.replace(srcString, repString) #This comparison test for layers in a group layer vs those not in. The .longName property includes all group layers as part of path if singlelayer.name != singlelayer.longName: outputFile.write(pathfull + "\t" + singlelayer.longName + "\t" + singlelayer.name + "\t" + dataSourceOrig + "\t" + dataSourceNew + "\t" + "GROUP LAYER SOURCE" "\n") singlelayer.findAndReplaceWorkspacePath(sourceString, replacementString, True) print "%s group source changed" % singlelayer singlelayer.save() print "%s saved" % singlelayer else: outputFile.write(pathfull + "\t" + "Non Group Layer" + "\t" + singlelayer.longName + "\t" + dataSourceOrig + "\t" + dataSourceNew + "\t" + "SINGLE LAYER SOURCE" "\n") singlelayer.findAndReplaceWorkspacePath(srcString, repString, True) print "%s source changed" % singlelayer singlelayer.save() print "%s saved" % singlelayer return layerlist print "loop exited for " + layerlist + " group layer" try: #Read input parameters from GP dialog # Prod Setting... folderPath = r"G:\JThomas\LayerFiles\Vector\REAL_PROPERTY" output = r"G:\JThomas\LayerFiles\logfile.log" sourceString = "" replacementString = r"P:\LE\Connections\LE_CAC @ CLJN.sde" #Create an output file outFile = open(output, "w") #Loop through each "LYR" file count = 0 for path, dires, files in os.walk(folderPath): # for filename in os.listdir(folderPath): for filename in files: # fullpath = os.path.join(folderPath, filename) fullpath = os.path.join(path, filename) if os.path.isfile(fullpath): if filename.lower().endswith(".lyr"): groupLayerName = "" #Reference LYR layerFile = arcpy.mapping.Layer(fullpath) #Iterate through layer list and all group layers. Return single layer for totallayer in arcpy.mapping.ListLayers(layerFile): #This function will take each layer in the .lyr file, cycle through any group layers then apply path changes to individual layers singlelayer = walknestedgroups(totallayer,sourceString,replacementString,fullpath,outFile) del layerFile outFile.close() #Open the resulting text file os.startfile(output) #Delete variables that reference data on disk del outFile except Exception, e: import traceback map(arcpy.AddError, traceback.format_exc().split("\n")) arcpy.AddError(str(e)) print "Error: %s" % e
Edit: not sure what the deal is with the code block. It looked fine in the preview and now it's all one line. I've attached it as a separate file. Sorry for any inconvenience viewing it!
import arcpy try: mxd = arcpy.mapping.MapDocument(r"C:\test\mymap.mxd") layers = arcpy.mapping.ListLayers(mxd) for layer in layers: if not layer.isGroupLayer: if layer.supports("DATASOURCE"): print layer.dataSource except: print "Exception ...."
import arcpy lyr = arcpy.mapping.Layer(r"C:\Temp\Group1.lyr") #Nested group layer file for layer in lyr: print layer.dataSource
Traceback (most recent call last): File "<interactive input>", line 2, in <module> File "C:\Program Files (x86)\ArcGIS\Desktop10.0\arcpy\arcpy\arcobjects\_base.py", line 70, in _get return convertArcObjectToPythonObject(getattr(self._arc_object, attr_name)) AttributeError: LayerObject: Get attribute dataSource does not exist
if lyr.supports("DATASOURCE"):
Angemeldete Mitglieder können Beiträge verfassen, Updates folgen und mehr. Neu hier? Registriere ein kostenloses Konto.
Find useful guides, FAQs, and documents to help you navigate and make the most of Esri Community.