10
$\begingroup$

I am making a grid on the screen. The grid is composed of different expressions. I want all of them to have a Gray background, which turns into a White background upon a mouse click, each expression separately. Instead of the whole thing I only show it here for a row of 3 elements to simplify the example. This works:

DynamicModule[{col1 = Gray, col2 = Gray, col3 = Gray},
 Row[{
   EventHandler[Dynamic@Style[x, Background -> col1],
    {"MouseClicked" :> (col1 = 
        col1 /. {Gray -> White, White -> Gray})}](*End eventhandler*),
   EventHandler[Dynamic@Style[x, Background -> col2],
    {"MouseClicked" :> (col2 = 
        col2 /. {Gray -> White, White -> Gray})}](*End eventhandler*),
   EventHandler[Dynamic@Style[x, Background -> col3],
    {"MouseClicked" :> (col3 = 
        col3 /. {Gray -> White, White -> Gray})}](*End eventhandler*)
 }]
]

However, as soon as I try to automate the same code, i.e., instead of writing each element of the row separately, to use the Table statement it does not work. This is my trial of the automation that does not work:

DynamicModule[{col1 = Gray, col2 = Gray, col3 = Gray},
 Row[Table[
   EventHandler[
    Dynamic@Style[x, Background -> ToExpression["col" <> ToString[i]]],
    {"MouseClicked" :> (Background -> ToExpression["col" <> ToString[i]] =
        Background -> ToExpression["col" <> ToString[i]] /. {Gray -> Green, 
          Green -> Gray})}](*End eventhandler*),
   {i, 1, 3}]]
]

I would like to understand what is wrong. Is it possible to wrap EventHandler by a Table statement?

$\endgroup$
5
  • 2
    $\begingroup$ Somewhat related post here. $\endgroup$ Oct 22, 2012 at 14:53
  • 1
    $\begingroup$ It's difficult to say what the bigger problem is that Alexei is trying to solve. But it does smell like a problem that might be more suitably handled by Toggler and friends than EventHandler, as István might be suggesting by his linked content. That having been said, I think Mr.Wizard points to a better method for tracking a variable number of values. $\endgroup$
    – John Fultz
    Oct 23, 2012 at 2:46
  • $\begingroup$ Let me explain. There is an approach in pedagogics and psychology to train the attention of children with slight problems of concentration. The exercise is that the teacher uses a hand lamp to light up one out of lots of task preliminarily drawn on the blackboard. The child is trained to only focus on the highlighted task. This method has drawbacks, especially if applied in a small room without a large blackboard ans so on. $\endgroup$ Oct 23, 2012 at 10:37
  • $\begingroup$ Now my idea is to make the same on the computer screen. I imagine it in the following way. There should lots of exercises all with a gray background.The teacher/parent switches one of them into say, green by the mouse click. Alternatively it may automatically go randomly. The task are all different and are also formed using random numbers generator. Since it is assumed for a primary school chile, they are simple, like addition, multiplication, division, squares and alike. Now as soon as one exercise is highlighted, the result should be due to be entered into an input field and checked. $\endgroup$ Oct 23, 2012 at 10:39
  • $\begingroup$ Upon checking a signal should be given to inform the child, if he is right, or not. There may be also other things to do, say, to count the amount of right and wrong answers, and so on. Previously I already did simple training interactive toys. They were, however, more simply in that they only exposed one task at a time. Here I want, however, to expose many tasks at once, but only make one of them due to solve. That is what stays behind my question. $\endgroup$ Oct 23, 2012 at 10:48

3 Answers 3

13
$\begingroup$

There are several issues here. You need to "inject" the symbol name into the expression using With (or similar) to prevent trying to make an assignment to ToExpression["col" <> ToString[i]]. Further, you've got spurious Background -> expressions which do not belong. (I also use Symbol in place of ToExpression.) That gives us:

DynamicModule[{col1 = Gray, col2 = Gray, col3 = Gray},
 Table[
   With[{bg = Symbol["col" <> ToString[i]]},
    EventHandler[Dynamic@Style["x", 50, Background -> bg],
     {"MouseClicked" :> (bg = bg /. {Gray -> Green, Green -> Gray})}]
   ],
   {i, 1, 3}
 ] // Row
]

Mathematica graphics

There is a problem with this however, because the symbols you create inside Table, whether you use ToExpression or Symbol, are not localized as you expect. If you set col1 = "Fail!" outside of the module you will see this problem.

Probably a better method is to use indexed objects as follows:

DynamicModule[{col},
 col[_] = Gray;
 Table[
   With[{i = ii},
    EventHandler[Dynamic@Style["x", 50, Background -> col[i]],
     {"MouseClicked" :> (col[i] = col[i] /. {Gray -> Green, Green -> Gray})}]
   ],
   {ii, 1, 7}
 ] // Row
]

Mathematica graphics

A bit more concisely with Function, Array, and TimesBy:

DynamicModule[{col},
  col[_] = 1;
  EventHandler[
    Style["x", 50, Background -> Dynamic @ {Gray, Green}[[col@#]] ]
    , {"MouseClicked" :> (col[#] *= -1)}
  ] & ~Array~ 7 // Row
]

Changing the position of Dynamic also changes the behavior of the output when the kernel is terminated; the line resets but the X's can still be clicked to toggle.

$\endgroup$
9
  • $\begingroup$ Thank you. It looks like what I had in mind. I should admit, however, that I do not understand why the scoping of the parameter "i" here makes the job. I see though that without it does not work as intended. Could you comment this? $\endgroup$ Oct 23, 2012 at 13:52
  • $\begingroup$ @Alexei are you referring to the With[{i = ii} thing? $\endgroup$
    – Mr.Wizard
    Oct 23, 2012 at 13:53
  • $\begingroup$ Yes. I mean this thing. $\endgroup$ Oct 23, 2012 at 14:04
  • $\begingroup$ @Alexei please see this: mathematica.stackexchange.com/q/7756/121 $\endgroup$
    – Mr.Wizard
    Oct 23, 2012 at 15:04
  • $\begingroup$ @Alexei may I know why you un-Accepted this answer? I argue that indexed objects are still the best way to go. Among other things Unique[] gets quite slow when there are many Symbols in the system. $\endgroup$
    – Mr.Wizard
    Oct 24, 2012 at 11:54
4
$\begingroup$

When handling symbols like you do, I find it best to avoid converting from and to strings. This is typically needed in other languages to do meta-programming, however due to Mathematicas philosophy being that everything is an expression, meta-programming can be accomplished simply by holding evaluation order. Here is a simple implementation of your functionality:

 SetAttributes[ClickAbleField, HoldAll]
 ClickAbleField[var_] := 
    EventHandler[
        Dynamic@Style[x, Background ->var]
    ,{"MouseClicked" :> (var = var /. {Gray -> Green, Green -> Gray})}
    ]

 DynamicModule[{col1 = Gray, col2 = Gray, col3 = Gray}, 
   Row[{Hold[col1], Hold[col2], Hold[col3]}] /. Hold -> ClickAbleField
 ]

Note that I use hold to prevent imidiate evaluation of the col variables, but also tell ClickAbleField to hold it's input arguments using the attribute HoldAll, such that the mentions inside it's definition will reference the unevaluted variable, and not it's current value.

If you have long lists of variables, you can also just pass them around in Hold and map functions over them like you would ususally do, then release the hold afterwards:

 DynamicModule[{col1 = Gray, col2 = Gray, col3 = Gray},
  Row[List @@ (ClickAbleField /@ Hold[col1, col2, col3])]
 ] 

If you want to generate something based on arbitrary lists of variables, you could use Unique to generate the variables:

vars = Table[Unique["col"], {5}];
Hold[DynamicModule[initvars, body]] /. {
  initvars -> Replace[Hold @@ vars, a_ :> (a = Gray), 1],
  body -> Row[ClickAbleField /@ vars]
} /. Hold -> List // First
$\endgroup$
2
  • $\begingroup$ Thank you. It seems not quite what I am after, or I do not understand something. $\endgroup$ Oct 23, 2012 at 10:53
  • $\begingroup$ @AlexeiBoulbitch I added an example of generating your interface with arbitrary number of variables. the initial code simply showed how to accomplish the code you already posted. $\endgroup$
    – jVincent
    Oct 23, 2012 at 12:34
3
$\begingroup$

You can also use FlipView

 Row[Table[FlipView[Style["x", 50, Background -> #] & /@ {Gray, Green, Red}], {5}]]

enter image description here

$\endgroup$
2
  • $\begingroup$ Short code! +1 (Though in fairness perhaps not as flexible as my method.) $\endgroup$
    – Mr.Wizard
    Jan 20, 2013 at 15:20
  • $\begingroup$ Mr.Wizard, thanks for the vote. $\endgroup$
    – kglr
    Jan 21, 2013 at 0:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.