File: progressbar.h

package info (click to toggle)
mrtrix3 3.0.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 13,712 kB
  • sloc: cpp: 129,776; python: 9,494; sh: 593; makefile: 234; xml: 47
file content (327 lines) | stat: -rw-r--r-- 10,084 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
/* Copyright (c) 2008-2022 the MRtrix3 contributors.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Covered Software is provided under this License on an "as is"
 * basis, without warranty of any kind, either expressed, implied, or
 * statutory, including, without limitation, warranties that the
 * Covered Software is free of defects, merchantable, fit for a
 * particular purpose or non-infringing.
 * See the Mozilla Public License v. 2.0 for more details.
 *
 * For more details, see http://www.mrtrix.org/.
 */

#ifndef __progressbar_h__
#define __progressbar_h__

#include <string>
#include <memory>
#include <mutex>
#include <condition_variable>
#include <chrono>


#include "mrtrix.h"
#include "timer.h"
#include "types.h"
#include "math/math.h"
#include "debug.h"

#define BUSY_INTERVAL 0.1

namespace MR
{


  //! implements a progress meter to provide feedback to the user
  /*! The ProgressBar class displays a text message along with a indication of
   * the progress status. For command-line applications, this will be shown on
   * the terminal. For GUI applications, this will be shown as a graphical
   * progress bar.
   *
   * It has two modes of operation:
   * - percentage completion: if the maximum value is non-zero, then the
   * percentage completed will be displayed. Each call to
   * ProgressBar::operator++() will increment the value by one, and the
   * percentage displayed is computed from the current value with respect to
   * the maximum specified.
   * - busy indicator: if the maximum value is set to zero, then a 'busy'
   * indicator will be shown instead. For the command-line version, this
   * consists of a dot moving from side to side.
   *
   * Other implementations can be created by overriding the display_func() and
   * done_func() static functions. These functions will then be used throughout
   * the application.  */
  class ProgressBar { NOMEMALIGN
    public:

      //! Create an unusable ProgressBar.
      ProgressBar () : show (false) { }
      ProgressBar (const ProgressBar& p) = delete;
      ProgressBar (ProgressBar&& p) = default;

      FORCE_INLINE ~ProgressBar  () {
        done();
      }

      //! Create a new ProgressBar, displaying the specified text.
      /*! If \a target is unspecified or set to zero, the ProgressBar will
       * display a busy indicator, updated at regular time intervals.
       * Otherwise, the ProgressBar will display the percentage completed,
       * computed from the number of times the ProgressBar::operator++()
       * function was called relative to the value specified with \a target. */
      ProgressBar (const std::string& text, size_t target = 0, int log_level = 1);

      //! returns whether the progress will be shown
      /*! The progress may not be shown if the -quiet option has been supplied
       * to the application.
        * \returns true if the progress will be shown, false otherwise. */
      FORCE_INLINE operator bool () const {
        return show;
      }

      //! returns whether the progress will be shown
      /*! The progress may not be shown if the -quiet option has been supplied
       * to the application.
       * \returns true if the progress will not be shown, false otherwise. */
      FORCE_INLINE bool operator! () const {
        return !show;
      }

      FORCE_INLINE size_t value () const { return _value; }
      FORCE_INLINE size_t count () const { return current_val; }
      FORCE_INLINE bool show_percent () const { return _multiplier; }
      FORCE_INLINE bool text_has_been_modified () const { return _text_has_been_modified; }
      FORCE_INLINE const std::string& text () const { return _text; }
      FORCE_INLINE const std::string& ellipsis () const { return _ellipsis; }

      //! set the maximum target value of the ProgressBar
      /*! This function should only be called if the ProgressBar has been
       * created with a non-zero target value. In other words, the ProgressBar
       * has been created to display a percentage value, rather than a busy
       * indicator. */
      FORCE_INLINE void set_max (size_t new_target);

      FORCE_INLINE void set_text (const std::string& new_text);

      //! update text displayed and optionally increment counter
      /*! This expects a function, functor or lambda function that should
       * return a std::string to replace the text. This functor will only be
       * called when necessary, i.e. when BUSY_INTERVAL time has elapsed, or if
       * the percentage value to display has changed. The reason for passing a
       * functor rather than the text itself is to minimise the overhead of
       * forming the string in cases where this is sufficiently expensive to
       * impact performance if invoked every iteration. By passing a function,
       * this operation is only performed when strictly necessary.
       *
       * The simplest way to use this method is using C++11 lambda functions,
       * for example:
       * \code
       * progress.update ([&](){ return "current energy = " + str(energy_value); });
       * \endcode
       *
       * \note due to this lazy update, the text is not guaranteed to be up to
       * date by the time processing is finished. If this is important, you
       * should also use the set_text() method to set the final text displayed
       * before the ProgressBar's done() function is called (typically in the
       * destructor when it goes out of scope).*/
      template <class TextFunc>
        FORCE_INLINE void update (TextFunc&& text_func, bool increment = true);

      //! increment the current value by one.
      FORCE_INLINE void operator++ ();
      FORCE_INLINE void operator++ (int) { ++ (*this); }

      FORCE_INLINE void done () {
        if (show) {
          done_func (*this);
          progressbar_active = false;
        }
      }

      template <class ThreadType>
        void run_update_thread (const ThreadType& threads) const;

      struct SwitchToMultiThreaded { NOMEMALIGN
        SwitchToMultiThreaded ();
        ~SwitchToMultiThreaded ();
      };



      static bool set_update_method ();
      static void (*display_func) (const ProgressBar& p);
      static void (*done_func) (const ProgressBar& p);
      static void (*previous_display_func) (const ProgressBar& p);

      static std::condition_variable notifier;
      static bool notification_is_genuine;
      static std::mutex mutex;;
      static void* data;

      mutable bool first_time;
      mutable size_t last_value;

    private:

      const bool show;
      std::string _text, _ellipsis;
      size_t _value, current_val, next_percent;
      double next_time;
      float _multiplier;
      Timer timer;
      bool _text_has_been_modified;

      FORCE_INLINE void display_now () { display_func (*this); }

      static bool progressbar_active;
  };










  FORCE_INLINE ProgressBar::ProgressBar (const std::string& text, size_t target, int log_level) :
    first_time (true),
    last_value (0),
    show (std::this_thread::get_id() == ::MR::App::main_thread_ID && !progressbar_active && App::log_level >= log_level),
    _text (text),
    _ellipsis ("..."),
    _value (0),
    current_val (0),
    next_percent (0),
    next_time (0.0),
    _multiplier (0.0),
    _text_has_been_modified (false) {
      if (show) {
        set_max (target);
        progressbar_active = true;
      }
    }




  inline void ProgressBar::set_max (size_t target)
  {
    if (!show)
      return;
    if (target) {
      _multiplier = 0.01 * target;
    }
    else {
      _multiplier = 0.0;
      timer.start();
    }
  }



  FORCE_INLINE void ProgressBar::set_text (const std::string& new_text)
  {
    if (!show)
      return;
    _text_has_been_modified = true;
    if (new_text.size()) {
#ifdef MRTRIX_WINDOWS
      size_t old_size = _text.size();
#endif
      _text = new_text;
#ifdef MRTRIX_WINDOWS
      if (_text.size() < old_size)
        _text.resize (old_size, ' ');
#endif
    }
  }





  template <class TextFunc>
    FORCE_INLINE void ProgressBar::update (TextFunc&& text_func, const bool increment)
    {
      if (!show)
        return;
      double time = timer.elapsed();
      if (increment && _multiplier) {
        if (++current_val >= next_percent) {
          set_text (text_func());
          _ellipsis.clear();
          _value = std::round (current_val / _multiplier);
          next_percent = std::ceil ((_value+1) * _multiplier);
          next_time = time;
          display_now();
          return;
        }
      }
      if (time >= next_time) {
        set_text (text_func());
        _ellipsis.clear();
        if (_multiplier)
          next_time = time + BUSY_INTERVAL;
        else {
          _value = time / BUSY_INTERVAL;
          do { next_time += BUSY_INTERVAL; }
          while (next_time <= time);
        }
        display_now();
      }
    }




  FORCE_INLINE void ProgressBar::operator++ ()
  {
    if (!show)
      return;
    if (_multiplier) {
      if (++current_val >= next_percent) {
        _value = std::round (current_val / _multiplier);
        next_percent = std::ceil ((_value+1) * _multiplier);
        display_now();
      }
    }
    else {
      double time = timer.elapsed();
      if (time >= next_time) {
        _value = time / BUSY_INTERVAL;
        do { next_time += BUSY_INTERVAL; }
        while (next_time <= time);
        display_now();
      }
    }
  }





  template <class ThreadType>
    inline void ProgressBar::run_update_thread (const ThreadType& threads) const
    {
      if (!show)
        return;
      std::unique_lock<std::mutex> lock (mutex);
      while (!threads.finished()) {
        notifier.wait_for (lock, std::chrono::milliseconds(1), []{ return notification_is_genuine; });
        if (notification_is_genuine) {
          previous_display_func (*this);
          notification_is_genuine = false;
        }
      }
    }

}

#endif