4

I wrote some code to monitor the tasks that I'm doing - looks like this:

enter image description here

Now it works just fine, but as soon as I wrap it up in palette it dies:

enter image description here

Nothing updates.... I know there must be an easy way to fix this - I'm probably something wrong with the namespace of the dynamic parts... but I'm blanking on it.

The reason I'd like to use a palette here because it's detachable and looks like it's own app. Here's the code:

    taskQueue[input:{{title_String, mins_},__}] := Module[
        {getTime, remaining = <||>, taskQueue = <||>, row, grid={}, currtitle, st},

        Do[
            taskQueue[i[[1]]] = i[[2]]*60;
            , {i, input}
        ];

        remaining = taskQueue;
        getTime[t_] := Lookup[remaining, input[[t,1]], 0];

        Do[ 
            row = {}; 
            AppendTo[row, 
                Item[Style[input[[i,1]], FontFamily->"Constantia", FontSlant->Italic, 
                    FontWeight->Bold, FontSize->16], Alignment->Left]
            ];

            AppendTo[row, 
                    With[{i=i},
                        Dynamic[formatSeconds@getTime[i], UpdateInterval->1]
                    ]
            ];
            AppendTo[grid, row];

            , {i, 1, Length@remaining}
        ];

        st=CreateScheduledTask[
            If[Length@remaining==0, 
                RemoveScheduledTask[ScheduledTasks[]],
                If[remaining[[1]]<=0, 
                    remaining= Rest@remaining;
                ];
                remaining[[1]]=remaining[[1]]-1
            ]
        ,1];

        AppendTo[grid,{startStopButton[StartScheduledTask[st], StopScheduledTask[st]],
            Dynamic[formatSeconds[Total@remaining], UpdateInterval->1, TrackedSymbols:>{remaining}]
        }];

        Framed[
            Grid[grid,
                Alignment -> {Left, Center}, 
                Dividers -> {None, {All,-1->False}},
                Spacings -> {1,2},
                Background -> {None, {Min[Length[taskQueue], 
                        Dynamic[Length[taskQueue]-Length[remaining]+1,UpdateInterval->1]]->LightBlue}},
                BaselinePosition -> Center
            ],
            FrameMargins->10,
            FrameStyle -> GrayLevel[0.64],
            RoundingRadius -> 5
        ]
    ]

(*Additional definitions needed*)

$playView=ToExpression@GraphicsBox[
                TagBox[{
                  {GrayLevel[0.9], RectangleBox[{5, -158}, {29, -135}]}, 
                  {RGBColor[0.26,0.68,0.46], 
                   PolygonBox[NCache[{{13, -153}, {13, -140}, {21, Rational[-293, 2]}, {13, -153}}, {{13, -153}, {13, -140}, {21, -146.5}, {13, -153}}]]}, 
                  {GrayLevel[0.5], 
                   StyleBox[LineBox[{{5, -158}, {5, -135}, {29, -135}, {29, -158}, {5, -158}}],
                    Antialiasing->False]}},
                 Null],
                ImageSize->{29.4921875, Automatic}];

    $stopView=ToExpression@GraphicsBox[
                 TagBox[{
                   {GrayLevel[0.9], RectangleBox[{34, -158}, {58, -135}]}, 
                   {RGBColor[0.81,0.33,0.26], RectangleBox[{42, -150}, {50, -143}]}, 
                   {GrayLevel[0.5], 
                    StyleBox[LineBox[{{34, -158}, {34, -135}, {58, -135}, {58, -158}, {34, -158}}],
                     Antialiasing->False]}},
                  Null]];

    Attributes[cycleState]={HoldFirst};
    cycleState[s_/;s>0,len_:2] := s = If[s>=len,1,s+1]

    SetAttributes[startStopButton,HoldAllComplete];
    startStopButton[startAction_, stopAction_] := DynamicModule[
         {views={$playView, $stopView},state=1},
                Button[Dynamic[views[[state]]], Switch[state,1,startAction,2,stopAction];state=cycleState[state];,Appearance->"Frameless"]
            ]

    Options[formatSeconds] = {"FixedWidth"->True};
    formatSeconds[secconds_, opts:OptionsPattern[]] := Module[
            {timeUnits = UnitConvert[Quantity[secconds, "Seconds"], MixedRadix["Hours", "Minutes", "Seconds"]],
            hours,mins,secs},

            hours = timeUnits[[1,1]];
            mins = timeUnits[[1,2]];
            secs = Round@timeUnits[[1,3]];

            Return @ iFormatSeconds[hours, mins, secs]
        ]

    Options[iFormatSeconds] = Options[formatSeconds];
    iFormatSeconds[hours_, mins_, secs_, OptionsPattern[]] := Module[
            {hf,mf,sf},

            hf = IntegerString[hours, 10, 2];
            mf = IntegerString[mins, 10, 2];
            sf = IntegerString[secs, 10, 2];

            Return @ Style[StringRiffle[{hf, mf, sf},":"], FontFamily->"Digital-7", FontSize->24]
    ]
Karsten7
  • 27,448
  • 5
  • 73
  • 134
M.R.
  • 31,425
  • 8
  • 90
  • 281
  • I tried all the usual tricks with dynamicmodule and stuff, and still cannot get the clock to tick down when in a palette. Also, it's a tad on the slow side I'm realizing now... any ideas to speed it up would be great :) – M.R. Sep 25 '15 at 03:19
  • Your function taskQueue is dependent on a function you call getTime. Are you sure that the palette code has access to that function? – m_goldberg Sep 25 '15 at 05:37
  • Doesn't it? Since getTime is inside the module? – M.R. Sep 26 '15 at 15:54
  • 1
    Could you not reduce the problem to a more manageable minimal example? There is a lot of formatting work in your code: could you strip layer by layer to pinpoint the point of failure? If you run taskQueue as a palette, an error is reported in the messages window saying: StartScheduledTask::timnf: Specified task st$159915 is not found. As you say, this seems an indication of a scoping problem. Have you tried to isolate the problem yourself though? – MarcoB Sep 30 '15 at 14:25
  • 1
    It's nonsensical to post such a large piece of code unrelated to the problem and expect someone to read and understand it all. – Dr. belisarius Sep 30 '15 at 16:40
  • How is it unrelated to the question? – M.R. Sep 30 '15 at 22:04

1 Answers1

2

Based on the error message the ScheduledTask seems to cause the problems. Therefore here is a way how to make a palette with a clock that can be started, stopped and continued by clicking a button without using a ScheduledTask.

CreatePalette@
 DynamicModule[{startStop = False, last = 0, refTime = AbsoluteTime[]}, 
  Grid[{{Dynamic[
      Refresh[If[startStop, Round[last = AbsoluteTime[] - refTime], 
        Round[last]], UpdateInterval -> 1]]},
    {Button[Dynamic@If[startStop, $stopView, $playView], 
      If[startStop, startStop = ! startStop, startStop = ! startStop; 
       refTime = AbsoluteTime[] - last]]}}, 
   Alignment -> {Left, Center}, 
   Dividers -> {None, {All, -1 -> False}}, Spacings -> {1, 2}, 
   BaselinePosition -> Center]]

StopWatchPalette

The reasons why the ScheduledTask causes problems and how these can be resolved can be found in this Q&A.

Karsten7
  • 27,448
  • 5
  • 73
  • 134