10
$\begingroup$

As the title suggests, I am trying to use a Plot inside a Manipulate which has its controls defined programmatically. Mathematica doesn't plot anything and gives me no error, so I don't know what else to do.

I have create a simplified example of my problem (my original program is more complex):

Manipulate[
 Evaluate[
  Plot[{a[1] + a[2] x}, {x, 0, 1}, PlotLabel -> {a[1], a[2]}]
  ]
 ,
 Evaluate[
  Sequence @@ ({{a[#], 1, Subscript[a, #]}, Range[3], 
       ControlType -> SetterBar} & /@ Range[2])
  ]
 ]

As you can see by playing with this program, the label of the Plot does update, but the Plot is empty.

Any help will be much appreciated. (And I'm sorry if this is a duplicate question; I've tried many of the solutions on similar questions, many of them using With, but I couldn't make the Plot appear and I'm frustrated now. Maybe you can help me?)


Edit:

This could be an updated example (it is very close to what I have and am trying to do):

DynamicModule[{f},
 f = {x, x^2};
 Manipulate[
  Plot[Pick[f, a /@ Range[2]], {x, 0, 1}, PlotLabel -> {a[1], a[2]}]
  ,
  Evaluate[
   Sequence @@ ({{a[#], True, Subscript[a, #]}, {True, False}} & /@ 
      Range[2])
   ]
  ]
 ]

In essence, I have an arbitrary-length list of curves I'd like to plot (so I need to be able to program how many controls appear: one per curve) but also I want to be able to select which of those curves are plotted (which is the purpose of the controls). I hope this makes sense (English is not my main language).


Final edit:

As a token of my thanks to all those helping me, I have created an example that merges all the wisdom shared on the answers. Thank you very much to all of you!

DynamicModule[{n = 3, f, a, po},
 f = x^# & /@ Range[n];
 With[{
   arange = a /@ Range[n]
   },
  Manipulate[
   po = Flatten@Position[arange, True];
   Plot[
    Evaluate@Pick[f, arange], {x, 0, 1},
    PlotLabel -> po,
    PlotStyle -> (ColorData[97] /@ Range[n])[[po]]
    ]
   
   ,
   Evaluate[
    Row[{
      Sequence @@ (Control[{{a[#], True, Subscript[a, #]}, {True, 
             False}}] & /@ Range[n])
      }, Spacer[20]]
    ]
   ]
  ]
 ]

This is what I was looking for, I spent three days trying on my own, and you help me achieve it in a few minutes. Thank you!

$\endgroup$
5
  • 3
    $\begingroup$ Remove the Evaluate from around your Plot. $\endgroup$
    – lericr
    Jun 17, 2023 at 23:50
  • $\begingroup$ Thank you, Lericr. Now the example does plot. Unfortunately, this only means that my example is not a reflection of my problem (if I remove the Evaluate from my original program if doesn't work). I'll try to come up with a better (although longer) example. But it will have to be tomorrow. $\endgroup$
    – RoberRM
    Jun 17, 2023 at 23:56
  • $\begingroup$ Well, I couldn't let it go, so I have updated my question with a second version of the code. This time it is much closer to what I have. $\endgroup$
    – RoberRM
    Jun 18, 2023 at 0:04
  • $\begingroup$ closely related Q/A: Plot many curves on the same graph $\endgroup$
    – kglr
    Jun 18, 2023 at 4:38
  • $\begingroup$ Thank you, kglr, for the link. I will have to study it more closely as they created many cool things I would like to implement on my Plots. But the plot itself was just another example for my bigger (hidden) question: is there a way to programmatically create controls in any situation (not just for a single plot)? And the answer is yes, and I've learned to do it. Thank you! $\endgroup$
    – RoberRM
    Jun 18, 2023 at 16:04

2 Answers 2

7
$\begingroup$

Either change the first Plot argument to

Pick[f, {a[1], a[2]}]

or inject the a's:

DynamicModule[{f}, f = {x, x^2};
 With[{arange = a /@ Range[2]},
  Manipulate[
   Plot[
    Pick[f, arange],
    {x, 0, 1},
    PlotLabel -> {a[1], a[2]}],
   Evaluate[
    Sequence @@ ({{a[#], True, Subscript[a, #]}, {True, False}} & /@ 
       Range[2])]]]
 ]

Someone on this site once said, "Manipulate is a strange beast." It rewrites your code in ways that don't always respect the way the ordinary kernel-based Mathematica works. In this case, the control variables need to be rewritten so they can be instantiated as front-end dynamic-module variables in a way that no matter how many times you paste the output, you always get new "localized" variables.

In this case it's a[1] and a[2] that are given new variable names. They no longer have the head a, and the a in a /@ Range[2] does not refer to these new variables.

$\endgroup$
4
  • 1
    $\begingroup$ One might think LocalizeVariables -> False would solve this problem (at the risk of making a global). But alas, a[1] and a[2] are localized anyway. I guess Manipulate really does not like indexed variables all that much. At least it allows code construction like the OP's $\endgroup$
    – Michael E2
    Jun 18, 2023 at 3:38
  • $\begingroup$ Thank you very much, Michael! Your answer (inject the as) was exactly what I was looking for. Maybe Bob's approach (use a TogglerBar ) could be easier for the vast majority of my new projects, but in the application I'm working on right now it is not possible to use it. Yours, on the other hand, is perfect!! I will wait a few days to accept yours as the final answer, in case someone else has a better solution. Thank you very much!! $\endgroup$
    – RoberRM
    Jun 18, 2023 at 16:11
  • 1
    $\begingroup$ @RoberRM You're welcome. I was principally trying to explain how to fix your approach and why your initial ideas ran into trouble with Manipulate, so that you might better understand how Manipulate works. For simple things, Manipulate is great. For more involved programming problems, like indexed variables, there are tricky pitfalls in what Manipulate does to make the simple things simple. The more familiar you are with the available tools, such as TogglerBar, the easier it becomes to keep things simple. $\endgroup$
    – Michael E2
    Jun 18, 2023 at 16:39
  • $\begingroup$ Definitely, Michael! I appreciate very much the explanations you gave me and the two options you wrote. And I will certainly try to use simpler solutions when available, so thank you for the advice (I'll add ToggleBar to my collection of tools too). $\endgroup$
    – RoberRM
    Jun 18, 2023 at 19:02
8
$\begingroup$
$Version

(* "13.2.1 for Mac OS X ARM (64-bit) (January 27, 2023)" *)

Clear["Global`*"]

f = x^Range[5];

TogglerBar enables choosing any or all functions for plotting.

Manipulate[
 funcs = If[funcs === {}, {1}, Sort[funcs]];
 Plot[Evaluate[f[[funcs]]], {x, 0, 1},
  PlotStyle -> (ColorData[97] /@ Range[Length[f]])[[funcs]],
  PlotLegends -> Placed[f[[funcs]], {.25, .7}]],
 {{funcs, Range[1, Length[f], 2], "functions"},
  Thread[Range[Length[f]] -> f],
  ControlType -> TogglerBar}]

enter image description here

$\endgroup$
1
  • $\begingroup$ Thank you very much, Bob! Although this was not my initial question (as I wanted to learn how to create controls programatically that worked in every situation) you have taught me something new and very cool and I'll be sure to try your approach with ToogleBar in the future. Also, I love (and am amazed) at how well (and quickly) you write code that takes into account many situations (for example, the user deselecting all toggles). Congratulations on that! And thank you again!! $\endgroup$
    – RoberRM
    Jun 18, 2023 at 16:09

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.