6
$\begingroup$

I have a Manipulate whose controls are generated based on a parameter in a containing Manipulate:

Manipulate[
 With[{f = Table[c[i], {i, n}], 
   controls = Sequence @@ Table[{{c[i], 0}, -1, 1}, {i, n}]}, 
  Manipulate[f, controls, 
   Button["Random", Do[c[i] = RandomReal[{-1, 1}], {i, n}]]]], {n, {3, 4, 5}}]

When I hit the "Random" button, to assign a random set of values to the parameters in the inner manipulate, chaos ensues, e.g.: "Manipulate argument {{-0.975768,0},-1,1} does not have the correct form for a variable specification." If I change the above slightly, for example:

Manipulate[
 With[{f = Table[c[i], {i, n}], 
   controls = Sequence @@ Table[{{c[i], 0}, -1, 1}, {i, n}]}, 
  Manipulate[f, controls, 
   Button["Random", c[1] = RandomReal[{-1, 1}]]]], {n, {3, 4, 5}}]

I don't get similar errors (and c[1] gets randomly assigned a value as expected). I can't wrap my head around what's going on... what's the relevant difference between these two examples? How can I assign to a parameter as in the second example but using a loop as in the first?

Edit: Turns out I also have additional controls beyond the c[i], whose position should be maintained through the randomization of the c[i]. For example, using belisarius' technique resets r here:

Manipulate[
 With[{f = Table[c[i], {i, n}], 
   controls = Sequence @@ Table[{{c[i], s[i]}, -1, 1}, {i, n}]}, 
  Manipulate[Append[f, r], controls, {{r, 0}, -1, 1}, 
   Button["Random", Do[s[i] = RandomReal[{-1, 1}], {i, n}]]]],
    {n, {3, 4, 5}}, Initialization -> {s@_ := 0}]

Assigning directly to e.g. c[1] as in the second example above does what I want:

Manipulate[
 With[{f = Table[c[i], {i, n}], 
   controls = Sequence @@ Table[{{c[i], 0}, -1, 1}, {i, n}]}, 
  Manipulate[Append[f, r], controls, {{r, 0}, -1, 1}, 
   Button["Random", Do[c[1] = RandomReal[{-1, 1}], {i, n}]]]], {n, {3, 4, 5}}]

What's different about assigning directly to c[1] as opposed to assigning to c[i] with i == 1?

$\endgroup$

3 Answers 3

5
$\begingroup$

You need to randomize the control initial value, not the name of the symbol associated to the control.
Perhaps this is what you want:

Manipulate[
 With[{f = Table[c[i], {i, n}], controls = Sequence @@ Table[{{c[i], s[i]}, -1, 1}, {i, n}]},
  Manipulate[f,
   controls,
   Button["Random", Do[s[i] = RandomReal[{-1, 1}], {i, n}]]]],
 {n, {3, 4, 5}}, Initialization -> {s@_ := 0}]

Mathematica graphics

$\endgroup$
3
  • $\begingroup$ Wonderful answer, of course +1. But something puzzles me. Manipulate[x, {{x,s},-1,1}, Button[Dynamic[{x,s}], s=RandomReal[{-1,1}]]] does not work; there is no connection between s and x. In the input form the current value of x is at the position of s. But when I wrap it in another Manipulate it works. The current value of x is now not visible in the input form. I would highly appreciate if you could shed a light on what happens with the local variables of the inner Manipulate when it is wrapped in another Manipulate. $\endgroup$ Oct 24, 2014 at 13:15
  • $\begingroup$ @FredSimons Perhaps that's worth another question ... $\endgroup$ Oct 24, 2014 at 13:36
  • $\begingroup$ Upvote because this answers the second part of my question as I phrased it. Turns out I slightly oversimplified my question a bit... sorry! I have additional controls beyond the c[i] whose position I want maintained through the randomization of the c[i]. I've edited the original question to include this. $\endgroup$
    – Dan
    Oct 24, 2014 at 18:18
2
$\begingroup$

Here's the solution I ended up going with:

Manipulate[
 With[{f = Table[c[i], {i, n}], 
   controls = Sequence @@ Table[{{c[i], 0}, -1, 1}, {i, n}], 
   randomize = Hold@CompoundExpression @@ 
    Table[Hold[c[i] = RandomReal[{-1, 1}]] /. i -> j, {j, n}]}, 
  Manipulate[Append[f, r], controls, {{r, 0}, -1, 1}, 
   Button["Random", ReleaseHold[randomize]]]], {n, {3, 4, 5}}]

Basically, the c[i] only get replaced by Manipulate with their localized versions if they appear exactly as c[1], c[2], etc., at the time the Manipulate is first evaluated. Later evaluation of c[i] with i == 1 for instance evaluates to the global c[1], not the one local to Manipulate. Thus, the trick is to generate the CompoundExpression that should execute on the button press outside of the Manipulate, with all of the c[1], c[2], etc. already in place, and put it inside the Manipulate using With. The Hold/ReleaseHold ensure that the CompoundExpression and assignments evaluate only at the appropriate time (when the button gets pressed).

$\endgroup$
1
$\begingroup$

Your question puzzled me for a long time. Let us have a look at your first question:

Manipulate[
  With[{f = Table[c[i], {i, n}], controls = Sequence @@ Table[{{c[i], 0}, -1, 1}, {i, n}]}, 
    Manipulate[f, controls, Button["Random", Do[c[i] = RandomReal[{-1, 1}], {i, n}]]]],
 {n, {3, 4, 5}}]

Why does it not work? You have a sliders that assign to c[1], c[2], etc, you have a button that assigns to c[1], c[2], etc, but the combination does not work. As usual with this sort of things, as soon as you understand it, it turns out to be simple. When you evalute the above command, the outer Manipulate will show the inner Manipulate for n=3. So there are controls for c[1], c[2] and c[3]. Also the button must be created. In the second argument there is a c[i], not being one of the control variables. The function Button does not evaluate the second argument, so this expression c[i] will be Global. Therefore, when we use the button, assignments will be done to Global`c[1], Global`c[2] and Global`c[3] instead of the localized control variables.

Hence, to make it work, we have to take care that the symbol c does not turn up in the button. That can be done:

Manipulate[
  With[{f = Table[c[i], {i, n}], controls = Sequence @@ Table[{{c[i], 0}, -1, 1}, {i, n}]}, 
    Manipulate[f, controls, Button["Random", f = RandomReal[{-1, 1}, {n}]]]],
  {n, {3, 4, 5}}]

On posting this answer, I saw that your solution for your extended problem (I did not see you edit before) follows more or less the same lines. I think this is a little bit shorter:

Manipulate[
  With[{f = Table[c[i], {i, n}], controls = Sequence @@ Table[{{c[i], 0}, -1, 1}, {i, n}]}, 
    Manipulate[Append[f, r], controls, {{r, 0}, -1, 1},
      Button["Random", f = andomReal[{-1, 1}, {n}]]]],
  {n, {3, 4, 5}}]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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