I created a tool in ArcGISPro 3.1.2 which intends to copy layers to already existing Group Layer:
(Layers FC1 and FC2 should be copied to GL1).
Script tools has one paramter (Data Type = Group Layer obviously);
The script code is as following:
import arcpy
def script_tool(gl):
"""Script code goes below"""
m = arcpy.mp.ArcGISProject("current").activeMap
arcpy.AddMessage(f"I'm an object: {type(gl)}") #<class 'arcpy._mp.Layer'>
#gl=m.listLayers("GL1")[0] #if uncommented script will work well
#arcpy.AddMessage(f"I'm an object: {type(gl)}")
for l in m.listLayers():
if l.isFeatureLayer:
arcpy.AddMessage(f"Adding {l.name} to {gl.name}")
m.addLayerToGroup(gl,l,"BOTTOM")
if __name__ == "__main__":
param0 = arcpy.GetParameter(0) #read as object, not value
script_tool(param0)
The script fails with following information:
File "E:\GIS_DATA\ArcGIS\WIW\WIW.atbx#MoveLayers_WIWatbx.py", line 23, in script_tool
File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\utils.py", line 191, in fn_
return fn(*args, **kw)
File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\_mp.py", line 2593, in addLayerToGroup
return convertArcObjectToPythonObject(self._arc_object.addLayerToGroup(*gp_fixargs((target_group_layer, add_layer_or_layerfile, add_position), True)))
ValueError: <MappingLayerObject object at 0x0000019CBBEBB270>
If I uncomment line 7 and get GroupLayer object "manually" the script will work ok.
So why it doesn't work when GroupLayer object is taken from parameter?
BTW - I'm aware that I could do some workaround like
new_gl=m.listLayers(gl.name)[0] #gl is object from script tool parameter
which would work as long as there are no duplicate names of Group Layers in TOC. Still I'd be greatful if someone can explain to me what going on behind the scenes.
Not sure why the addlayerToGroup doesnt know what to do with the Layer object since that is what it is expecting as input but the addlayerToGroup method doesn't know what to do with it. Maybe GetParameter overrides things and it isnt a true Layer Object.
Edit- just saw the statement about uncommenting. So disregard this:
Uncommenting the line 7 and pass in the gl's name property and it works like you said.
def script_tool(glayer):
...
gl = m.listLayers(glayer.name)[0]
The issue is there isn't a lot to get from describing the selected group layer:
I'm an object: <class 'arcpy._mp.Layer'> Group 1
areaOfInterest: -157.834681122836 19.7086074727567 -67.8299529846581 64.8231249486575 NaN NaN NaN NaN
children: [<geoprocessing describe data object object at 0x00000234B9B9D4B0>, <geoprocessing describe data object object at 0x00000234B9B9D830>]
dataElement: None
dataType: GroupLayer
nameString: Group 1
DSID: -1
FIDSet: 0;
Trying to get another group with the same doesn't seem to work. since 1:2 changes back to 1:1.
---
This will duplicate layers in the group if the layer is already part of the group. I added some checks to stop from adding them if they are part of the group and added a line to remove the layers once moved into the group so they are not loose in the TOC. There was a longName method for arcmap, but that seems to have gone away so the only way I could tell if the layer is in a group or not was to count the layers children. If it had more than 0, it is a group and you can use list comp to get the names of the items in the group. Rasters had a child so that is why I excluded it by dataType on line 9. You can probably improve the logic here.
def script_tool(glayer):
"""Script code goes below"""
m = arcpy.mp.ArcGISProject('current').activeMap
gl = m.listLayers(glayer.name)[0] # if uncommented script will work well
arcpy.AddMessage(f"I'm an object: {type(glayer)} {gl.longName}") #<class 'arcpy._mp.Layer'>
lyrs_in_group = []
for lyr in m.listLayers():
desc = arcpy.Describe(lyr)
if len(desc.children) > 0 and desc.dataType != 'RasterLayer':
lyrs_in_group = [l.baseName for l in desc.children]
arcpy.AddMessage(lyrs_in_group)
elif all([lyr.name not in lyrs_in_group, not lyr.isGroupLayer, lyr.isFeatureLayer, desc.dataType != 'RasterLayer']):
arcpy.AddMessage(f"Adding {lyr.name} to {gl.name}")
m.addLayerToGroup(gl, lyr, "BOTTOM")
m.removeLayer(lyr)
else:
arcpy.AddMessage(f"Nope {lyr.name}")
Thanks again for your time.
1) The code I used in example doesn't necessary make any valid things because I created it only to highlight the issue. My real challenge was to find empty layers (or more strictly speaking layers representing empty feature classes) and move them to exsiting Group Layer. I designed a tool with Group Layer parameter and spent some time thinking why it doesn't move the empty layers to that Group Layer even though Group Layer paramter returns an object (in ArcGIS Pro 2.8 it didn't even crashed -it just silently did nothing. Fortunately ArcGIS Pro 3.1 explicitly crashes). And since I found a workaround I only hoped here for an explanation of this rather strange behaviour (just another bug or I am missing something). So once again to make it clear - my question can be shortened to "why Group Layer object returned from tool parameter isn't valid object"
2) I do agree with you that my code would duplicate Layers if they were already in Group Layer. My real script works better but it's also much more complicated and would be more difficult to read.
3) I have trouble understading part of your post quoted below:
@StaticK wrote:The issue is there isn't a lot to get from describing the selected group layer:
I'm an object: <class 'arcpy._mp.Layer'> Group 1
areaOfInterest: -157.834681122836 19.7086074727567 -67.8299529846581 64.8231249486575 NaN NaN NaN NaN
children: [<geoprocessing describe data object object at 0x00000234B9B9D4B0>, <geoprocessing describe data object object at 0x00000234B9B9D830>]
dataElement: None
dataType: GroupLayer
nameString: Group 1
DSID: -1
FIDSet: 0;
Trying to get another group with the same doesn't seem to work. since 1:2 changes back to 1:1.
4) Your code is a nice and elegant way to avoid duplicating layers but it has funny issue. If there is empty destination Group Layer on top of TOC or any other empty Group Layer anywhere in TOC then arcpy.Describe(EmptyTopGroupLayer).children will crash. hasattr(desc, 'children') check has to be added.
Also IMHO FeatureLayer cannot be GroupLayer so I guess that condition "not lyr.isGroupLayer" is redundant in line 13.