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},
   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},
    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?

  • 2
    $\begingroup$ Somewhat related post here. $\endgroup$ – István Zachar Oct 22 '12 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 '12 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$ – Alexei Boulbitch Oct 23 '12 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$ – Alexei Boulbitch Oct 23 '12 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$ – Alexei Boulbitch Oct 23 '12 at 10:48

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},
   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:

 col[_] = Gray;
   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

  • $\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$ – Alexei Boulbitch Oct 23 '12 at 13:52
  • $\begingroup$ @Alexei are you referring to the With[{i = ii} thing? $\endgroup$ – Mr.Wizard Oct 23 '12 at 13:53
  • $\begingroup$ Yes. I mean this thing. $\endgroup$ – Alexei Boulbitch Oct 23 '12 at 14:04
  • $\begingroup$ @Alexei please see this: mathematica.stackexchange.com/q/7756/121 $\endgroup$ – Mr.Wizard Oct 23 '12 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 '12 at 11:54

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_] := 
        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
  • $\begingroup$ Thank you. It seems not quite what I am after, or I do not understand something. $\endgroup$ – Alexei Boulbitch Oct 23 '12 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 '12 at 12:34

You can also use FlipView

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

enter image description here

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

Your Answer

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

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