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?
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)
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.
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.
@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.