3
$\begingroup$

Looking for a methodology where the user can move the points of a Locator and then click a button to ask for calculations to proceed. The motivation for this is that the calculations of the full system are time consuming, perhaps several minutes. ContinuousAction -> False for the LocatorPane would cause the calculations to proceed after the user finished moving each Locator point. Since the user may want move move, add, or delete, several Locator points before starting the calculations, I want to place the the execution of the calculations under the user's control.

Below is a simple example of this using Manipulate. Manipulate with buttons

The Manipulate uses TrackedSymbols to identify when a hidden parameter, iUpdate, is changed. Clicking the buttons will change the value of iUpdate. In the body of the Manipulate, the code uses iUpdate to do a meaningless calculation, so that the Manipulate recognizes that "something" affected by the tracked symbol has changed.

Some helpful information related to this question, includes a discussion of making buttons for manipulate for Manipulate, the tutorial pages for Manipulate and Dynamic and the Advanced Dynamic tutorial page

Below is a working methodology. Am asking for alternative or improvements to this implementation.

Also, one other question: in the code for each button, the change to the value of iUpdate is placed after the FinishDynamic statement -- not sure about this; however, placing it before the FinishDynamic created problems.

Below is the code use for this example. Thanks for your comments and help.

Clear[adjYvalues]
adjYvalues[xyPts_, yAdj_?NumericQ] := Module[{},
  xyPts + ConstantArray[{0, yAdj}, Length@xyPts]
  ]
Clear[makeLcpInterpFunc]
makeLcpInterpFunc[xyPts_] :=  
 Module[{xyNew, xMin = 0, xMax = 2 π, xMinData, xMaxData},
  xyNew = Sort[xyPts];
  xyNew = 
   If[xMin < xyNew[[1, 1]], Prepend[xyNew, {xMin, xyNew[[1, 2]]}], 
    xyNew];
  xyNew = 
   If[xMax > xyNew[[-1, 1]], Append[xyNew, {xMax, xyNew[[-1, 2]]}], 
    xyNew];
  Interpolation[xyNew, InterpolationOrder -> 1]
  ]
Clear[makeLcpBackground]
makeLcpBackground[f1_, xyPts_, f2_] := Module[{fInterp, epsX = 0.05},
  (*fInterp = makeLcpInterpFunc[xyPts];*)
  Plot[{f1[x], f2[x]}, {x, 0, 2 π}
   , PlotRange -> {{0 - epsX, 2 π + epsX}, {-1.3, +1.3}}
   , Frame -> True
   , GridLines -> {{0, π/2, 2 π/2, 3 π/2, 4 π/2}, 
     Automatic}
   , ImageSize -> 600
   , Epilog -> {Red, AbsolutePointSize[4], Point[xyPts] }] 
  ]

manipLocPaneWithButtons = 
 DynamicModule[{lcPtsInit = {{0, 0}, {π/2, 
      1}, {3 π/2, -1}, {4 π/2, 0}}, lcPts 
   , status = 
    Style["Ready to start", Bold, 16, RGBColor[0.27, 0.5, 0.17]]
   , yAdj = 0
   , iCount = 0
   , fRef = Sin
   , fInterp
   , lcpBackground
   , errCurrent, errPrevoius},
  Manipulate[
   (*analysis *)
   (*lcPts = If[iOpt \[Equal] 1, findNewYvaluesFromFunc[Sin, {0, 
   2π}, {-1.5, 1.5},lcPts,3] , lcPts];*)
   iCount = iUpdate;
   lcPts = adjYvalues[lcPts, yAdj];
   yAdj = 0;
   fInterp = makeLcpInterpFunc[lcPts ] ;
   lcpBackground = makeLcpBackground[fRef, lcPts, fInterp] ;
   (*display *)
   Grid[{{status ~~ " iCount:" ~~  ToString[iCount] 
      }, (*end Row 1*)
     {
      LocatorPane[ Dynamic[lcPts], lcpBackground, 
       LocatorAutoCreate -> True, ContinuousAction -> False]
      }(*end Row 2*)
     }(*end Grid{{}} *)
    , Frame -> All
    ] (*end Grid[]*)
   (*controls*)
   , Delimiter
   , Style["Calculation Control", Black, Italic,  12]
   , Delimiter
   , Button["Reset"
    , status = Style["reseting points ...", Darker@Blue, 14, Bold];
    lcPts = lcPtsInit;
    FinishDynamic[];
    iUpdate = 0;
    , Method -> "Queued"
    ]
   , Delimiter
   , Button["Update / Calculate"
    , yAdj = +0.0;
    status = Style["updating ...", Darker@Blue, 14, Bold];
    FinishDynamic[];
    iUpdate += 1;
    , Method -> "Queued"
    ]
   , Delimiter
   , Button["Move Up"
    , yAdj = +0.1;
    status = Style["move up ...", Darker@Blue, 14, Bold];
    FinishDynamic[];
    iUpdate += 1;
    , Method -> "Queued"
    ]
   , Delimiter
   , Button["Move Down"
    , yAdj = -0.1;
    status = Style["move down ...", Darker@Blue, 14, Bold];
    FinishDynamic[];
    iUpdate += 1;
    , Method -> "Queued"
    ]
   , {iUpdate, None}
   , ControlPlacement :> {Left, Left}
   , TrackedSymbols :> {iUpdate, status}
   , Initialization :> (
     lcPts = lcPtsInit;
     iUpdate = 0;
     iCount = 0;
     )
   , SynchronousUpdating -> False
   , LocalizeVariables -> False
   ] (*end Manipulate*)
  ];(*end DynamicModule*)

Button["start Test", CellPrint@manipLocPaneWithButtons ]
$\endgroup$
  • $\begingroup$ Have you seen this answer? What I wouldn't like in your example is that you mix the GUI with code that does calculations with the data (aka your underlying "model"). The approach I've given in the linked answer should work for your use-case although this is only a small demonstration. $\endgroup$ – halirutan Oct 8 '20 at 1:34
  • $\begingroup$ Was not aware of that. Thanks for identifying it. $\endgroup$ – user6546 Oct 8 '20 at 4:16

Your Answer

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

Browse other questions tagged or ask your own question.