Automatic update of ValueList only when connected

Hello.

I’m working on a custom component in c#.

I would like to set the key and item automatically when connecting ValueList, like the input with []symbol of Elefront.


AutoValueList.gh (10.1 KB)
AutoValueList.gha (7 KB)
AutoValueList.cs (5.0 KB)

How can I determine if ValueList is connected?
I would like to distinguish between such cases.

  1. Do not update ValueList if connected
  2. Update ValueList if you connect for the first time

Here are my thoughts.
Since it is determined by whether the key has the same value, the connected ValueList will also change every time the key changes.

Adding using System.Linq;

protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
        {
            pManager.AddTextParameter("Value List", "V", "Value List ... ", GH_ParamAccess.item);
            pManager.AddTextParameter("Keys", "K", "Value List Keys ... ", GH_ParamAccess.list);
            pManager.AddTextParameter("Items", "I", "Value List Items ... ", GH_ParamAccess.list);
        }
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
        {
            pManager.AddTextParameter("Selected Item", "S", "Selected Item ...", GH_ParamAccess.item);
        }
protected override void SolveInstance(IGH_DataAccess DA)
        {
            // Set Keys and Items
            List<string> keys = new List<string>();
            List<string> items = new List<string>();
            DA.GetDataList(1, keys);
            DA.GetDataList(2, items);

            // Create Value List
            foreach (IGH_Param source in Params.Input[0].Sources)
            {
                if (source is Grasshopper.Kernel.Special.GH_ValueList)
                {
                    Grasshopper.Kernel.Special.GH_ValueList vl = source as Grasshopper.Kernel.Special.GH_ValueList;
                    // Create list to check the keys differences.
                    List<string> keysValue = new List<string>();
                    for (int i = 0; i < keys.Count; i++)
                        keysValue.Add(keys[i]);
                    List<string> defKeys = new List<string>();
                    foreach (var key in vl.ListItems)
                    {
                        defKeys.Add(key.Name);
                    }
                    // If the same value already exists for Keys, the ValueList is not changed.
                    if (defKeys.SequenceEqual(keysValue))
                    {
                        ;
                    }
                    else
                    {
                        // Clear the contents of ValueList
                        vl.ListItems.Clear();
                        // Add double-cotation to item
                        string addQuotation;
                        for (int i = 0; i < items.Count; i++)
                        {
                            addQuotation = "\"" + items[i] + "\"";
                            // Add item to ValueList
                            vl.ListItems.Add(new Grasshopper.Kernel.Special.GH_ValueListItem(keys[i], addQuotation));
                        }
                        // Reset Selection
                        vl.SelectItem(0);
                        vl.ListMode = Grasshopper.Kernel.Special.GH_ValueListMode.DropDown;
                        vl.ExpireSolution(true);
                    }
                }

                // Set Selected Item
                String selectedItem = String.Empty;
                DA.GetData(0, ref selectedItem);
                DA.SetData(0, selectedItem);
            }
        }

Thank you.

I referenced the link below.

Hi,

I moved your code managing the ValueList inside a method called by the event handler ParamSourcesChanged. This event fires when a new source is added or removes to an input of the component, which allows to distinguish your two cases.

This seems to have the desired behaviour, the list updates when it’s connected or reconnected, but not when the keys and values are modified. Although what you currently have seems best in my opinion as it ensures the value list is constantly updated.

AutoValueList.cs (5.6 KB)

1 Like

@magicteddy
Thanks so much for spending the time on my question!!

I didn’t know about ParamSourcesChanged!
It almost works fine, but I have an additional question.

It is about the timing of when the inputs are assigned to keys and items.
If even one input is missing, I get the error message 1. Input parameter K failed to collect data.
At this point, I think that SolveInstance is not executed.

Therefore, keys and items are still empty, and ParamSourcesChanged is running, but I don’t think it is getting the values to assign to the ValueList.

To make it work anyway, I made the input optional so that it would work on the first input too.
I added pManager[0].Optional = true;.

But I am wondering if there is a way to do this without making it optional.
Do you think there is such a way?

I made a few adjustments to the code as follows.
AutoValueList2.cs (5.3 KB)

Also, this code checks if the keys in the ValueList are the same before updating the ValueList.
Do you think it is possible to check and update items as well as keys?
My understanding of vl.ListItems is unclear.

Thanks so much!!

Hello

Indeed SolveInstance seems to only be called if all mandatory inputs recieve something, I definitely thought it was not the case. However in this situation this is a good thing, because you wouldn’t want to fill the valueList unless a list of keys AND an list of values are provided ? As a fun component behaviour, I added a test to check if Keys and Values are provided something, and if not, it removes the wires from the value list, which prevents the update.

I don’t really see what you are looking for exactly with this component.

My understanding of vl.ListItems is a List of GH_ValueListItem which is an object storing both the key and a value of the ValueList.
Right now, when a wire is updated, the code is checking if the new list of keys entering the K input is identical to the keys stored in ValueList, and only if it’s not the case, it updates the entire list. Which means that if you change the values and then unplug/plug the value list, the values won’t be updated, is that what you want ? If not you could basically remove half the code and only keep what’s in the else branch.

AutoValueList2.cs (4.8 KB)

1 Like

@magicteddy
Thanks so much for once again answering my question!

The addition of e.Parameter.RemoveAllSources(); is very cool.
I had no idea such functionality was even possible!!

I have two issues.
First, my question was whether it could work without making the input to V optional.
(I mean I want error messages for V as well as K and I.)

Second, I don’t want to update the ValueList if the inputs to K and I are unchanged.
If we update it, if we had made any selections in the ValueList, the selection would revert to the 0th selection.

This is just a screen capture and a file with pManager[0].Optional = true; commented out.

Thanks so much!!!

AutoValueList3.cs (4.7 KB)

I think I got this to work as intended.

The problem of storing keys and values is that we’d need to be able to access input data outside of SolveInstance, which seems only possible using VolatileData.

However this requires to deal with IGH_Structure so I went another way…

I made all three parameters optional, but added the runtime messages inside SolveInstance.

protected override void SolveInstance(IGH_DataAccess DA)
        {
            // Set Keys and Items
            keys.Clear();
            items.Clear();

            bool exit = false;
            // Set Selected Item
            String selectedItem = String.Empty;

            if (!DA.GetData(0, ref selectedItem))
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Input parameter Value List failed to collect data.");
                exit = true;
            }

            if (!DA.GetDataList(1, keys))
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Input parameter Keys failed to collect data.");
                exit = true;                
            }

            if (!DA.GetDataList(2, items))
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Input parameter Items failed to collect data.");
                exit = true;
            }

            if (exit)
            {
                return;
            }
            else
            {
                DA.SetData(0, selectedItem);
            }
            
        }

I also added a memory so that the selected item does not change if there is an update of the list triggered.

The component :

  • has all three messages if inputs are missing, and returns null until all three inputs are provided
  • deletes a wire from Value List is Keys and Values are not provided a single source, to ensure the user defines this input after keys and values.
  • updates the value list only when it is plugged or re-plugged.
  • keeps memory of the selected item if there are changes in the list
  • and reverts to the first item if the previously selected item is gone.

AutoValueList

AutoValueList3.cs (6.0 KB)

2 Likes

@magicteddy
Thank you so much for your more than perfect answer!!
You solved my problem in a very intelligent way!
I couldn’t have solved it without your help! Thank you!

Incidentally, your screen capture gif is so beautiful!
The Grasshopper canvas is white or transparent and very easy to see.
The mouse click animation is cool too.
Could you please tell me how you captured it?

Thanks so much!!!

1 Like

I use https://www.screentogif.com/ as a screen recorder.
I permanently changed the canvas and the grid to blank in the settings, it’s not due to the recorder.

Have a nice day !

1 Like

@magicteddy
Thanks for your answers!!!
Have a nice day too!!