22
$\begingroup$

Let's say you have a palette with a button that has to do some time-consuming task when clicked:

downloadMyMovie[] := Pause[10];
CreateWindow[
 PaletteNotebook[
  Button["Do heavy work", downloadMyMovie[]; MessageDialog["Done"]]
 ]
]

Note that the "Done" dialogue is never shown because preemptive evaluations have a shorter time-out than 10s. While I could use Method->"Queued" it would give the impression that the job is finished instantly, because the button does not stick. Then, out of the blue the finish dialogue would pop up.

Question: Is there a simple template code that one can use to show some kind of progress-bar when the task is running so that the user sees the job is still running?

$\endgroup$
  • $\begingroup$ I don't have access to v10 at the moment, but there is an undocumented option to CreateScheduledTask that might be useful here. Does CreateScheduledTask[Pause[10],{1,1},0,EpilogFunction:>(MessageBox["Done"])] do something along the lines you are looking for? $\endgroup$ – bobthechemist Mar 23 '15 at 15:00
16
$\begingroup$

I'm not sure if this is exactly what you need but this is what I've recently done to inform the user about ongoing calculation.

Usage

withProgressIndicator[proc, delay]

Performs a proc, and when it lasts longer than delay (default 0), a progress indicator in dialog is created. It will be closed after finishing the proc.

  • It should be run on Main Link, with "Queued" method but it should not matter.

  • You can Abort proc too.

  • From your question seems that you don't want to block FrontEnd from user before finishing the task so feel free to change "ModalDialog" to "ModelessDialog".

Examples

Button["GO", withProgressIndicator[While@True, 1], Method -> "Queued"]

enter image description here

withProgressIndicator[Pause[5]; Print[1], .2]

Code

SetAttributes[withProgressIndicator, HoldFirst];

withProgressIndicator[proc_, delay : _?NonNegative : 1] := 
 Module[{dialog, scheduledTask, dialogPrompt},
  dialogPrompt[] := (dialog = CreateDialog[
      Column[{
        Dynamic@ProgressIndicator[Appearance -> "Indeterminate"],
        Button["Abort", 
         FrontEndExecute[FrontEndToken["EvaluatorAbort"]]; 
         DialogReturn[];, Method -> "Preemptive"]
        }],
      WindowFrame -> "ModalDialog", WindowFrameElements -> None, 
      WindowTitle -> "Processing..."]);

  scheduledTask = RunScheduledTask[dialogPrompt[], {delay}];
  proc;
  RemoveScheduledTask@scheduledTask;
  NotebookClose[dialog];
  ]
$\endgroup$
  • $\begingroup$ Very nice. Unfortunately, on Linux this doesn't seem to work very reliable.. +1 $\endgroup$ – halirutan Mar 23 '15 at 15:53
  • $\begingroup$ @halirutan good to know, what is the problem exactly? $\endgroup$ – Kuba Mar 23 '15 at 15:55
  • $\begingroup$ It seemed as if the window did not always appear, even if I set the delay to 0. But I would need to look at this in detail to find out why exactly this happened or whether it was something else in my real code. $\endgroup$ – halirutan Mar 23 '15 at 15:58
16
$\begingroup$

OK, I guess I found something myself while trying to circumvent RunScheduledTask.

DynamicModule[{prog = False},
  Column[{
   Button[
     "Do heavy work", 
      prog = True; Pause[10]; prog = False, 
      Method -> "Queued"
   ],
   Dynamic@If[prog,
     ProgressIndicator[Appearance -> "Percolate"], 
     Invisible[ProgressIndicator[Appearance -> "Percolate"]]
   ]
  }]
]

This gives a very responsive and nice looking progress-bar. Here in its use case:

enter image description here

$\endgroup$
  • $\begingroup$ Instead of the If statement one can use PaneSelector. Its default setting ImageSize -> All ensures that its overall size doesn't change. I used it in this answer. Unfortunately one has to work around this bug by using, for example, Dynamic@Setting@prog. $\endgroup$ – Karsten 7. Sep 29 '16 at 18:52
13
+100
$\begingroup$

Building on Kubas code here is an alternative which solves some (minor) problems: it will not leave behind scheduled tasks when the abort button is used, it works equaly well when the Alt+. shortcut is used to abort and it might work better when aborting preemptive evaluations. Here is the code:

SetAttributes[withProgressIndicator, HoldFirst];

withProgressIndicator[proc_, delay : _?NonNegative : 1] := 
  Module[{dialog, scheduledTask, dialogPrompt}, 
   dialogPrompt[] := (
      dialog = CreateDialog[
        Column[{
         Dynamic[ProgressIndicator[Appearance -> "Indeterminate"]],
         Button["Abort", FrontEndToken["EvaluatorAbort"],Evaluator -> None]
        }], 
        WindowFrame -> "ModalDialog", 
        WindowFrameElements -> None,
        WindowTitle -> "Processing..."
     ]
   );
   scheduledTask = RunScheduledTask[dialogPrompt[], {delay}];
   CheckAbort[proc, $Aborted];
   RemoveScheduledTask[scheduledTask];
   NotebookClose[dialog];
 ]

The main change is that I use CheckAbort to check for aborts, which ensures that the cleanup-part is done in any case. That will make it possible to remove the scheduled task even when the computation was aborted and to close the progress dialog in the main code, not the button callback. As the button callback now only does the abort, we don't need a kernel for that and can let the frontend handle the callback diretly using Evaluator->None. I think that will work in some corner cases where the preemptive kernel-callback might not, but don't know how relevant that is. If that would cause problems, one of course could stick with the preemptive kernel-callback and replace the FrontEndToken with a FrontEndTokenExecute...

$\endgroup$
  • $\begingroup$ I forgot to tell you that it is really good tip for UI programming. +1ed long time ago $\endgroup$ – Kuba Sep 29 '16 at 13:21
  • $\begingroup$ @Kuba: Thanks. Actually I was mainly answering that because I remembered that I once knew how to do this but couldn't find the relevant code anymore. So I hope the next time I'll find this :-) $\endgroup$ – Albert Retey Sep 29 '16 at 14:23

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.