19
$\begingroup$

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

$\endgroup$
4
  • 1
    $\begingroup$ You should really look at Anki (which of course doesn't mean that we shouldn't solve this in Mathematica!) $\endgroup$
    – Szabolcs
    Commented Feb 16, 2012 at 13:16
  • 5
    $\begingroup$ +1 for doing spelling homework using Mathematica! $\endgroup$
    – Eli Lansey
    Commented Feb 16, 2012 at 16:13
  • 1
    $\begingroup$ +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"], " "]]. $\endgroup$
    – kglr
    Commented Feb 16, 2012 at 18:36
  • $\begingroup$ 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. $\endgroup$
    – Mr.Wizard
    Commented Feb 14, 2013 at 19:52

1 Answer 1

11
$\begingroup$

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

$\endgroup$
3
  • $\begingroup$ Yes, but it needs to be on a grid. Otherwise it looks ugly as you have shown. $\endgroup$ Commented Feb 16, 2012 at 15:49
  • $\begingroup$ @Gustavo see edit; sorry for sloppy formatting but I don't have time to make it pretty. $\endgroup$
    – Mr.Wizard
    Commented Feb 16, 2012 at 16:41
  • $\begingroup$ Thank you! I had tried unsuccessfully with Map, but I see you cleverly solved it using MapIndexed. $\endgroup$ Commented Feb 16, 2012 at 17:00

Your Answer

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

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