progress indicator without dynamic

Here is one approach, based on @b3m2a1's excellent answer here:

Attributes[StaticMonitor] = {HoldAll};
UpdateMonitor[] := Null;
StaticMonitor[expr_, mon_] :=
 Block[
  {UpdateMonitor, boxID = ToString@Unique[]},
  PrintTemporary[RawBoxes@TagBox[ToBoxes@mon, boxID, BoxID -> boxID]];
  UpdateMonitor[] := FrontEndExecute@FrontEnd`BoxReferenceReplace[
     FE`BoxReference[
      FE`Evaluate@FrontEnd`EvaluationNotebook[],
      {{boxID}},
      FE`BoxOffset -> {FE`BoxChild[1]}
      ],
     ToBoxes@mon
     ];
  expr
  ]

Since we can't rely on Dynamic to automatically trigger updates, we need to call UpdateMonitor[] to force the update. The actual updating is done by replacing the contents on the cell created by PrintTemporary using the BoxReference* utilities explained in this answer. One could also replace the entire cell created by PrintTempoary, but that leads to undesirable flashing of the scrollbar.

Here is how it looks like in action:

StaticMonitor[
 Table[
  [email protected];
  UpdateMonitor[];
  i,
  {i, 10}
  ],
 ProgressIndicator[i, {0, 10}]
 ]

enter image description here


The folllowing solution is based on some functionalities of https://github.com/Ludwiggle/JWLS_2

In particular, we borrow the webListenerF function, the refresh function and the refresh.wl file from the JWLS_2 project. refresh.wl is a StringTemplate that the refresh function uses to create a dynamic HTML page; such HTML page will serve as our monitor/progress indicator.

What you need:

  1. Download the template from https://github.com/Ludwiggle/JWLS_2/blob/master/JWLS_2_kernel/refresh.wl and put it in your working directory.
  2. Copy the webListenerF function definition from line 55 to line 77 of https://github.com/Ludwiggle/JWLS_2/blob/master/JWLS_2.sh
  3. Copy the refresh function definition from line 139 to 144 of the same JWLS_2.sh script above

Usage Example

Let's say you have some computation in a loop and you want to monitor the value of a variable g:

First of all, activate the socket with SocketListen[5859, webListenerF] . Then create and open the HTML page that monitors the value of g with refresh[HoldForm @ g] // SystemOpen .

Now you can start your loop process where g gets updated, something like

Clear[g,k]     
k = {RandomReal[]};

While[True,
      AppendTo[k, RandomReal[]];
      g = ListLinePlot[k, PlotMarkers -> {Automatic, 10}];
      [email protected]]

Check your browser and enjoy.

enter image description here

Cons: It works outside the notebook interface.

Pros: It works without the notebook interface.

Notes:

The refresh function takes the page refresh interval in milliseconds as a second argument.

For smoother animations, modify the webListenerF to export rasters instead of vector graphics. Then add the proper HTML tags in the template.

For maximum efficiency and smoothness, define the whole computation inside refresh, so that the kernel executes your code while synchronously serving the javascript client.

If something goes wrong try to close and reopen the socket.