I am trying to somewhat replicate the Grasshopper “Explode Tree (Bang)” component with ghpython component (0.6.0.3, Rhino 5 SR12 32 bit, Grasshopper 0.9.0076).
I am looking for the same functionality that “Explode Tree (Bang)” component has, but with a two slight differences:
The output parameters will be automatically created, instead of manual creation used by the “Explode Tree (Bang)” component (user needs to click on the “+” sign to create).
The component would only create output parameters, if _runIt input is set to True. Otherwise all output parameters will be deleted, except for the “output” one.
I have two issues:
After the outputs are removed, the following error message emerges:
Output parameter Index[i] too high or too low for Component Python Script
It is repeated for the number of output parameters - 1 times (because I am never removing the “output” parameter).
I am not sure if it is caused by the ghenv.Component.Params.Output.RemoveRange method on line 21.
So I tried to remove the output parameters one by one instead:
#ghenv.Component.Params.Output.RemoveRange(1, numberOfOutputs-1)
outputParamsIndices = range(1, ghenv.Component.Params.Output.Count) # never include "output"
outputParamsIndices.reverse() # remove the parameters from the end
for index in outputParamsIndices:
ghenv.Component.Params.UnregisterOutputParameter(ghenv.Component.Params.Output.Item[index])
ghenv.Component.Params.OnParametersChanged()
But again the upper error message remained.
I can solve this by checking the “Do not show this message again” check box in the lower left corner. But still I would have to do this each time Rhino is restarted. It’s a bit annoying.
Second issue is that, once output parameters are removed, the wires which connected those output parameters to the panels, remain glitchy. Here is an explanation in screenshots:
Component’s _runIt input set to False:
Component’s _runIt input set to True, and output parameters connected to panels manually:
Component’s _runIt input set to False. The wires remain glitchy:
Btw. the inputData contains a tree with three branches
I would welcome any kind of help.
Component is attached below.
Without further checking or trying it seems to me that you shouldn’t be unregistering parameters like that in a loop. You’re essentially changing the list into which you have the (original) indices pointing.
Instead I’d make a loop that runs until only the one output remains. On each iteration remove the element at the highest index, signal for change in parameters. Rinse and repeat.
I can see you did some pretty amazing plugins, so my level of knowledge is definitively far lower than yours.
Still, no change of the outputParamsIndices list is happening during the for loop.
And even if it would be happening, it is not significant due to it being reversed. So the last index (the largest one) is always used first.
You shouldn’t attempt this. The Grasshopper definition solver does not expect any changes to the definition graph while the graph is being solved. This means that if you do change it, even if you manage it (by hacking around the check that you see right in your first screenshot), you might never be able to get the right answers while solving the solution. The ONLY way to update anything that is definition-related is outside the solution. Frankly, this mantra has been repeated so many times I thought you knew it
It seems I missed all those topics requiring something similar.
Does that mean that I can ask the component to expire the solution, and ask for a new solution? And then perform the unregistering of the output parameters?
The way to do it is to schedule a new solution with the document, and provide a callback to the scheduler so you get informed just before the scheduled solution start. Have a look at GH_Document.ScheduleSolution()
Of course another way to do it is to run your code from outside a solution to begin with, for example via a menu or button click.
A third way is to handle the SolutionStart event for GH_Document. This is called just before any solution and you’re still allowed to make changes at this point.
I would go with second option and use the current _runIt input-button. But would scheduling a new solution, resolve the issue with panel wires hanging the “air” after the removal of output parameters?
No it won’t solve that, that’s an entirely separate issue. Wires are not actually things in their own right, they are merely logical connections that are drawn as bezier curves. Every parameter (IGH_Param or more derived type) has a list of sources from which it inherits data. When the canvas is painted a curve from each parameter to each of its sources is drawn.
When you have a ‘floating’ wire it means that some parameter is associated with a source which no longer exists in the document. It doesn’t get drawn because the document never asks it to draw itself, however it also doesn’t get Garbage Collected because the recipient parameter keeps it alive by storing it in its sources list.
When you remove an output, you must ensure all its connections are severed. This is not difficult, when using the GH_Component.Params.UnregisterOutputParameter(IGH_Param, bool); method, just pass in true for the second argument to isolate the output before removing it.
also, just a side point, but with “bang” you don’t need to click plus for each one, you can just right click the component and choose “match outputs”…
I’m extremely curious about the larger goal you’re trying to achieve. What’s the use case for dynamically removing the outputs based on a solution change (toggle)?
It is suppose to be a Ladybug component with say 3 inputs: “requiredKey” (a single item), “keys” (a list), “values” (data tree). The number of branches of the “values” input is suppose to correspond to the number of items in the “keys” list.
The component is suppose to take the “requiredKey”, and check if the same one is located in the “keys” list. If it is, it will take the index of that item, and generate a number of output parameters corresponding to values.Branches[index] values.
The “_runIt” toggle is a feature of Ladybug, where somewhat longer runtime components require this input.
If I explained all of this in a confusing way, then essentially replicating the “Bang” component is what I am looking for.
This is perhaps not what you want and also perhaps it doesn’t work, but I think when compiled components are written in C# you can also do things when a component is placed on the canvas e.g. automatically create a second component that is connected to the first. The same should probably be possible now that you can compile Python components.
So, I think, when your compiled component A changes the outputs of your automatically created component B it should work. Although, even if it works, it is, of course, somewhat hackish and thus ugly looking… (Not to me, but your Ladybug users, I guess.)
I haven’t checked if this can be done with Rhino WIP ghpython components. The current scope of Ladybug is still Rhino 5 and ghpython 0.6.0.3. which does not support compiling. Even if it does it has been decided to go on with uncompiled ghuser files in future as well.
Which makes sense as there are quite a few of them by now. But when you have two components A and B. A should be able to change Bs outputs in Rhino 5, too. (I think… I guess… But I am not sure at all.)
l
This definition shows how to override the AppendAdditionalComponentMenuItems method to visualize a context menu that will have the “Match outputs” options like the Explode Tree component. This is an option possible with “GH_Component SDK Mode”, and therefore needs Rhino WIP.
Please have a look and tell me your thoughts. Of course, this can be further improved to even better replicate the behavior of the native component. Thanks!
Giulio
–
Giulio Piacentino
for Robert McNeel & Associates giulio@mcneel.com
i know it is an old topic but maybe i find a solution and i test it
and i hope if someone can make this script automatically generate output plugged to chosen component (text ; curve , mesh …)
Hi @DavidRutten.
I was reading this post and so I started to try the solutions that you proposed, but I still end up with the same error posted at the beginning of this discussion.
This is the solution using ScheduleSoltution()