2
$\begingroup$

Is it possible to find out which variable changed in manipulate?

I've got a solution here, but it's kludgy, and I wonder if there is any way to get this information directly from the system?

(If you have any trouble understanding the question, just run this code, which works correctly.)

Module[{oldParams}, 
   ControlChanged[parameters_List] := 
      ControlActive[
         Position[MapThread[UnsameQ, {oldParams, parameters}], True][[1, 1]], 
         oldParams = parameters; 
         None
       ]
]; 

Manipulate[
   ControlChanged[{x, y, z}], 
   {x, 0, 1}, 
   {y, 0, 1},
   {z, 0, 1}]
$\endgroup$
  • $\begingroup$ It may matter what do you want and what do you want to use it for. Is the index all you need? $\endgroup$ – Kuba Oct 11 '16 at 18:29
  • $\begingroup$ Sure. I want to recalculate based on what changed. I have a large set of variables which have relations, e.g. x + y == 1. If the user changes x, I want to recalculate y and leave x alone. If the user changes y, I want to recalculate x and leave y alone. $\endgroup$ – Charles Gillingham Oct 11 '16 at 20:28
  • $\begingroup$ If the user changes x, I want to recalculate y and leave x alone. If the user changes y, I want to recalculate x and leave y alone be careful with this. You'll end up in an infinite loop. If you modify a control variable inside Manipulate expression, it will automatically trigger another update and you will easily go to infinite loop, having to terminate the kernel from task bar. The basic rule of thumb is Manipulate, is to treat control variables as read-only. There are ways to work around this, but this will require different design. $\endgroup$ – Nasser Oct 12 '16 at 1:12
2
$\begingroup$

Here's a function track[var] that will set a "tracking" variable var to the held variable which changes. Just use it as the setting for TrackingFunction, wrapped with Evaluate[]. Why is Evaluate needed? Good question. It works with it and it doesn't work without it. Manipulate holds its arguments, but evaluates some of them at certain points during the construction of the output. In particular, it tests the option value for TrackingFunction. (It will revert to the default function, if the value is not a valid Function or List of functions.) Evidently, Evaluate is important somewhere along the line.

ClearAll[track];
SetAttributes[track, HoldAll];
track[var_] := {Function[{val, u}, var = Hold[u], HoldRest], 
   Function[{val, u}, u = val, HoldRest], # &};
Manipulate[Column@{
   {var, Hold[x], var === Hold[x]},
   Grid[{{x, y, z}}, Frame -> All, 
     Background -> {{Hold[x], Hold[y], Hold[z]} /. {var -> Red, _Hold -> None}, None}]
   },
 {x, 0, 1, TrackingFunction -> Evaluate@track[var]},
 {y, 0, 1, TrackingFunction -> Evaluate@track[var]},
 {z, 0, 1, TrackingFunction -> Evaluate@track[var]},
 {{var, None}, None}]

Mathematica graphics

Mathematica graphics

Mathematica graphics

$\endgroup$
  • $\begingroup$ "TrackingFunction" is the sort of thing I was looking for. $\endgroup$ – Charles Gillingham Oct 14 '16 at 6:01
  • $\begingroup$ This duplicates the code I wrote above: $\endgroup$ – Charles Gillingham Oct 14 '16 at 22:52
  • $\begingroup$ Module[{cc}, cc = None; Manipulate[ cc, {x, 0, 1, TrackingFunction -> ((cc = 1; x = #) &)}, {y, 0, 1, TrackingFunction -> ((cc = 2; y = #) &)}, {z, 0, 1, TrackingFunction -> ((cc = 3; z = #) &)} ] ] $\endgroup$ – Charles Gillingham Oct 14 '16 at 22:53
3
$\begingroup$

If I understand you right, One way to do this, is to use the second argument of dynamics. Each dynamic control can use this as a place to record that it was selected, and update a common variable.

Then when Manipulate updates, simply examine this variable to see which control caused the Manipulate to update.

In this example, this common variable controlVariableName is just string of the name of the control, but you can make it anything else you want. Also the common variable controlVariableItself will contain the control variable value itself. i.e. x or y, etc...

Manipulate[

 Grid[{
   {Style[Row[{"changed [", controlVariableName, "] control!"}], Red],
     SpanFromLeft},
   {Style[Row[{"Its current value [", controlVariableItself , "]"}], 
     Red], SpanFromLeft},
   {"x=", x, "y=", y, "z=", z}
   }],

 (*controls*)
 Grid[{{"x", 
    Manipulator[
     Dynamic[x, {x = #, controlVariableName = "x", 
        controlVariableItself = x} &], {0, 1, .1}, ImageSize -> Tiny],
     Dynamic[x]}, {"y", 
    Manipulator[Dynamic[y, {y = #, controlVariableName = "y", 
        controlVariableItself = y} &], {0, 1, .1}, ImageSize -> Tiny],
     Dynamic[y]}, {"z", 
    Manipulator[Dynamic[z, {z = #, controlVariableName = "z", 
        controlVariableItself = z} &], {0, 1, .1}, ImageSize -> Tiny],
     Dynamic[z]},
   {Button[
     Text@Style["press me", 12], { controlVariableName = "Button", 
      controlVariableItself = "Button"}]}
   }],

 {{controlVariableName, "x"}, None},
 {{controlVariableItself, x}, None},
 {{x, 0}, None},
 {{y, 0}, None},
 {{z, 0}, None},
 TrackedSymbols :> {x, y, z}
 ]

enter image description here

$\endgroup$
  • $\begingroup$ This is more like it, but can I get to that second argument from Manipulate? Or do I have to break it all out that way? $\endgroup$ – Charles Gillingham Oct 11 '16 at 20:49
  • $\begingroup$ @CharlesGillingham If you mean pass the variable value itself, instead of just its name as string as in the example? Sure. I've updated the answer. $\endgroup$ – Nasser Oct 11 '16 at 22:31
  • $\begingroup$ These are interesting applications of the answer to this question. $\endgroup$ – Charles Gillingham Oct 15 '16 at 1:29
  • $\begingroup$ What I personally want to do is enforce constraints between a set of parameters. E.g. If x+y == 1, then I want {x,0,1,TrackingFunction->((x = #; y = 1 - x;&)} $\endgroup$ – Charles Gillingham Oct 15 '16 at 1:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.