for example:
i found in this code,it cannot reach the “else” part, when there is no input for the component at all…
Can anyone why how to fix this problem(pls neglect the small pink rectangle,just for custom attributs test )?
Thank you!
for example:
var ix = Component.Params.IndexOfName("v");
var has_input = Component.Params.Input[ix].SourceCount > 0;
Or if you keep track of the (input) Params index yourself:
var has_input = Component.Params.Input[0].SourceCount > 0;
if (has_input)
Component.Message = "has input" ;
else
Component.Message = "no input" ;
Be aware if Output params because no event is fired when changing them, so your code wouldn’t be updated when removing a wire. On the Inputs it works though. (See the problem with updating Outputs in this post from yesterday)
// Rolf
Hello RIL
thank you
i have tried your method,but if i disconnect the connection,the Message is also unchange!is 3 also.
To me it doesn’t look like your input name is “input” (which you use in your code). The name appears to be “v”. But in order to avoid any name trouble, use zero (0) as index for the first input. It should work (I just tried it on my own definition. It updates as expected.
Edit: Try this file. It updates as expected.
ConnectionMessages_.gh (7.2 KB)
// Rolf
RIL,thankyou,i open the gh file,it works nice,but i want to do in custom component,in visual studio.it seems also no works…(i replace the "input " into 0)
Perhaps you must subscribe to the Input event in the VS version? Try this:
// In SolveInstance, add this:
this.Params.ParameterChanged -= this.OnParamsChanged;
try
{
// Do your thing
// ...
}
finally
{
this.Params.ParameterChanged += this.OnParamsChanged;
}
}
// Your own eventhandler responding to changes on the input:
public void OnParamsChanged(object source, EventArgs e)
{
RhinoApp.WriteLine("Event was fired! (An Input or Output was connected or disconnected)");
var ix = this.Params.IndexOfInputParam("v");
var cnt = this.Params.Input[ix].SourceCount;
var has_input = cnt > 0;
if (has_input)
this.Message = "has input " + cnt.ToString();
else
this.Message = "no input";
}
I tried your method,this method is more difficult,it works almost good,but only one thing is no good:
when I drag the component from toolbar into canvas,the component Message doesnot show"no input"…just after i connect a input (message show “has input”)and disconnect the input,the message show “no input”…almost good,just how to fix when first drag into canvas to show “no input”,thank you RIL
Yes, this is because a component being placed on the Canvas has not yet had the chance to subscribe to anything…
Unfortunately I have not (yet) tried all the events if there’s any other way one could activate this check, but perhaps @DavidRutten has any hints on how to catch a related event.
There are lots of events being fired, if you just can catch them when needed…
// Rolf
So reading this top to bottom and replying in that order. The reason that your else never runs is because the input isn’t optional. When a non-optional input doesn’t have data the SolveInstance()
method isn’t even called. You can mark inputs optional when you add them inside RegisterInputParams()
The named accessor uses the name, not the nickname of the parameter. The nickname can be easily changed by the user so it’s not considered a good identifier. However @RIL is right in that the index accessor is much more reliable and should be used whenever possible.
Thank you,David,the method works.
thank you RIL,i learn a lot from you code,thank you.
David,it doesnot work in this case:
More on index accessors. It took me long before I discovered that the pManager
actually returns the index of newly registered Params. Using those index values has two major benefits:
So for these reasons I started using variables “IN_Params” and “OUT_Params” when registering them, see code below (see also how the IN_Name and OUT_Name indexes are used in the SolveInstance
code far below)
private int IN_Mesh;
private int IN_Radius;
private int IN_CenterPoints;
private int IN_Directions;
private int IN_Iterations;
private int IN_Optional;
private int OUT_HitPoints;
private int OUT_CircumPoints;
private int OUT_IntersectRays;
private int OUT_MeshFaces;
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
IN_Mesh = pManager.AddMeshParameter("Mesh", "M", "Blah, blah, blah", GH_ParamAccess.item);
IN_Radius = pManager.AddNumberParameter("Radius", "R", "", GH_ParamAccess.list);
IN_CenterPoints = pManager.AddPointParameter("CenterPoint", "C", "Blah, blah, blah", GH_ParamAccess.list);
IN_Directions = pManager.AddVectorParameter("Directions", "D", "Blah, blah, blah", GH_ParamAccess.list);
IN_Iterations = pManager.AddIntegerParameter("Iterations", "I", "Blah, blah, blah", GH_ParamAccess.item, 10);
IN_Optional = pManager.AddBooleanParameter("Optional output", "O", "Blah, blah, blah", GH_ParamAccess.item, false);
}
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
{
OUT_HitPoints = pManager.AddPointParameter("ContourPoints", "H", "Blah, blah, blah", GH_ParamAccess.tree);
OUT_CircumPoints = pManager.AddPointParameter("RayPoints", "C", "Blah, blah, blah", GH_ParamAccess.list);
OUT_IntersectRays = pManager.AddVectorParameter("IntersectRays", "R", "Blah, blah, blah", GH_ParamAccess.list);
OUT_MeshFaces = pManager.AddMeshFaceParameter("MeshFace", "F", "Blah, blah, blah", GH_ParamAccess.list);
}
protected override void SolveInstance(IGH_DataAccess DA)
{
// ===================
// MESH CONTOUR POINTS
// ===================
Mesh m_mesh = null;
if (!DA.GetData(IN_Mesh, ref m_mesh))
throw new System.ArgumentNullException("(Mesh)");
var m_radius = new List<double>();
if (!DA.GetDataList(IN_Radius, m_radius))
throw new System.ArgumentNullException("Invalid Radius List.");
var m_centerpts = new List<Point3d>();
if (!DA.GetDataList(IN_CenterPoints, m_centerpts))
throw new System.ArgumentNullException("Invalid Center Point List.");
var m_directions = new List<Vector3d>();
if (!DA.GetDataList(IN_Directions, m_directions))
throw new System.ArgumentNullException("Invalid Directions List.");
var m_iterations = -1;
if (!DA.GetData(IN_Iterations, ref m_iterations))
throw new System.ArgumentNullException("Invalid Iterations value");
var m_optional = false;
if (!DA.GetData(IN_Optional, ref m_optional))
{
// throw new System.ArgumentNullException("Invalid Optional List.");
}
a.s.o
In this way the code becomes crystal clear and less error prone. No more bugs related to renaming or reordering inputs and outputs.
// Rolf
good idea!
Optionality is a property of IGH_Param, so you can assign that property either before or after:
int index = pManager.AddParameter(new XYZEAxisParam(), ...);
pManager[index].Optional = true;
or
IGH_Param param = new XYZEAxisParam();
param.Optional = true;
pManager.AddParameter(param, ...);
or
pManager.AddParameter(new XYZEAxisParam() { Optional = true }, ...);
David ,i found this way also can:
but in custom paramter,the pmanger doesnot support add a default value…
more concise
default values and optional parameters are not the same thing. They may both result in having your SolveInstance method called, but they do so in different ways. It is not unthinkable that you may want both a default value and an optional parameter.
If you have your own parameter type, then you must assign default values to the PersistentData
yourself.