Re: How to find which variable caused the trigger in Manipulate[]
|
|
Thread rating:  |
Nasser Abbasi - 06 Oct 2009 13:00 GMT "Nasser Abbasi" <nma@12000.org> wrote in message news:...
> Again, I want something like this > > Manipulate[ > process[( pickTheCorrectControlVariableWhichChanged ], > {a, 0, 1}, {b, 0, 1}, Initialization :> > > (process[arg_] := Module[{}, Plot[Sin[arg*x], {x, -Pi, Pi}]]) > ] I made some progress and I think I have a solution.
I save the old value of each control variable in a global variable, then in the Manipulate expression, I check, using an If statement which current value of the control variable is different from the old value. I got it to work ok finally.
Here is an example:
olda = 999; oldb = 999;
Manipulate[ If[olda != a, {olda = a; Style[StringJoin["a=", ToString[a]]]},
If[oldb != b, {oldb = b; Style[StringJoin["b=", ToString[b]]]},
Text["this message should NOT show up!"]]], {a, 0, 1}, {b, 0, 1},
LocalizeVariables -> True, TrackedSymbols -> {a, b}]
Here is another example as the above, but with plot
olda = 999; oldb = 999;
Manipulate[ If[olda != a, {olda = a; Plot[Sin[a*x], {x, -Pi, Pi}, PlotLabel -> "a"]},
If[oldb != b, {oldb = b; Plot[Sin[b*x], {x, -Pi, Pi}, PlotLabel -> "b"]},
Text["this message should NOT show up!"]]], {a, 0, 1}, {b, 0, 1},
LocalizeVariables -> True, TrackedSymbols -> {a, b}, ContinuousAction -> False]
So, the above seems to do what I want. i.e. a way to find which control variable changed.
The problem is where to save these olda and oldb global variables as I am writing this for a demo.
I could ofcourse have a Module around the whole thing as follows and have olda and olodb as local variables to the module
Module[{oldb = 999, olda = 999},
Manipulate[If[olda != a, {olda = a; Plot[Sin[a*x], {x, -Pi, Pi}, PlotLabel -> "a"]}, If[oldb != b, {oldb = b; Plot[Sin[b*x], {x, -Pi, Pi}, PlotLabel -> "b"]}, Text["this message should NOT show up!"]]], {a, 0, 1}, {b, 0, 1}, LocalizeVariables -> True, TrackedSymbols -> {a, b}, ContinuousAction -> False ]
]
But I am not sure if this is allowed in a demonstration. I need to check. If so, then my problem is finally solved
--Nasser
Nasser Abbasi - 08 Oct 2009 12:50 GMT "Nasser Abbasi" <nma@12000.org> wrote in message news:...
> Here is what I tried so far, and this fail: > [quoted text clipped - 15 lines] > {oldy, -999, ControlType -> None} > ] As always seem to happen, after I posted the above, I find a solution. All what I have to do is track the correct symbols.
Here is the solution
Manipulate[
Which[
x != oldx, {oldx = x; Text[StringJoin["you moved the ", "x ", " slider"]]},
y != oldy, {oldy = y; Text[StringJoin["you moved the ", "y ", " slider"]]},
True, Text["why Ami here??"] ],
{x, 0, 1}, {y, 0, 1}, {oldx, -999, ControlType -> None}, {oldy, -999, ControlType -> None},
TrackedSymbols -> {x, y} (*this is the trick*) ]
By telling Mathematica to only track x and y and not oldx and oldy, I get what I want !
--Nasser
Nasser Abbasi - 08 Oct 2009 12:57 GMT >> Again, I want something like this >> [quoted text clipped - 4 lines] >> (process[arg_] := Module[{}, Plot[Sin[arg*x], {x, -Pi, Pi}]]) >> ]
> I made some progress and I think I have a solution. > [quoted text clipped - 17 lines] > > LocalizeVariables -> True, TrackedSymbols -> {a, b}] It is me again, with the same problem.
I found out that I can NOT use global variables in a demo, and I also can't wrap the whole Manipulate inside a module. Any one of the above method will have solved this problem, but rules are rules, so now I have to find another solution. So I am stuck again.
Before I recode everything again, which I would hate to do, I thought I'll ask one more time, may be some expert can have a solution. But this one is really hard.
I'll explain again the problem to make sure we are all clear on it.
I need to write a Manipulate where inside the Manipulate I need to detect which slider or in other words, which control variable was changed. i.e. which slider the user just changed to cause the Manipulate expression to be generated. (I need to do this so I can do different processing based on the slider that was selected)
We all know that Manipulate generates a new version of its expression when one of the control variables changes value. I need to know which control variable changed.
There is the general layout
Manipulate[
(* expression that uses control variables *)
,
(* controls here which update the control variables values *) ]
But there are restriction on the solution, since this will be for a demo. Again, there can NOT be global variables used, (i.e. no variables in the Manipulate initialization section, since these are global), and there can NOT be a module around Manipulate[], i.e. no Module[{...}, Manipulate[...]] allowed.
Here is a small code to show the problem
Manipulate[ Text[StringJoin["you moved the ", "x or y", " slider"]], {x, 0, 1}, {y, 0, 1} ]
Could someone modify the above, so that the code above will tell which slider the user _just_ moved?
It seems like an impossible problem for me to solve without the use of global variables or a module around Manipulate.
Here is what I tried so far, and this fail:
Manipulate[ Which[
x != oldx, {oldx = x; Text[StringJoin["you moved the ", "x ", " slider"]]},
y != oldy, {oldy = y; Text[StringJoin["you moved the ", "y ", " slider"]]},
True, Text["why Ami here??"] ],
{x, 0, 1}, {y, 0, 1}, {oldx, -999, ControlType -> None}, {oldy, -999, ControlType -> None} ]
The reason it fail is because oldx and oldy are updated to the same value of x and y whenever x or y changes before I get the chance to do the comparison. I.e. when the new version of the expression is generated, oldx=x and oldy=y each time. This seems to consequences of Manipulate generating DynamicModule[] for the whole thing. You can see this by using the SnapShot option on the Manipulate output. This will generate the whole expression form. (Nice tool).
If someone can make it so that the above will display the correct message each time, then I would declare that person to be the Mathematica Guru of the year.
I think I have reached the limit of my current Mathematica understanding when it comes to internals of Manipulate and Dynamics to be to solve this one. But I'll keep on looking.
Thank you, --Nasser
John Fultz - 10 Oct 2009 12:15 GMT This is a very simple version of what you asked for that doesn't solve some of the fundamental problems you'll probably come across, but it shows you the techniques to solving them. Principally...
* Contain all evaluation inside of scoped Dynamics to prevent the entire Manipulate from refreshing. * Add flag variables (and perhaps you'll want other variables, too) as control variables, but with ControlType->None so they don't appear. * Use separate Dynamics, which display inside a Row[] as empty strings (this is important...remember that if a Dynamic doesn't display onscreen, then there's nothing available to update...see my previous posts on Dynamic if you're at all confused about this), to track the individual variables. + These separate Dynamics each scope a single variable only. + The scoped variable is determined by using Refresh with TrackedSymbols
One of the problems my version doesn't solve is sensibly setting a start condition, so the evaluation assumes that the initial state has changed the y parameter (as a result of the initial creation of the flag-tracking dynamics). That's a problem I'll let you figure out.
Manipulate[Row[{ Dynamic[ Refresh[xFlag = True; yFlag = False; "", TrackedSymbols -> {x}]], Dynamic[ Refresh[xFlag = False; yFlag = True; "", TrackedSymbols -> {y}]], Dynamic[ Text[StringJoin["you moved the ", Which[xFlag, "x", yFlag, "y"], " slider"]]] }], {x, 0, 1}, {y, 0, 1}, {{xFlag, False}, ControlType -> None}, {{yFlag, False}, ControlType -> None}]
Sincerely, John Fultz jfultz@wolfram.com User Interface Group Wolfram Research, Inc.
>>> Again, I want something like this >>> [quoted text clipped - 39 lines] > > Before I recode everything again, which I would hate to do, I thought= I'll
> ask one more time, may be some expert can have a solution. But this one= is
> really hard. > [quoted text clipped - 9 lines] > > We all know that Manipulate generates a new version of its expression= when
> one of the control variables changes value. I need to know which control > variable changed. [quoted text clipped - 57 lines] > oldx=x > and oldy=y each time. This seems to consequences of Manipulate= generating
> DynamicModule[] for the whole thing. You can see this by using the > SnapShot [quoted text clipped - 11 lines] > Thank you, > --Nasser John Fultz - 12 Oct 2009 11:39 GMT This looks to me like a bug, but one that is very simple to work around. Use Rule instead of RuleDelayed for the Refresh. I.e.,
Manipulate[ Row[{Dynamic[Refresh[r = process["x "]; "", TrackedSymbols -> {x}]], Dynamic[Refresh[r = process["y "]; "", TrackedSymbols -> {y}]], Dynamic[Refresh[Text[r], TrackedSymbols -> {x, y}]]}], {x, 0, 1}, {y, 0, 1}, {r, "", "", ControlType -> None}, Initialization :> (process[s_String] := Module[{}, Text[StringJoin[s, DateString[]]]])]
After a bit of experimentation, my advice is to completely avoid RuleDelayed with the TrackedSymbols option.
Sincerely, John Fultz jfultz@wolfram.com User Interface Group Wolfram Research, Inc.
> "This is a very simple version of what you asked for that doesn't solve > some [quoted text clipped - 82 lines] > > The above works as I wanted. When the x or the y slider is moved, the= code
> detects which one and shows the corresponding message. > [quoted text clipped - 23 lines] > > --Nasser John Fultz - 12 Oct 2009 11:39 GMT I was quite explicit about this in my previous email...
>> * Use separate Dynamics, which display inside a Row[] as empty strings I.e., the Row is scoping two empty strings and the Text[]. Replacing Row with Column makes empty space because empty strings take up a full line height in a Column. The controls come from the surrounding Manipulate, and so nothing inside the Row is displaying as a column.
Sincerely, John Fultz jfultz@wolfram.com User Interface Group Wolfram Research, Inc.
On Sun, 11 Oct 2009 14:30:22 -0500, DrMajorBob wrote:
> Why does that Row display as a column? (Even when I stretch the window > over two monitors.) [quoted text clipped - 222 lines] >>> Thank you, >>> --Nasser John Fultz - 21 Oct 2009 11:28 GMT On Tue, 20 Oct 2009 04:54:32 -0400 (EDT), Nasser M. Abbasi wrote:
>> This looks to me like a bug, but one that is very simple to work around. >> Use [quoted text clipped - 82 lines] > So, I am trying to solve this problem. But I have no idea how Mathematica > does these things internally, so I have been doing lots of trial and= error
> trying to find out why. But no luck yet. > [quoted text clipped - 4 lines] > It seems that Row[{...}] is not the right thing to use for this, I tried > Grid, Column, and nothing helped, or may be this is a bug? May be some= one
> has any other ideas, please let me know. > [quoted text clipped - 5 lines] > Thanks, > --Nasser I can't reproduce this. In 7.0.0 and 7.0.1, your example perfectly remembers for me both the values of x and y, setting the sliders at exactly the right point upon reloading. Perhaps what you meant is that it's not remembering the value of *r* after you move the x slider, save, and reload.
It does properly store the value of r, but once you reload the notebook, all of the Dynamics compute again. Since computing the Dynamics has the side effect of setting r, the stored value of r will get wiped out. This amounts to the same problem as I mentioned in my first response...in this example, whether r is initially set to the value of x or y is undefined. It's undefined because there's nothing in the Mathematica documentation which guarantees the order in which the Dynamics are evaluated. This is true both when you initially create it, and also when you load it again from disk (or copy and paste it, as well). It so happens that it's evaluating Dynamics left-to-right in the Row, but that's not behavior you should count on. So the setting of r (if that is, indeed, what you're seeing) is within the designed behavior, and not a bug.
Sincerely, John Fultz jfultz@wolfram.com User Interface Group Wolfram Research, Inc.
John Fultz - 21 Oct 2009 11:29 GMT On Tue, 20 Oct 2009 13:44:11 -0500, Nasser M. Abbasi wrote:
>> Manipulate[Row[{ >> Dynamic[Refresh[r = x; "", TrackedSymbols -> {x}]], [quoted text clipped - 61 lines] > and at the same time, when the user closes the notebook and reopen it, the > sliders and the display to be restored to the same state as before= closing
> the notebook. > [quoted text clipped - 10 lines] > > --Nasser Actually, I'm not without ideas. I just don't have the time to develop them too carefully when I'm not sure I understand the real problem. Now that you've clarified that the problem is, indeed, what I suspected it was, here's a solution I've developed:
Manipulate[ Row[{Dynamic[Refresh[r = x; "", TrackedSymbols -> {x}]], Dynamic[Refresh[r = y; "", TrackedSymbols -> {y}]], Dynamic[Refresh[ If[initializing === True, initializing = False; r = initialState]; initialState = r; Text[r], TrackedSymbols -> {y, x}]]}], {x, 0, 1, 0.01}, {y, 0, 1, 0.01}, {r, "", "", ControlType -> None}, {{initialState, 0}, ControlType -> None}, {{initializing, False}, ControlType -> None}, Initialization :> (initializing = True)]
The really important feature of this solution is that it uses the Initialization option of Manipulate to determine when Manipulate is in its starting state, and thereby determine when to initialize r by some means other than the first two Dynamics.
Now this code has the distinct advantage that it works, and it's not so complicated, but there's one fatal flaw in it. It relies on the same undocumented assumption I discussed earlier...that Dynamics are validated left-to-right. It would fall down if you flipped the order of the Row[], or in a hypothetical future implementation of Dynamic that violates this assumption. So that's not my final solution. But I wanted you to see this so you could understand better how I built my final solution. My final solution takes the same approach, but tracks the Dynamics as they're initialized, forcing the Text[] Dynamic to evaluate (possibly again) after the others. The solution doesn't seem very elegant, but it does work. I'm afraid that the educational value of this example will require some study of the code, but you seem willing to do that. I reversed the Row[] in this example to prove that it does, in fact, work as advertised.
Manipulate[Row[{ Dynamic[Refresh[ Switch[dynamicsInitialized, {True, True, False}, dynamicsInitialized[[3]] = True; r = initialState, {True, True, True}, initialState = r]; Text[r], TrackedSymbols -> {y, x, dynamicsInitialized}]], Dynamic[Refresh[dynamicsInitialized[[1]] = True; r = x; "", TrackedSymbols -> {x}]], Dynamic[Refresh[dynamicsInitialized[[2]] = True; r = y; "", TrackedSymbols -> {y}]]}], {x, 0, 1, 0.01}, {y, 0, 1, 0.01}, {r, "", "", ControlType -> None}, {{initialState, 0}, ControlType -> None}, {{dynamicsInitialized, {False, False, False}}, ControlType -> None}, Initialization :> (dynamicsInitialized = {False, False, False})]
Sincerely, John Fultz jfultz@wolfram.com User Interface Group Wolfram Research, Inc.
John Fultz - 21 Oct 2009 11:30 GMT On Tue, 20 Oct 2009 22:13:15 -0500, Nasser M. Abbasi wrote:
> From: "John Fultz" > [quoted text clipped - 61 lines] > But when I click the "initial settings" button, then look at the "Paste > snaphot", then I see this variable having the value {False, False,= False}.
> I came up with a theory on how this could be: > [quoted text clipped - 24 lines] > > --Nasser Yes, you've largely got it. For purposes of full understanding, I'll just correct a very forgivable oversight.
Not three evaluations, but six.
Evaluation 1: The code in Initialization comes first. That's a documented, ironclad guarantee. My solution relies on that guarantee.
The remaining evaluations, as I've stressed, could hypothetically come in any order, but in version 7, here's how they come...
Evaluation 2: The Switch[] code. But the Switch[] fails, as dynamicsInitialized will be {False, False, False}, remains unevaluated, and r is left unchanged.
Evaluation 3: The x initialization, as you say.
Evaluation 4: The y initialization, as you say.
Evaluation 5: Evaluations 3 and 4 have now re-tickled the Switch[] code, by virtue of changing dynamicsInitialized (you'll note that I added dynamicsInitialized to that particular Refresh). So now the Switch[] code evaluates again, but this time with dynamicsInitialized set to {True, True, False}
Evaluation 6: Evaluation 5 now tickles *again* the Switch[] statement by virtue of changing dynamicsInitialized to {True, True, True}. So one last re-evaluation.
And now things are in a stable state and no further evaluations happen until you start moving sliders.
Sincerely, John Fultz jfultz@wolfram.com User Interface Group Wolfram Research, Inc.
|
|
|