How can I get a Number Slider object and set a value?

Hi guys,

I’m trying to build a Grasshopper Component in C# that implements an optimization algorithm (RBF-optimization, if you are curious). I have the optimization algorithm as a Python library and want to build an interface around it.

As a first step, I’m trying to figure out how to get a Number Slider object and set a value. (Which I need to create different model instances during the optimization process.) I guess that GH_SliderBase.FixValue() is the way to go, but it’s not clear to me how I can get the GUID of the slider object based on the user dragging with the mouse. (I think this should work exactly like in Galapagos.)

I’m new to the Grasshopper SDK, so any help on what would an appropriate approach is highly appreciated.

Thanks,
Thomas

1 Like

FixValue is not the right method to call, that method merely fixes the assigned value (i.e. it makes sure it doesn’t exceed the limits and adheres to the correct rounding scheme).

The problem you have is actually three-fold:

  1. Get references to all sliders you want to change.
  2. Change all slider values.
  3. Run a new solution.

I don’t know how you define which sliders are relevant to you, so I cannot much comment yet on item #1.

There are four ways in which you can change the slider value, you’ll have to decide which is best for you:

  1. Through the Grasshopper.Kernel.Special.GH_NumberSlider.TickValue property
  2. Through the Grasshopper.Kernel.Special.GH_NumberSlider.SetSliderValue() method
  3. Through the Grasshopper.Kernel.Special.GH_NumberSlider.TrySetSliderValue() method
  4. By accessing the underlying slider object via GH_NumberSlider.Slider (not recommended).

Galapagos uses the first approach, because it is the simplest and only involves integer arithmetic (Galapagos avoids floating point numbers whenever possible). The TickCount and TickValue properties available on every GH_NumberSlider object tell you how many unique states the slider supports and which is the current state. For example an integer slider from -5 to +5 supports 11 unique states {-5,-4,-3,-2,-1,0,1,2,3,4,5} so you can set the slider to any state between and including 0 to 10.

SetSliderValue() and TrySetSliderValue() are similar, but they assign floating point values to the slider instead. The value you assign is not guaranteed to be the value you end up with, as it is subject to limits and rounding. The difference between SetSliderValue() and TrySetSliderValue() is only relevant if the slider has an internal expression which modifies the ultimate slider value. TrySetXXX will attempt to backsolve the expression so that the ultimate value best resembles the value you requested, whereas SetXXX just sets the slider value prior to the expression.

By accessing the underlying slider object you get access to all slider properties, but it is not exactly trivial to make sure the slider ends up in a valid state if you start poking those.

Ultimately after you’ve set all values, you’ll have to trigger a new solution. You can do so by calling NewSolution(false) on the GH_Document which contains the sliders.

4 Likes

Thanks for the succinct explanation on the sliders!

Strangely, I cannot find any of the methods you mentioned in the SDK help.
Specifically, I can’t see the “Special” namespace in Grasshopper.Kernel.
(However, in Visual Studio I can see the methods you mention.)

Regarding point 1 (what sliders I want to reference), I’d like to have the users drag a connection to the ones that they find relevant. So, somehow I need the the cursor to register what component it is hovering over.

Yeah the Grasshopper.Kernel.Special namespace isn’t documented. It basically contains most of the ‘weird’ objects like text panels, sliders, colour swatches etc.

The way Galapagos does this is actually rather involved. You need a lot of code to hijack the mouse and override the display. I can show you how to do it, but it would be a lot easier to just have an input on your component where you can plug in sliders.

Ah, I guess that why it’s called special ;-).

Sure, I don’t mind setting up an input for the sliders.
In that case, would it work something like so?

  1. The user hooks the sliders on as an input.
  2. Get the slider GUIDS (with GH_ConnectivityDiagram.GetAllInputs()?).
  3. Now the sliders can be manipulated, as you explained above.

It’s probably a little confusing that BOTH parameters and fitness are inputs, but definitely good enough for a first prototype. (I guess that’s why you did it differently in Galapagos, right?)

Actually I just realised that’s probably a bad idea (the sliders as inputs). It will expire the solver object whenever the sliders change, so you have to sort of get around that. If the component is inert (like Galapagos) then it doesn’t really matter I suppose.

You won’t need the connectivity diagram for this, you just have to iterate over the source parameters of your input and cast them to GH_NumberSlider.

foreach (IGH_Param param in Params.Input[0].Sources)
{
GH_NumberSlider slider = param as GH_NumberSlider;
if (slider == null)
continue;
// Set the slider.
}

1 Like

Thanks, looping over the parameters makes sense.

Sorry, but what do you mean with the component being “inert”? And how do I make it so?

I understand your point how the solution is recomputed every time the sliders change. (This means the component will run again from scratch, right?) I guess a way to get around that would be for the solver to write the current data to a file at every step, and to pick up from there on the next step.

Most components do things. They consume some sort of input data and then compute new output data. This makes them an integral part of a solution. When an object changes it expires all the objects which directly depend on it. Then those objects expire and they in turn expire objects that directly depend on them. So when an object changes (say a slider is dragged) there’s a shock wave that travels from left to right through the network, expiring everything that is causally dependent on the slider.

So if your component is causally connected to a bunch of sliders it will expire itself whenever those sliders change. However Galapagos doesn’t really do anything during a solution, all the interesting stuff happens in a window which is separate from the object. If your component takes a similar approach then that’s fine, however if your object acts more like a typical component then this may seriously complicate matters.

Perhaps it makes the most sense for the time being to store your collection of relevant sliders as a local list and provide some interface so people can add/subtract sliders. For example the menu with ‘All Sliders’ and ‘Selected Sliders’ in Galapagos. That would be a breeze to implement and you can always later add a more visual interface on top of this mechanism.

Addendum:

My worry is that if your component expires because it is causally connected to a slider, and this slider is changed as part of the component solution, a vicious cycle may start where the slider and the component expire each other in a never ending loop.

Here’s an idea:

Why don’t I make a separate component, just to have the params connect to it?
The parameter component can maintain a list of the number sliders.

The solver components find the parameters component, and gets the GUIDs of the number sliders from there.
In that way, the solver component never get’s expired, right? I could also do the same for the fitness value.

Hey David,

I can’t find a GH_NumberSlider in the SDK.
There’s only GH_Slider and GH_SliderBase.
Are they the same thing? Which one is the right one?

Thanks,
Thomas

Nevermind, I found it: Grasshopper.Kernel.Special.GH_NumberSlider

GH_SliderBase is the core class for sliders. It contains almost all the logic, but it is not tied to a specific interface element.
GH_Slider is the implementation of GH_SliderBase on a winforms control.
GH_NumberSlider is the implementation of GH_SliderBase as a canvas object.

1 Like

Hi all,

The information in this post is really useful and helped me to do the same thing. However, this strategy results in a GH breakpoint ‘An object expired during a solution’ which makes sense (as David pointed out, ’ a vicious circle’ is created). The good thing is that this solution works if you close the pop-up warning window and I guess that’s good enough if you accept that your code/component/plug-in is not gonna be perfect.

Still, I was wondering if there was a safer/cleaner way to do this. Specifically, if it was possible to have input parameters that do not ‘regenerate’ the solution, I believe that we would get rid of the aforementioned message.

Let me know if you think it is possible and if you have any idea how to actually implement the idea.

Thanks,

R.

If you want to affect the solution using data from the current solution, it’ll simply have to wait until the next one. The best way of doing is is to create a schedule and register a callback:

GH_Document.ScheduleSolution(1, callbackDelegate)

This means that 1 millisecond after the current solution finishes, the callback will be invoked (this is where you make the changes you want to make and expire the objects affected) and then a new solution will be started.

2 Likes

Hi David,

I’m having a problem with the plug-in that I’m building and Archsim.
The plug-in resets the number sliders and then starts a new solution:

GH_Document.NewSolution(false, GH_SolutionMode.Default);

The problem is that, at this line in the code, Rhino crashes.
Using Archsim with Galapagos works fine, so I guess I’m doing something wrong.
Do you have any idea what it could be?

The plug-in has worked fine in the past, using DIVA, Karamba etc, and it also works when I disable the energy simulation in my definition. (Maybe the problem is that the simulation opens in new window, but then that’s the case for DIVA as well).

Cheers,
Thomas

When you say crash, is there an error message?

Just the window that says that Rhino stopped unexpectedly and asks if one would like to report the crash,

If you clicked yes, then the crash data -assuming there is any- will show up on my list soon. With a bit of luck this data contains both the reason for the crash and the history of function calls that led to the problem.

Oh sorry, I forgot we disabled crash reports coming in from Grasshopper on Rhino5 since almost all the reports that came in were either already fixed in Grasshopper for Rhino6, untraceable, or came from plugins.

While the report window is visible (ie. after the crash but before you submit/cancel the crash report) there should be a text file on the desktop with callstack information.