Dynamically updating slider

unhandled

(Cemreugurlu) #1

Hi, I am trying to change the number slider value dynamically. Following code is working with one dimensional array, which is xnew[]:

  private void RunScript(List<double> x, int nTimes, bool run, ref object A)
  {
    //based on code snippets from David Rutten and James Ramsden:
    //http://james-ramsden.com/remotely-change-value-of-number-slider-grasshopper/
    int n = x.Count;
    if(run)
    {
      if((nTimes != minimum))
      {
        minimum = nTimes;
        counter = nTimes;
      }
      //while(counter > 0){//time iteration, now it is controlled instead with the input nTimes
      double[] x_new = new double[n];
      var input = Component.Params.Input[0];//gets sliders in
      for(int i = 0; i < n;i++)
      {
        Random rnd = new Random(Guid.NewGuid().GetHashCode());
        x_new[i] = rnd.NextDouble();
        var slider=input.Sources[i] as Grasshopper.Kernel.Special.GH_NumberSlider;
        slider.SetSliderValue((decimal) x_new[i]);
        slider.Slider.DrawControlBackground = true;
        slider.Slider.DrawControlBorder = true;
        slider.Slider.ControlEdgeColour = System.Drawing.Color.Blue;
        slider.Slider.ControlBackColour = System.Drawing.Color.Aquamarine;
        //slider.Slider.DecimalPlaces = 4;
      }
      InRun = run;
      if(counter < 0){InRun = false;}
      A = x_new;
      if(InRun){GrasshopperDocument.ScheduleSolution(100, ScheduleCallback);}
      //}
    }
  }
  // <Custom additional code> 
  // <Custom additional code>
  //private bool _run = false;
  private int minimum = -1;
  private int counter = -1;
  private bool InRun = false;
  private void ScheduleCallback(GH_Document document)
  {
    counter--;
    Component.ExpireSolution(false);
  }

However, I want to change the value of slider with two dimensional array, which requires one more loop (for using it in populated algorithms) Following code gives an idea about what I want to do, but not working properly:

  private void RunScript(List<double> x, int nTimes, bool run, ref object A)
  {
    //based on code snippets from David Rutten and James Ramsden:
    //http://james-ramsden.com/remotely-change-value-of-number-slider-grasshopper/
    int n = x.Count; //parameter Size
    int p = 5; //population size
    if(run)
    {
      if((nTimes != minimum))
      {
        minimum = nTimes;
        counter = nTimes;
      }
      double[,] x_new = new double[p, n];
      var input = Component.Params.Input[0];//gets sliders in
      for(int i = 0; i < p;i++)
      {
        for(int j = 0; j < n;j++)
        {
          Random rnd = new Random(Guid.NewGuid().GetHashCode());
          x_new[i, j] = rnd.NextDouble();
          var slider=input.Sources[j] as Grasshopper.Kernel.Special.GH_NumberSlider;//i_th slider
          slider.SetSliderValue((decimal) x_new[i, j]);
          slider.Slider.DrawControlBackground = true;
          slider.Slider.DrawControlBorder = true;
          slider.Slider.ControlEdgeColour = System.Drawing.Color.Blue;
          slider.Slider.ControlBackColour = System.Drawing.Color.Aquamarine;
          //slider.Slider.DecimalPlaces = 4;
        }
      }
      InRun = run;
      if(counter < 0){InRun = false;}
      A = x_new;
      if(InRun){GrasshopperDocument.ScheduleSolution(100, ScheduleCallback);}
      //}
    }
  }

  // <Custom additional code> 
  // <Custom additional code>
  //private bool _run = false;
  private int minimum = -1;
  private int counter = -1;
  private bool InRun = false;
  private void ScheduleCallback(GH_Document document)
  {
    counter--;
    Component.ExpireSolution(false);
  }

Any hint is very much appreciated!
Cemre


(David Rutten) #2

You’re not supposed to change slider values from within your RunScript() method. It causes an expiration during a solution. You should assign the slider value from within your ScheduleCallback().


(David Rutten) #3

The problem seems to be that you have j sliders, but you’re assigning values to them i times in a row without running a new solution in between. So the values you assign are overwritten whenever your outer i loop increments.

I’m not entirely sure I get what you’re trying to do, but it looks as though you want to assign some random numbers to a bunch of sliders, run a solution, then assign some other random numbers to the same sliders, run a solution again, etc. repeat N times. Correct?


(Cemreugurlu) #4

Hi, thank you for your response. Yes, I have j sliders and I want to assign values to them i times. In each loop increment (i), I want to take a solution that consisting of parameter values. And I want to repeat this process nTimes. I add another example code below, which includes a “mass addition” of sliders solution and I am trying to store each mass addition solution in a list below:slider update.gh (9.5 KB)

 private void RunScript(List<double> x, int nTimes, bool run, double solution, ref object A, ref object B)
  {
    //based on code snippets from David Rutten and James Ramsden:
    //http://james-ramsden.com/remotely-change-value-of-number-slider-grasshopper/

    int n = x.Count; //parameter Size
    int p = 5; //pop size
    double[,] population = new double[5, 3]{{1.2,2.3,5.2},{4.4,2.1,3.5},{2.5,0.5,3.1},{4.2,2.7,3.6},{1.4,0.4,2.7}};
    List<double> result = new List<double>(p);
    if(run)
    {
      if((nTimes != minimum))
      {
        minimum = nTimes;
        counter = nTimes;
      }
      double[,] x_new = new double[p, n];
      var input = Component.Params.Input[0];//gets sliders in

      for(int i = 0; i < p;i++)
      {
        for(int j = 0; j < n;j++)
        {
          //Random rnd = new Random(Guid.NewGuid().GetHashCode());//make the psuedo-random as random as possible
          x_new[i, j] = population[i, j];
          var slider=input.Sources[j] as Grasshopper.Kernel.Special.GH_NumberSlider;//i_th slider
          slider.SetSliderValue((decimal) x_new[i, j]);
          slider.Slider.DrawControlBackground = true;
          slider.Slider.DrawControlBorder = true;
          slider.Slider.ControlEdgeColour = System.Drawing.Color.Blue;
          slider.Slider.ControlBackColour = System.Drawing.Color.Aquamarine;
        }
        result.Add(solution);
      }
      InRun = run;
      if(counter < 0){InRun = false;}
      A = x_new;
      B = result;
      if(InRun){GrasshopperDocument.ScheduleSolution(100, ScheduleCallback);}
    }
  }

  // <Custom additional code> 
  // <Custom additional code>
  //private bool _run = false;
  private int minimum = -1;
  private int counter = -1;
  private bool InRun = false;
  private void ScheduleCallback(GH_Document document)
  {
    counter--;
    Component.ExpireSolution(false);
  }
  // </Custom additional code> 
}

(David Rutten) #5

Okay, this code definitely needs to run outside of solutions. In plug-ins like Galapagos the code that modifies sliders is triggered by a UI event (like a button press). If you don’t want to build a user-interface for your process, then you must find some other way to get your looping code out of the solution.

Scheduling is as good a way as any other. There is a slight complication in that if the sliders you’re modifying are attached to the script component, then a changing slider will trigger your RunScript method again, so you have to protect against this.

I’ll try and come up with an example.


(David Rutten) #6

RandomOptimiser.gh (20.7 KB)

This is a lot easier if you can just have a straight up loop, but that requires your code callstack originates from outside the solution process. So it pretty much requires a UI event to kick things off which in turn requires some UI.

The script I wrote uses an _iterations counter which is decremented every time a solution runs. When this counter hits zero, we’re done processing.


(Cemreugurlu) #7

Hi Thank you for your kind response. It works nicely! But, the code you shared still doesn’t work with two dimensional array and produces one missing output value e.g. iteration is 10 but output values count is 9. The idea maybe different than mine. I tried to use List to change number slider value instead of a random number as in the attachment. I very much appreciate if you can help in this code. Best, Cemre RandomOptimiser (2).gh (7.7 KB)


(Cemreugurlu) #8

In order to change the value of “j” number of sliders “i” times and to repeat this process “n” times, do we need to define two _iterations counter, and also two _running in your code?


(David Rutten) #9

No, you need two loops, but the inner loop which iterates over sliders can run to completion without any counters. I don’t make the value matrix ahead of time, but other than that my approach is the same as what you’re after.


(Cemreugurlu) #10

I have one more question: to change number slider values with two dimensional matrix for populated algorithms, I think I need one more loop as given below (_iter: which is equal to pop size) other than _iterations (which means generation count). However, it seems only one loop in the following code:

private void RunScript(DataTree Sliders, int Runs, int PSize, ref object A)
{
// If we’re not running, that means we’re setting up a new solver run.
if (!_running)
{
_running = true;
_random = new Random();
_iterations = Math.Max(Runs, 1);
_iter = PSize;
_sliders = FindSliders();
_param = FindParam();
}
// If we’re not done with this process, schedule another solution.
// Otherwise we’re done so reset the _running flag.
if (_iterations > 0)
GrasshopperDocument.ScheduleSolution(10, ScheduleCallback);
else
_running = false;
}
//
private bool _running = false;
private int _iterations;
private int _iter;
private Random _random;

private Grasshopper.Kernel.Special.GH_NumberSlider _sliders;
private Grasshopper.Kernel.Parameters.Param_Number _param;
private void ScheduleCallback(GH_Document document)
{
double[,] population = new double[5, 3]{{1.2,2.3,5.2},{4.4,2.1,3.5},{2.5,0.5,3.1},{4.2,2.7,3.6},{1.4,0.4,2.7}};
List xreals = new List(15);
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 3; j++)
{
xreals.Add(population[i, j]);
}
}
xreals.ToArray();
_iterations–;
for (int k = 0; k < _iterations; k++)
{
for (int i = 0; i < _iter; i++)
{
foreach (Grasshopper.Kernel.Special.GH_NumberSlider slider in _sliders)
{
slider.SetSliderValue((decimal) _random.NextDouble() * 10); //for now randomly, we want to assign numbers from population matrix later
}
}
}
}
private Grasshopper.Kernel.Special.GH_NumberSlider FindSliders()
{
var input = Component.Params.Input[0];
var sliders = new List<Grasshopper.Kernel.Special.GH_NumberSlider>();
foreach (var source in input.Sources)
{
var slider = source as Grasshopper.Kernel.Special.GH_NumberSlider;
if (slider != null)
sliders.Add(slider);
}
return sliders.ToArray();
}
private Grasshopper.Kernel.Parameters.Param_Number FindParam()
{
var input = Component.Params.Input[1];
foreach (var source in input.Sources)
{
var param = source as Grasshopper.Kernel.Parameters.Param_Number;
if (param != null)
return param;
}
return null;
}