Manipulate auto re-evaluation logic puzzler
- To: mathgroup at smc.vnet.net
- Subject: [mg123939] Manipulate auto re-evaluation logic puzzler
- From: Frank Iannarilli <frankeye at cox.net>
- Date: Sun, 1 Jan 2012 02:26:41 -0500 (EST)
- Delivered-to: l-mathgroup@mail-archive0.wolfram.com
- Reply-to: comp.soft-sys.math.mathematica at googlegroups.com
Can someone please explain this extremely puzzling behavior of Manipulate[]? (can follow along by evaluating code later below): In short, a first Button[] push invokes A & B, where B changes a TrackedSymbols variable ("plots") that induces display update. But a second button push fails to induce display update, when it seemingly should. My Manipulate cell generates both Button[] and the usual Control[] activated interactions. The Button[] executes both processImage[] AND genPlots[] Modules defined within the Manipulate. processImage[] does expensive processing on an externally "passed-in" Global` variable named "image" to generate the intermediate result processedImage, which is localized to the Manipulate. User adjustment of the Control[] activates cheap analysis "post-processing" by genPlots[] on this intermediate result. So please evaluate the later-below defined cells, as follows: testMyPatience[]:=Manipulate[... (* Manipulate cell definition *) testMyPatience[] image= (the first of the two image= cells) Now push the Process! button - displayed results appear. Now evaluate the second of the two image= cells (change from random to Sine image): image= Now push the Process! button - the display is unchanged!! (Yuck!) But now push the L2 control - the display updates to show the newer image results! Yet the Button[] function processImage[] is careful to invoke plots=genPlots[] as its last instruction. And "plots" is included in TrackedSymbols. With my full-scale code, I can see that pressing the Process! button does actually result in computation (30 seconds of it) - how can Manipulate not see "plots" getting modified upon Button-push, and therefore execute the appropriate Refresh? John Fultz, are you there? :-) Thanks! ------------ image = Table[RandomReal[], {32}, {32}]; testMyPatience[]; image = Table[Sin[i*j/64], {i, 1, 32}, {j, 1, 32}]; testMyPatience[] := Manipulate[ (* image is in Global` namespace, and is "passed" by an external party. *) (* The actual processing of image is expensive which is why we have \ it button-activated. It produces processedImage which can be analyzed repeatedly by \ genPlots[] for different controller settings w/ o processedImage needing to be recomputed. *)(* This Button- activated Module modifies the external variables {firstTime, snapShot,processedImage}. *) processButton[] := Module[{}, firstTime = False; snapShot = image; (* Yields external variable pocessedImage *) processedImage = Transpose[image]; (* Yields external plots *) plots = genPlots[]; ]; (* Take processedImage and post- process as desired into diagnostic graphics. Preserve processedImage by treating as "read-only" - it is expensive to compute - the diagnostic results are not, and can be iterated for different settings, while preserving the expensive processedImage. *) genPlots[] := Module[{touchedProcessedImage, g1, g2}, (* Now change appearance of processedImage IAW chosen metric *) If[cmetric == "L2", touchedProcessedImage = processedImage^2]; If[cmetric == "L1", touchedProcessedImage = Reverse[processedImage]]; (* Now make graphics *) g1 = ListPlot3D[snapShot, PlotLabel -> "Image", PlotRange -> All]; g2 = ListPlot3D[touchedProcessedImage, PlotLabel -> "touched", PlotRange -> All]; (* Return *) GraphicsGrid[{{g1, g2}}, ImageSize -> 400] ]; plots = If[Not[firstTime], genPlots[], "Hi trusty Mathematica weenie!" ], (* The last executable statement above must terminate with a comma, thus marking the division between the "action" and control/ layout specification sections of the Manipulate[] *) (* >>>>>>> CONTROL/LAYOUT SECTION <<<<<<<<< *) Item[Style["Test My Patience", 16, Bold, Brown], Alignment -> Center], Delimiter, {{cmetric, "L1", "Plot Appearance"}, {"L1", "L2"}}, Button["Process!", processButton[], Method -> "Queued"], (* Dummy control variables, which are actually variables we'd like to keep localized to within \ this Manipulate, or otherwise they'd leak out as session- global variables. A good rule of thumb: if a variable is included in TrackSymbols, it better be localized. LocalizeVariables-> True localizes variables associated with Controls. *) {firstTime, ControlType -> None}, {snapShot, ControlType -> None}, {plots, ControlType -> None}, {processedImage, ControlType -> None}, (* Manipulate options *) LocalizeVariables -> True, (* Very Strange!!! If we DON'T track processedImage, graphics don't update, even when processButton is pressed, which includes invocation of plots=genPlots[], AND we are tracking the dummy control variable "plots" ! *) TrackedSymbols :> {cmetric, firstTime, plots}, ContinuousAction -> False, Initialization :> (firstTime = True) ]