10

In the last 10 years (see this post) when I want to track the result of a longer calculation, I use the following pattern:

SetAttributes[progressBar,HoldFirst]
progressBar[i_,total_]:= Module[{},
    Echo@Dynamic@Row[
        {ProgressIndicator[i,{0,total}]," ",NumberForm[100.i/total,{\[Infinity],2}],"% ",i}
    ]
]

so I can monitor a function as follows, using progressBar

progressExample[data_List]:= Module[{i = 0,len = Length@data},
    progressBar[i,len];
    Scan[(i++;Pause[0.1];f[#])&,data]
]
progressExample[Range[10]]

enter image description here

The problem is that this code crashes for long runs, I think because of the use of Dynamic. Some idea for a better way to do that? Any suggestions for a more modern or native solution?

Murta
  • 26,275
  • 6
  • 76
  • 166

2 Answers2

9

If you are willing to explicitly insert a call inside your computation loop to a progress updating small function, then you can do this in much simpler way without using Dynamics at all.

It is not a progress indicator, but prints the progress % only. It only uses PrintTemporary. So you have to make this explicit call anywhere in your code you want to update the progress. I would put it at the bottom of the loop for example.

Video

enter image description here

Code

CurrentValue[$FrontEnd, {"PrintAction"}] = {"PrintToNotebook"}
progressBar[currentValue_?NumericQ, maxValue_?NumericQ] := Module[{per},
      NotebookDelete[temp]; 
      per = Row[{NumberForm[100. currentValue/maxValue, {∞, 2}], "% "}];
      temp = PrintTemporary[per];
  ]

progressExample[data_List] := Module[{i = 0, len = Length@data}, Scan[ (i++; Pause[0.2]; progressBar[i, len]; f[#]) &, data ] ];

progressExample[Range[60]]

Version that uses PrintTemporary with ProgressIndicator

This version adds the actual bar, but still does not use Dynamics. Only PrintTemporary

progressBarV2[currentValue_?NumericQ, maxValue_?NumericQ] := Module[{per},
      NotebookDelete[temp]; 
      per = Row[{Spacer[5], NumberForm[100. currentValue/maxValue, {∞, 2}], "%"}];
      temp = PrintTemporary@Row[{ProgressIndicator[currentValue, {0, maxValue}], per}]
  ]

So just call the above V2 instead. This is the result

enter image description here

Nasser
  • 143,286
  • 11
  • 154
  • 359
  • 1
    While this doesn't use Dynamic, it does perform notebook manipulation (printing and deleting cells). And in my experience, cell creation can be worse for performance than a suitably optimized dynamic expression. But of course, if the Dynamic expressions are actually causing the crash, this might help. (Assuming the repeated cell creation doesn't upset the front-end even more) – Lukas Lang Aug 13 '22 at 13:23
3
progressBar[i_, high_] := Module[{},
  Row[{
    ProgressIndicator[i, {0, high}],
    Spacer[10], i,
    " (", NumberForm[100. i/high, {\[Infinity], 2}], "%)"
    }]
  ]

progressExample[data_List] := Module[{i = 0, len = Length@data},
  Monitor[
   Scan[(i++; Pause[0.1]; f[#]) &, data],
   progressBar[i, len]
   ]
  ]

progressExample[Range[150]]
Jean-Pierre
  • 5,187
  • 8
  • 15