Hey! I’m trying to create a gh component in C# with a dynamic set of inputs:
Generate a set of inputs dynamically based on the component “initial” state. This was done using IGH_VariableParameterComponent and Params.RegisterInputParam(ParamObject).
Based on the inputs connected to the component, generate a new set of inputs. Basically what I want to achieve is that whenever a new variable is connected to the component, then a function that clears and generates the new set of inputs is called. Also, when a variable that is already connected to the component is modified, a new set of inputs is generated again. I’ve tried adding an event handler to each of the ParamObjectObjectChanged events but It was only fired when a variable was connected with the component, and not when it was modified…
Is it possible to capture an event when a variable connected to the component is modified? If so, how can I do this?
A variable change will trigger a new solution.
However it is impossible to add or remove input or output parameters during a solution.
What you will have to do is to schedule a new solution with a callback method.
In that method, modify the inputs and outputs, expire the component and toggle a private bool variable so the component actually knows he needs to skip that process on the next solution.
Thank you for your help. Indeed that solution works for when I have all the inputs connected to the component. However, I was also looking to update the inputs when a variable is changed.
For instance, in this case, I made a component to calculate the area of either a circle or a rectangle. If the boolean is_square is set to true, then display the inputs for a rectangle (width and height) and compute the area of a rectangle. If is_square is set to false, then show the inputs for a circle (diameter) and calculate the area of a circle:
Sure, however you need to make all input parameters Optional (except the toggle maybe ?) so that the component runs even if some inputs aren’t specified.
Unfortunately I only know which parameters are optional at runtime… These input parameters are also created dynamically, so instead of “SampleAreaCalculation” it can be any other unrelated calculation with another set of input parameters.
Also, I get a set of inputs by sending a request to a server with the current state of the component. For example, in the case of “SampleAreaCalculation”, If I send a request containing the input parameter is_square=True, then the response will contain the width and height (and is_square) input parameters.
So basically, I would like to:
Send a request to my server each time an input variable is modified or connected to the component.
If the number or names of the variables from the response are different from the component ones, then update the inputs of the component.
The fact that you don’t know how many inputs are “required” doesn’t prevent you from setting those inputs to Optional, so that the component runs as soon as it has the minimum information required to run (in your case, seems to be the first input).
in the component class :
private bool isRunning = false;
in SolveInstance() :
if(!isRunning)
{
//get your request result here
}
//check if the inputs are valid.
if(areInputValid)
{
//proceed to calculation and SetData
if(data exists)
{
isRunning = false
}
else
{
AddRuntimeMessage("Input parameter XXX failed to collect data");
return;
}
}
else
{
//schedule a new solution and do nothing else
}
in the callback :
add or remove input parameters
then call a VariableParameterMaintenance() method.
then set isRunning to true
then ExpireSolution(true);
in VariableParameterMaintenance():
set names, component message, optional and data access.
Param[i].Optional = true; //set inputs optional
Also call this method in RegisterInputParams.
You may also need to ensure that the boolean input is only recieving ONE value. Otherwise your parameter live modification is going to cause problems.
You can override BeforeSolveInstance and check the VolatileData only has one (and exactly one) element.
Perhaps you might consider two separate components, one for the square and one for the circle. Then, add a third component which decides which one to call based on the input received and feed the area into the next component in the GH flow?
Thank you again magicteddy, I almost got it working by setting every input parameter to optional. This way, the solution is re-calculated on every connection/modification of an input parameter.
However, there is just one small thing missing: I would like the component Input parameters to be updated right after I connect a variable to the component. For intance, in the “SampleAreaCalculation”, if I connect a boolean toogle set to False, to the is_square input variable, then the inputs of the component should immediately change. Because I’m using OnPingDocument().ScheduleSolution(0, callBack), the inputs of the component are only updated after I add another element to my grasshopper (like a panel, etc), or make any changes in the component.
Is there any way to force an update of the components inputs? Thank you again for your help.
Thank you, the problem is that I need the component to be able to “figure out” what are the inputs given its current state, otherwise I would need to create a new component for each possible state of the component, which is only known at runtime.
What does your AddInputParameters method look like ?
There should be a call to RegisterInputParam in there, I recall having problems if removing it.
The only difference I can spot is that I use Instances.ActiveCanvas.Document to get the document instead of OnPingDocument(), I don’t know if that makes a difference.
My RegisterInputParam and RegisterOutputParam is empty because all my input parameters are generated dynamically. Basically, my component first asks what “calculation” I want, and then depending on the calculation, it populates the component with the calculation’s input variables.
These inputs are added in an unrelated function by calling Params.RegisterInputParam(IGH_Param param) on each param object (part of Grasshopper.Kernel.Parameters).
edit: nevermind about the edited bit . Using Instances.ActiveCanvas.Document also seems not to make a difference.
edit2: video of the problem:
Basically, the component’s inputs only changed when I changed the bool variable a 2nd time, or, added another gh variable.
In my case, all of my inputs are set to “optional=True” since I don’t know which inputs are dependent (optional) on others or not. Maybe it’s because of this…
Yeah, this was definitely the tricky part. Basically I was most likely getting out-of-date parameters because SolveInstance is probably not meant to be ran asynchronously