1
$\begingroup$

I would like to be able to evaluate a function based on multiple Slider values by pushing a Button that creates the function, produces the data, and also produces a plot in the same "GUI" window. This is like a Manipulate for a plot in many ways, but I want the update to be button-driven and not slider-driven (since the function evaluation is expensive). I expected to easily find an example, but my search-engine-foo failed me. To make this concrete, consider a distilled case: 2 sliders to set a slope and intercept of a univariate affine function, and then 1 button that produces the resulting function, evaluates it on a given list of inputs, and then produces a list plot (ideally in the "GUI" pane, or whatever). For this simplified example, the two parts to be integrated are something like

Column[{
  Grid[{
    {"b:", Manipulator[Dynamic[b0], ContinuousAction -> False]},
    {"m:", Manipulator[Dynamic[m0], ContinuousAction -> False]}
    }],
  Button["Run", With[{b = b0, m = m0},
    Print@With[{pts = Map[x |-> m*x + b, Range[10]]},
      ListPlot[pts]
      ]]]
  }]

However, ideally the variables will be local to the GUI, and the figure will be within the GUI frame and will update when the button is pressed (e.g., by updating the contents of a Panel). I definitely would not mind using a FormObject to achieve this.

$\endgroup$
1
  • 1
    $\begingroup$ You can use TrackedSymbols to limit the automatic updating to only some symbols. If you add a dummy symbol (e.g. with manipulate spec {{dummy,0}, None}), you can track updates to that symbol & change it when clicking the button $\endgroup$
    – Lukas Lang
    Jul 15, 2023 at 18:03

2 Answers 2

1
$\begingroup$

Here is a relatively straightforward way to achieve what you seem to be after:

DynamicModule[{b = 0, m = 1, result = ""},
  Column[{
   Grid[{
     {"b:",Manipulator[Dynamic[b]]}, 
     {"m:",Manipulator[Dynamic[m]]}
   }],
   Button["Run",
     result = "calculating...";
     Pause[1];
     result = ListPlot[Map[x |-> m*x + b, Range[10]]],
     Method -> "Queued"
   ],
   Dynamic[result]
 }]
]

Note that I added an artificial Pause to simulate the "expensive" calculation and added a simple progress info as you mentioned that the calculation itself is expensive and the button only makes sense in that case. I also added Method->"Queued" which will avoid that you run into timeouts when exceeding the corresponding limits which I think is also important when your calculation really is expensive...

Here is a slightly more elaborated version which:

  • allows to abort
  • extract the lengthy calculation in an extra function
  • takes full control of all gui-updates

I think this probably can serve as a starting point for many similar use cases:

DynamicModule[{b = 0, m = 1, result = "", running = False},
  Column[
    {
      Grid[{
        {"b:", Manipulator[Dynamic[b]]}, 
        {"m:", Manipulator[Dynamic[m]]}
      }]
      ,
      Dynamic[
        If[Not @ running,
          Button["Run", 
            result = ProgressIndicator[Appearance -> "Indeterminate"]; 
            running = True; 
            WithCleanup[
              result = CheckAbort[runCalculation[m,b],"aborted!"]
              , 
              running = False
            ],
            Method -> "Queued"
          ],
          Button["Abort", FrontEndToken["EvaluatorAbort"], Evaluator->None]
        ],
        TrackedSymbols :> {running}
      ],
      Dynamic[result, TrackedSymbols :> {result}]
    }
  ]
]
$\endgroup$
3
  • $\begingroup$ This seems quite satisfactory. I upvoted but I'm going to play with it a bit before accepting. Thanks! $\endgroup$
    – Alan
    Jul 24, 2023 at 21:49
  • $\begingroup$ For the first approach, "calculating" won't appear; right? Also, I initialized result with a blank graphic (just axes) for a bit better initial look. Still happy! $\endgroup$
    – Alan
    Jul 24, 2023 at 21:52
  • $\begingroup$ Good to heat this helped. The problem with "calculating" not appearing I also noticed, but for me it was only happening for the first click on the run button, and I think it completely was gone in the second version with the explicit TrackedSymbol option for the last Dynamic. Is it persisting for you? $\endgroup$ Jul 27, 2023 at 13:27
4
$\begingroup$
$Version

(* "13.3.0 for Mac OS X ARM (64-bit) (June 3, 2023)" *)

Clear["Global`*"]

Use TrackedSymbols so that Manipulate only updates when Run is toggled.

Manipulate[
 If[run, m = mv; b = bv; run = False];
 pts = Table[{x, m*x + b}, {x, 0, 10}];
 ListPlot[pts,
  AxesLabel -> (Style[#, 14] & /@ {HoldForm[x], HoldForm[y]})],
 {{mv, 1, "slope"}, 0.1, 10, 0.1, Appearance -> "Labeled"},
 {{bv, 0, "intercept"}, -10, 10, 0.1, Appearance -> "Labeled"},
 {{run, False, Style["Run", 14, Blue]}, {True, False}, 
  ControlType -> Checkbox},
 SynchronousUpdating -> False,
 TrackedSymbols -> run]

enter image description here

$\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.