I’m trying to write a C# component which takes in an object finds the given public properties for the type and updates the output params to match and sets their corresponding value. I have seen a few posts which describe using the IGH_VariableParameterComponent interface, however these appear to be called on events to do with UI updates (or the add/remove param click), and not called when the connector to the input param is set (they also do not have access to the input param without me storing a private reference in the component class).
@DavidRutten, I’m tagging you as you have commented on similar threads and seem to be aware of what needs to be done. As you have already noted the SolveInstance is not the context for this type of behaviour, but seems to be the only place i can get the type information without storing some reference to the object/type. Would be great if you can share some insight on how to achieve this. Basically i just want grasshopper to act as a way to inspect models which are nested without explicitly creating components for each model type… I could use source generators (but then i have the issue of way too many components).
Thank flokart, that solution works. This issue i was originally trying to solve, slow performance of an alternative library with a component providing the same functionality, appears to exist for me too.
I will start up another thread to address this issue, but my original question of dynamically adding output params was achieved by:
use the solveInstance to cache the input properties (but take no action)
within the solveInstance ScheduleSolution can be used to schedule a task to create the output params (using the cached properties)
There is a bit param registering/unregistering/cleanup needed
call ExpireSolution(false) to ensure data is cleared and downstream components are notified if changes made
I expect ExpireSolution will retrigger the solveInstance, however, this time you just need to check the params and cached properties match up, and assuming they do skip scheduling the task to update the params and simply set them instead.
… I probably should have provided an example instead.
The issue i am now having is that the SetDataList performs incredibly slow. I have a list of 50,000 object (which i don’t think is large)
Here is the elapsed time to set the params for an object made up of 5 nested objects and 3 lists.
Elapsed time to set data: 0ms
Elapsed time to set data: 0ms
Elapsed time to set data: 0ms
Elapsed time to set data: 0ms
Elapsed time to set data: 0ms
Elapsed time to set data list: 398ms [Size=8349]
Elapsed time to set data list: 2792ms [Size=47802]
Elapsed time to set data list: 0ms [Size=0]
I don’t want to give the verbatim solution as it is a little too close to the original authors and there is no open source license in his repo (and you can already view it yourself). This is the concept of what i have described.
public class ExampleOutParams : GH_Component, IGH_VariableParameterComponent, IDisposable
{
public ExampleOutParams()
: base("ExampleOutParams", "Nickname",
"Description",
"Category", "Subcategory")
{
}
private Dictionary<string, Type> _properties;
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
pManager.AddGenericParameter("Object", "Object", "", GH_ParamAccess.item);
}
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
{
}
protected override void SolveInstance(IGH_DataAccess DA)
{
GH_ObjectWrapper objWrap = null;
DA.GetData(0, ref objWrap);
var obj = objWrap.Value;
// add logic for caching _properties from input
// check validity of out params
if (OutputParamsAreValid() is false && DA.Iteration == 0)
{
// schedule a solution to create output params
OnPingDocument().ScheduleSolution(5, d =>
{
CreateOutputParams(true);
});
}
else
{
// simply set the outparams
foreach (...) // some iteration over the values to set
{
string name = ... // a name matching the output param name (alternatively can be the index)
object value = ... // some logic to get the values
if (value is IEnumerable<object> enumerable)
{
DA.SetDataList(name, enumerable);}
else
{
DA.SetData(name, value);}
}
}
}
private void CreateOutputParams(bool recompute)
{
// if out params are less than the current dictionary count, register further out params
if (Params.Output.Count < _properties.Count)
{
while (Params.Output.Count < _properties.Count)
{
var new_param = CreateParameter(GH_ParameterSide.Output, Params.Output.Count);
Params.RegisterOutputParam(new_param);
}
}
// in the case it is less, unregister the excess
else if (Params.Output.Count > _properties.Count)
{
while (Params.Output.Count > _properties.Count)
{
Params.UnregisterOutputParameter(Params.Output[Params.Output.Count - 1]);
}
}
// notify Params have changed
Params.OnParametersChanged();
// Call parameter maintenance to set names/access accordingly
VariableParameterMaintenance();
ExpireSolution(recompute);
}
private bool OutputParamsAreValid()
{
// suggest some method to check outparams match the cached properties dictionary
// i.e. check name, order, access type
}
public bool CanInsertParameter(GH_ParameterSide side, int index)
{
return false;
}
public bool CanRemoveParameter(GH_ParameterSide side, int index)
{
return false;
}
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
{
// generate a generic param when a new one is registered
return new Param_GenericObject();
}
public bool DestroyParameter(GH_ParameterSide side, int index)
{
return true;
}
public void VariableParameterMaintenance()
{
for (var i = 0; i < Params.Output.Count; i++)
{
var name = _properties.Keys.ToList()[i];
// store the type to check if IEnumerable, use list access in this case
var type = _properties[name];
var outParam = Params.Output[i];
// set Name/NickName/etc of outParam, spefically the access type shown below
if (typeof(IEnumerable).IsAssignableFrom(type))
{
outParam.Access = GH_ParamAccess.list;
}
else
{
outParam.Access = GH_ParamAccess.item;
}
}
}
protected override System.Drawing.Bitmap Icon => null;
public override Guid ComponentGuid => new Guid("5abb438c-4a52-4c5f-86e1-3deb4733f137");
public void Dispose()
{
ClearData();
foreach (var ghParam in Params)
{
ghParam.ClearData();
}
}
}