Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion Groups
Mathematics
General TopicsResearchOperations ResearchStatisticsMathematical LogicNumerical AnalysisUndergraduate MathAlgebra HelpRecreational Math
Math Software
MapleMathematicaMATLABScilabSASSPSS

Math Forum / Math Software / Mathematica / October 2009



Tip: Looking for answers? Try searching our database.

Re: How to find which variable caused the trigger in Manipulate[]

Thread view: 
Enable EMail Alerts  Start New Thread
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.
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2012 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.