How To Update Layer Parent

I’m looking at this code snippet.

I want to change a layer’s parent id:

Layer layerToChange = findLayer();
layerToChange.ParentLayerId = new_parent_guid;
layerToChange.CommitChanges(); //method does not exist

But CommitChanges() doesn’t exist?

@EricM

https://developer.rhino3d.com/api/rhinocommon/rhino.docobjects.layer/commitchanges

deprecated in 6.0
obsolete: No longer needed. Layer changes in the document are now immediate

3 Likes

Without a commit statment, how do you know if the update was successful? Say if there is a name collision with another layer under that parent? Or if it can’t change because it’s tied to a block definition?

@EricM

I remember I checked the existance of the particular layer like this before changing the parent.
System.Int32 FindByFullPath(System.String layerPath, System.Int32 notFoundReturnValue)

https://developer.rhino3d.com/api/rhinocommon/rhino.docobjects.tables.layertable/findbyfullpath#(string,int32)

I am not sure this is the most elegant solution though.
Might be interesting to see how this can be done better.

Hi @EricM,

A good workflow is this:

  1. Get the layer
  2. Make a copy of
  3. Modify the copy
  4. Call LayerTable.Modify

For example:

var newLayer = new Layer();
newLayer.CopyAttributesFrom(oldLayer);
newLayer.Name = newName;
rc = doc.Layers.Modify(newLayer , oldLayer.Id, true);

Bonus points for checking to see if a layer with newName exists before trying to modify.

– Dale

1 Like

Yeah, I’m doing a move or merge routine, but I don’t know how many other ways it could fail. I thought of two, but I’m guessing there’s more.

I found two problems when modifying layers. I think they are all related to the layers panel/eto.

The first is a hard crash. I sent in the crash report, and it’s 100% reproducible with a 3dm (let me know where to send it). This code snippet works with certain layers but fails with others (see comments). This is the actual F# that is crashing, but I’m happy to translate it to C# (it’s fairly straightforward).

let nameP (s1: string)  (s2: string) : bool =  
    System.String.Compare(s1, s2, System.StringComparison.InvariantCultureIgnoreCase) = 0
    
let printLayer (layer: Layer): string =  
    layer.FullPath.Replace("::", "->")
    
let setLayerVisible (layer: Layer) = 
    // success
	// let targetName = "control"
    // let viewP = false
	
	// success
	// let targetName = "junk"
    // let viewP = false
	
	// 100% reproducible hard crash
	let targetName = "crvs"
    let viewP = true
    
    if (nameP layer.Name targetName) then
        let a = new Layer()
        a.CopyAttributesFrom(layer)
        a.IsExpanded <- true //trying to debug 2nd issue
        a.IsVisible <- viewP
        let resP = rs.Doc.Layers.Modify(a, layer.Index, false)
        if resP = true then  
            Rhino.RhinoApp.WriteLine($"Updated layer {printLayer layer}") 
            
        else
            Rhino.RhinoApp.WriteLine($"ERROR: Could not updated layer {printLayer layer}")
            
        resP
    else true
    
rs.Doc.Layers |> Seq.forall setLayerVisible

I got two different errors around this code. One was a threading exception that said something to the effect of ‘calling thread couldn’t do something because another thread owned the resource’. Another time it crashed, I was able to grab this message before it bailed:

[ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: An ItemsControl is inconsistent with its items source.
  See the inner exception for more information. ---> System.Exception: Information for developers (use Text Visualizer to read this):
This exception was thrown because the generator for control 'Eto.Wpf.Forms.Controls.EtoDataGrid Items.Count:58' with name '(unnamed)' has received sequence of CollectionChanged events that do not agree with the current state of the Items collection.  The following differences were detected:
  Accumulated count 54 is different from actual count 58.  [Accumulated count is (Count at last Reset + #Adds - #Removes since last Reset).]

One or more of the following sources may have raised the wrong events:
     System.Windows.Controls.ItemContainerGenerator
      System.Windows.Controls.ItemCollection
       System.Windows.Data.ListCollectionView
  *     Eto.Wpf.Forms.Controls.EtoGridCollectionView
(The starred sources are considered more likely to be the cause of the problem.)

Second, when it doesn’t crash Rhino, the layer’s panel doesn’t reflect the current state. I can see geometry turning on and off as expected in the viewport. The actual layer’s view state is being changed. However, deeply nexted and collapsed layers are not being updated in the layers panel. When I drill down into the layer tree, it shows a layer as being visible when I know it’s off.

Expanded layers are updated properly. If the grandparent layer is expanded and the parent layer is collapsed, the layer updates properly. If the grandparent and parent are both collapsed, then the state in the layers panel fails to update.

1 Like

You do know the Rhino documents is not thread safe, right?

If you can crash with a C# sample, we’d like to see it.

Thanks,

— Dale

1 Like

There’s nothing in that code that does any threading. C#'s async (and everything else added to the language in the last 10+ years) comes from F#. It’s kind of like how cpp is getting a borrow checker a la Rust.

I interpreted the hard crash and error messages (threading and eto) as Rhino using a worker to paint the UI and assumed a cpp foot gun was in play. I have since learned that Fesh defaults to running scripts via async. I missed that and the tooltip that says UI interactions should be run in sync.

I’m asking why it defaults to async, especially if the doc and UI are not thread-safe.

1 Like

@EricM Yes, the Rhino Document is not thread safe. That is why Grasshopper and Rhinopython modify the Document from the UI thread only. However, I found that a worker thread can also modify the document without issues as long as it is only one thread that modifies the document. Anything that affects the UI (like adding layers) must be done from an UI thread though! When you are using my Fesh editor you can choose at the bottom right the safe and synchronous option. This will also block the UI while running code. In my libary Rhino.Scripting all UI calls do a context switch if they are async. So it should be safe to be used from a worker thread that does not block the UI. Please upload a file to reproduce the bug with the above code.

See the continuation of the discussion at: Could not do something; another thread owns xxx · Issue #3 · goswinr/Fesh.Rhino · GitHub

see also this thread Rhino 8.9 layer table window not updating and crashing - Rhino Developer - McNeel Forum

Rhino (well the ETO layer Table) became signifantly less tolerant of cross thread document updates as of 8.9 :frowning: