3

I have a Manipulate that has dynamic variables that I want to use in the displayed expression and use in the parameters for the manipulate controls. I only calculate these dynamic variables when one of the TrackedSymbols changes using Refresh. It all works. However, these variables are in scope outside of the Manipulate. I would like them not to be as they are only needed inside of it.

The variables whose scope I want to restrict are lower and upper. After I execute this they are available in the notebook to query and alter. How do I restrict their scope? The two function definitions are included below as well.

Manipulate[

 Refresh[
  lower = distPlotRange[distribution, -1, 4];
  upper = distPlotRange[distribution, 1, 4];
  fillRange = {Max[#[[1]]], Min[#[[2]]]} &[Transpose[{fillRange, {lower, upper}}]];
  ,
  TrackedSymbols :> {distribution}
  ];

 plotPdfInterval[distribution, Interval[fillRange],
  PlotRange -> {{lower, upper}, {0, Full}},
  PlotStyle -> Orange,
  Filling -> Axis, FillingStyle -> Blue,
  AxesOrigin -> {lower, 0}]
 ,
 {fillRange, Dynamic@lower, Dynamic@upper, IntervalSlider, Method -> "Push", MinIntervalSize -> 0.01},
 {{distribution, NormalDistribution[]}, 
   {NormalDistribution[], StudentTDistribution[15], LogNormalDistribution[5, 1]}, 
   PopupMenu},
 Initialization :> (fillRange = {-1, 1};)]

First some utility functions:

plotPdfInterval[dist_?DistributionParameterQ, interval_, opts : OptionsPattern[Plot]] :=
 Module[{distPdf = PDF[dist, #] &},
   Plot[distPdf[x], {x, Min[interval], Max[interval]}, opts]
  ]

distPlotRange[dist_?DistributionParameterQ, boundary_?(MemberQ[{-1, 1}, #] &), stdDevs_?NumberQ] :=
 With[{qt = Quantile[dist, If[boundary == -1, 0, 1]]},
  If[NumberQ@qt, qt, 
   Mean@dist + boundary stdDevs StandardDeviation@dist]]
Edmund
  • 42,267
  • 3
  • 51
  • 143

2 Answers2

3

You can insert your Manipulate[...] in a DynamicModule[{lower,upper},...]:

DynamicModule[{lower, upper},
 Manipulate[
  Refresh[
   lower = distPlotRange[distribution, -1, 4];
   upper = distPlotRange[distribution, 1, 4];
   fillRange = {Max[#[[1]]], Min[#[[2]]]} &[
     Transpose[{fillRange, {lower, upper}}]];,
   TrackedSymbols :> {distribution}
   ];
  plotPdfInterval[distribution, Interval[fillRange], 
   PlotRange -> {{lower, upper}, {0, Full}}, PlotStyle -> Orange, 
   Filling -> Axis, FillingStyle -> Blue, 
   AxesOrigin -> {lower, 0}], {fillRange, Dynamic@lower, 
   Dynamic@upper, IntervalSlider, Method -> "Push", 
   MinIntervalSize -> 0.01}, {{distribution, 
    NormalDistribution[]}, {NormalDistribution[], 
    StudentTDistribution[15], LogNormalDistribution[5, 1]}, 
   PopupMenu}, Initialization :> (fillRange = {-1, 1};)]
 ]

DynamicModule[{lower,upper},...] saves the variables lower and upper when the notebook is closed. You don't need that. I don't think that is a problem, but if it is, add the option UnsavedVariables -> {lower,upper}.

andre314
  • 18,474
  • 1
  • 36
  • 69
2

One can localize the scope of variables to Manipulate by adding them as arguments to Manipulate with ControlType None. For your case

Manipulate[Refresh[lower = distPlotRange[distribution, -1, 4];
  upper = distPlotRange[distribution, 1, 4];
  fillRange = {Max[#[[1]]], Min[#[[2]]]} &[
    Transpose[{fillRange, {lower, upper}}]];, 
  TrackedSymbols :> {distribution}];
 plotPdfInterval[distribution, Interval[fillRange], 
  PlotRange -> {{lower, upper}, {0, Full}}, PlotStyle -> Orange, 
  Filling -> Axis, FillingStyle -> Blue, 
  AxesOrigin -> {lower, 0}], {fillRange, Dynamic@lower, Dynamic@upper,
   IntervalSlider, Method -> "Push", 
  MinIntervalSize -> 0.01}, {{distribution, 
   NormalDistribution[]}, {NormalDistribution[], 
   StudentTDistribution[15], LogNormalDistribution[5, 1]}, 
  PopupMenu}, {lower, None}, {upper, None},
 Initialization :> (fillRange = {-1, 1};)]
Karsten7
  • 27,448
  • 5
  • 73
  • 134