19

When my daughter asked me help with her spelling homework, for me the obvious thing to do was to write a Mathematica program for it.

The words:

words = {"lightning", "thunder", "cloudy"};

The code:

Grid[
 Module[{x = 0, t = 0},
    {
     #,
     Button["Start", t = Hold@AbsoluteTime[] - AbsoluteTime[]], 
     Button[Style["\[Checkmark]", Darker@Green], t = ReleaseHold[t]; x++],
     Button[Style[    "\[Times]",        Red  ], t = ReleaseHold[t]; x--],
     Dynamic[x],
     Dynamic[Clock[]; ReleaseHold[t]]
     }
    ] & /@ words]

Which produces this output:

screenshot

So when I ask her a word, I click "start". Then I click ✔ or × if her answer is right or wrong respectively. I plan to keep track of time to answer, so the timer now shows how much time it took to answer.

So my question is: Why do I have to use Module instead of DynamicModule? For some reason the Grid command does not work if I use DynamicModule. On the other hand, if I use Module (as it is shown), then the syntax highlighter shows my "t" and "x" variables in red as if I am doing something wrong: enter image description here

Update: I have just realized that using Module instead of DynamicModule is not a viable option as the scores are lost when you reopen the notebook.

Update 2: For the record, this is the finished program incorporating the advice from Mr.Wizard and kguler

DynamicModule[{x, t, status, h},
 x[_] = 0;
 t[_] = 0;
 status[_] = False;
 h[_] = {};
 Column[{Grid[
    MapIndexed[{
       #,
       Button["Start", 
              Speak@#; status[#2] = True; 
              t[#2] = Hold@AbsoluteTime[] -AbsoluteTime[], 
              Enabled -> Dynamic[! status[#2]]],
       Button[Style["\[Checkmark]", Darker@Green], 
              t[#2] = ReleaseHold[t[#2]]; x[#2]++; AppendTo[h[#2], t[#2]]; 
              status[#2] = False,
              Enabled -> Dynamic[status[#2]]],
       Button[Style["\[Times]", Red],
              t[#2] = ReleaseHold[t[#2]]; x[#2]--; AppendTo[h[#2], -t[#2]];
              status[#2] = False, 
              Enabled -> Dynamic[status[#2]]],
       Button["spell", Speak@StringJoin[Riffle[Characters[#], ","]]],
       Dynamic[x[#2]],
       Dynamic[Clock[]; ReleaseHold[NumberForm[t[#2], 2]]],
       Dynamic@
        If[Length@h[#2] > 0, 
         Module[{z = Transpose[{Abs[#], Sign[#]} & /@ h[#2]]}, 
          Graphics[
           (Rectangle @@@ 
            (Partition[{Accumulate[First@z],Last@z}\[Transpose],2,1,{2,2},{{0, 0}}] /.
             {{x1_, y1_Integer}, {x2_, y2_Integer}} -> {{x1 + 0.2, 0}, {x2, y2}})) /.
             {Rectangle[{x1_,0}, {x2_, 1}] -> {Darker@Green, Rectangle[{x1, 0}, {x2, 1}]}, 
              Rectangle[{x1_,0}, {x2_,-1}] -> {Red, Rectangle[{x1,0}, {x2, -1}]}},
           ImageSize -> {Automatic, 20}, 
           PlotRange -> {-1, 1}]], 
         ""
        ]
    } &, words],
    Alignment -> Left],
    Row[{Button[
      "reset", (x[{#}] = 0; t[{#}] = 0; h[{#}] = {}) & /@ 
       Range@Length@words], Spacer[10]}]}]]

enter image description here

Gustavo Delfino
  • 8,348
  • 1
  • 28
  • 58
  • 1
    You should really look at Anki (which of course doesn't mean that we shouldn't solve this in Mathematica!) – Szabolcs Feb 16 '12 at 13:16
  • 5
    +1 for doing spelling homework using Mathematica! – Eli Lansey Feb 16 '12 at 16:13
  • 1
    +1. Using Beep is perhaps going too far, but ... Speak and Characters might come handy in this task: Speak["potatoes"] and StringJoin[Riffle[Characters["potatoes"], " "]]. – kglr Feb 16 '12 at 18:36
  • I happened to glance at this again and I noticed that you did not localize pattern names with RuleDelayed. For example Rectangle[{x1_,0}, {x2_,-1}] -> {Red, Rectangle[{x1,0}, {x2, -1}] should be written Rectangle[{x1_,0}, {x2_,-1}] :> {Red, Rectangle[{x1,0}, {x2, -1}] to protect x1 and x2. – Mr.Wizard Feb 14 '13 at 19:52

1 Answers1

11

This is because the object created by DynamicModule does not actually evaluate until it is displayed, therefore Grid has nothing to format other than the outer list.

words = {"lightning", "lightning", "cloudy"};

dynlist = 
  DynamicModule[{x = 0, t = 0},
    {
     #,
     Button["Start", t = Hold@AbsoluteTime[] - AbsoluteTime[]], 
     Button[Style["\[Checkmark]", Darker@Green], t = ReleaseHold[t]; x++],
     Button[Style[    "\[Times]",        Red  ], t = ReleaseHold[t]; x--],
     Dynamic[x],
     Dynamic[Clock[]; ReleaseHold[t]]
     }
    ] & /@ words;


ToString[ dynlist[[1]] ]

"DynamicModule[{x = 0, t = 0}, {lightning, Button[Start, t = \ Hold[AbsoluteTime[]] - AbsoluteTime[]], Button[[Checkmark], t = \ ReleaseHold[t]; x++], Button[[Times], t = ReleaseHold[t]; x--], \ Dynamic[x], Dynamic[Clock[]; ReleaseHold[t]]}, DynamicModuleValues :> \ {}]"

You could build the rows inside the module:

Column[
 DynamicModule[{x = 0, t = 0},
    Grid@{{
     #,
     Button["Start", t = Hold@AbsoluteTime[] - AbsoluteTime[]], 
     Button[Style["\[Checkmark]", Darker@Green], t = ReleaseHold[t]; x++],
     Button[Style[    "\[Times]",        Red  ], t = ReleaseHold[t]; x--],
     Dynamic[x],
     Dynamic[Clock[]; ReleaseHold[t]]
     }}
    ] & /@ words]

Mathematica graphics


Addressing your comment you could move the map operation inside Grid like this:

DynamicModule[{x, t},
 x[_] = 0;
 t[_] = 0;
 Grid[
  MapIndexed[
    {#, Button["Start", t[#2] = Hold@AbsoluteTime[] - AbsoluteTime[]], 
     Button[Style["\[Checkmark]", Darker@Green], 
      t[#2] = ReleaseHold[t[#2]]; x[#2]++], 
     Button[Style["\[Times]", Red], t[#2] = ReleaseHold[t[#2]]; 
      x[#2]--], Dynamic[x[#2]], 
     Dynamic[Clock[]; ReleaseHold[t[#2]]]} &,
   words
]]]

Mathematica graphics

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371