How to delete a layer from GH?

I would like to delete some layers and the objects on those layers in Rhino from GH. They were previously generated by Human. I know there is a “Delete on Layer” component, but that only deletes objects and doesn’t delete in sublayers.

So what I have are the layer paths to delete in the typical format of “Layer::Sublayer::etc.”

Of course I am aware of the usual warnings.

Is there a way to do it with Python or C#? I found this https://developer.rhino3d.com/api/RhinoCommon/html/M_Rhino_DocObjects_Tables_LayerTable_Purge.htm, which I think is the method to delete a layer and its contents, but not sure how to get the GUID of a layer based on its path and how to plug it all together.

Thanks.

2 Likes

Have you tried Purge?

I’m using the following code to delete curves on a certain layer:

import rhinoscriptsyntax as rs
import scriptcontext
import Rhino

scriptcontext.doc = Rhino.RhinoDoc.ActiveDoc

crv=rs.ObjectsByLayer('panel', False)

if crv: rs.DeleteObjects(crv)

scriptcontext.doc = ghdoc

Thanks, yes, Purge is the correct command, but I just gave it a small go and it seems it doesn’t work as I would hope.

Firstly I dont really like using Rhino commands triggered from GH. They usually cause trouble, especially when using in loops. GH just gets stuck a lot, because a Rhino command is stuck somewhere. So I much prefer how for example Human does it, where its not triggering Rhino commands.

But yeah, anyways, I didn’t get it to work anyways. It seems that it doesn’t work with Full layer paths in the format “Layer::Sublayer::Sublayer”

Thank you though.

This is what I am trying (with my limited knowledge of python and rhino script) but its not working right yet:

I’m not a programmer but the code below works.

In your code, you do not need the layer = rs.GetString … that would be for a Python script inside Rhino I believe

Yeah, I think its more my limited knowledge of python :sweat_smile: I’ll play around some more.

Oh, actually it totally works. Just didn’t know that Python doesn’t understand “true” and instead expects a 1. And I still had the layer = rs.GetString(“Layer to purge”) in there from the documentation, which I now realise is the prompt for the layer in the Rhino command line, so I just had to get rid of that.

Now its working fine with:

import rhinoscriptsyntax as rs
import scriptcontext
import Rhino

scriptcontext.doc = Rhino.RhinoDoc.ActiveDoc

if x == 1:
   if layer: rs.PurgeLayer(layer)

scriptcontext.doc = ghdoc

I have a True only button connected to x (type hint: boolean) and a “layer” input (type hint: str).

Woop woop! Thanks @martinsiegrist

Edit: Just noticed you also gave the same input at the same time. :slight_smile:

1 Like

I had to get rid of the solution again as I think there is a bug or wrong documentation for “Purge Layer”.

It works fine with empty layers, but just silently fails if any of the sublayers contain geometry.

From the documentation at https://developer.rhino3d.com/api/RhinoScriptSyntax/#layer-PurgeLayer:

You can see that in the explanation it says the layer is deleted even if it contains geometry, but in the layer input it says the layer needs to be empty and thats also the observed behaviour.

So somewhere there is a #bug there.

Seems like someone just copy/pasted in the documentation and in the code behind it. Here is the documentation for DeleteLayer and you can tell the parameter has the same description:

1 Like

Well in the worst case you can delete geometry and the purge empty layers.

True, but I think that is what Purge is for, right? To delete geometry you have to specify the exact layer you want to delete the objects from, so that means you have to first find and then pass it all sublayers just to be able to delete the parent layer.

Purge should be able to delete a parent layer with all the sublayers and objects without specifying them. I think someone just copied the deleteLayer function and forgot to change it.

I’ll post this again as a bug report.

purgerecursively.gh (5.7 KB)

using Rhino.DocObjects;
using Rhino.DocObjects.Tables;

public class Script_Instance: GH_ScriptInstance {

  private void RunScript(bool ok, string layerName) {
    if (!ok) return;

    var layerTable = RhinoDocument.Layers;
    var layerId = layerTable.FindByFullPath(layerName, -1);
    if (layerId == -1) return;

    PurgeRecursive(layerTable, layerTable[layerId]);
  }
 
  private void PurgeRecursive(LayerTable lt, Layer parent) {
    foreach(var it in parent.GetChildren() ?? Array.Empty<Layer>())
      PurgeRecursive(lt, it);

    lt.Purge(parent.Id, true);
  }
}

Wow, that looks pretty cool.

So PurgeLayer layer also still has to first delete everything in the sublayers? From the description, I was under the impression, that it wouldn’t need that. So then whats the difference to DeleteLayer? I was assuming PurgeLayer is like selecting a parent layer in Rhino, pressing Del and then clicking “Yes to All”!?

As far as I understand Purge does not delete geometry. It rather cleans your file from stuff that’s not needed. I’ve been using the command regularly in Rhino to delete empty layers, unused block definitions and unused materials.

I think you are confusing it with the “Purge” command in Rhino. This is a RhinoScript command called “PurgeLayer” and from the description should be able to take a layer and delete it, regardless of sublayers and objects in the sublayers. Probably if you would have a Block on any of the Sublayers it wouldn’t let you do it, but that’s not an issue in my case.

Can one of the devs please confirm what the correct way to delete stuff in RhinoScript is for:

  1. Deleting a layer with all its contents
  2. Deleting a layer only if its empty (I guess “DeleteLayer”)
  3. Deleting only the contents of a layer
1 Like

I’m not sure if this helps at all, but it might clarify things a bit. rs.PurgeLayer simply wraps this method:

def PurgeLayer(layer):
    """Removes an existing layer from the document. The layer will be removed
    even if it contains geometry objects. The layer to be removed cannot be the
    current layer
    empty.
    Parameters:
      layer (str|guid): the name or id of an existing empty layer
    Returns:
      bool: True or False indicating success or failure
    Example:
      import rhinoscriptsyntax as rs
      layer = rs.GetString("Layer to purge")
      if layer: rs.PurgeLayer(layer)
    See Also:
      AddLayer
      CurrentLayer
      DeleteLayer
      RenameLayer
    """
    layer = __getlayer(layer, True)
    rc = scriptcontext.doc.Layers.Purge( layer.LayerIndex, True)
    scriptcontext.doc.Views.Redraw()
    return rc

While rs.DeleteLayer wraps this one:

def DeleteLayer(layer):
    """Removes an existing layer from the document. The layer to be removed
    cannot be the current layer. Unlike the PurgeLayer method, the layer must
    be empty, or contain no objects, before it can be removed. Any layers that
    are children of the specified layer will also be removed if they are also
    empty.
    Parameters:
      layer (str|guid): the name or id of an existing empty layer
    Returns:
      bool: True or False indicating success or failure
    Example:
      import rhinoscriptsyntax as rs
      layer = rs.GetString("Layer to remove")
      if layer: rs.DeleteLayer(layer)
    See Also:
      AddLayer
      CurrentLayer
      PurgeLayer
      RenameLayer
    """
    layer = __getlayer(layer, True)
    return scriptcontext.doc.Layers.Delete( layer.LayerIndex, True)
1 Like

Thank you. But that IS what I am trying and what is not working:

purge_not_working.gh (8.9 KB)
purge_not_working.3dm (2.8 MB)

Clicking the button does nothing for the list of layers or even the Test layer.

If you create an empty layer and even add some sublayers and then pass just the Parent Layer to the phython component it works and deletes the layer and all its sublayers. But as soon as there is an object in any of the sublayers it simply does nothing.

If you duplicate the phython component and replace “PurgeLayer” with “DeleteLayer” the behaviour is identical. Therefore I think in the code behind the PurgeLayer method, someone just copied the DeleteLayer method and forgot to remove the flag about containing geometry.

Yes this can be a bit tricky. I’d probably change the logic and instead operate on the full layer paths.

Something like this might be a start:

201119_purge_not_working_AHD_01.gh (6.5 KB)

1 Like

Thanks @AndersDeleuran.

It’s working right now, but still very slow. It does something on every calculation even when Purge is not true (its connected to a button to only trigger when needed).

Any idea what could cause GH to take so much longer for every calculation even though its not supposed to do anything?

Also then this shows that PurgeLayer doesn’t do what it says in the description, ie. delete any layer including its sublayers and objects if you have to delete all the child layers first and sort of work through it recursively.

So the question remains: is the description wrong or does the method have a bug?

It’s probably a combination of using the implied GHPython looping and changing the scriptcontext before checking if Purge is True. Here’s a quick go at fixing those issues:


201120_purge_not_working_AHD_03.gh (9.6 KB)

1 Like

Hi all,
I’m looking to Purge only the empty sub-layers under a specific layer.
So I need the script to read all the layers under a given layer name, and delete only the empty ones.
I have limited knowledge of scripting, so I’ll appreciate your help here if possible.
Many thanks!