The bar object
forbear exports the bar, the two types of the fields of a program, and two character kinds:
use forbear, only : bar_object, field_object, progress_object, ASCII, UCS4| Method | What it does |
|---|---|
initialize | Set up the bar: its elements, its range, what it reports, where and how it draws. Resets every other setting. |
start | Print the scale (if asked), draw the bar at 0%, take over the terminal line. |
update | Compute the progress of the current value, redraw the bar when due; at 100% end its line. |
finish | End the bar where it is: an indeterminate bar, a loop left before its end. |
write | Print a line above the running bar. |
suspend, resume | Clear the bar while other code prints freely, then draw it again. |
add_field | Add a field of the program, for the template. |
is_stdout_locked | True while the bar is running on a terminal, from start to 100% or finish. |
destroy | Reset the bar to its defaults. |
= | Copy a bar (intrinsic assignment). |
initialize
call bar%initialize([keyword=value, ...])Every argument is optional and passed by keyword. initialize first resets the bar (see destroy): every setting not passed takes its default, whatever the bar had before.
Elements. Each element has a string and three colour keywords; the strings are class(*), of default, ASCII or UCS4 kind. The colours and the styles are names: see Colours and styles.
| Element | String (default) | Colour keywords |
|---|---|---|
| Prefix | prefix_string (none) | prefix_color_fg, prefix_color_bg, prefix_style |
| Suffix | suffix_string (none) | suffix_color_fg, suffix_color_bg, suffix_style |
| Left bracket | bracket_left_string (none) | bracket_left_color_fg, bracket_left_color_bg, bracket_left_style |
| Right bracket | bracket_right_string (none) | bracket_right_color_fg, bracket_right_color_bg, bracket_right_style |
| Done part | filled_char_string (*; █ with partial_blocks; ▌ with a theme) | filled_char_color_fg, filled_char_color_bg, filled_char_style |
| Remaining part | empty_char_string (-; a space with partial_blocks; ▌ with a theme) | empty_char_color_fg, empty_char_color_bg, empty_char_style |
| Spinner | spinner_string (none): the key of a spinner | spinner_color_fg, spinner_color_bg, spinner_style |
| Message | given by update | message_color_fg, message_color_bg, message_style |
Reports. Each one is off by default and has the same three colour keywords.
| Switch | Adds | Colour keywords |
|---|---|---|
add_progress_percent | the progress, nnn% | progress_percent_color_fg, progress_percent_color_bg, progress_percent_style |
add_progress_count | the count, current/max, integers with whole-number bounds | progress_count_color_fg, progress_count_color_bg, progress_count_style |
add_progress_speed | the smoothed speed, (nnn.nn%/s), the number in six characters | progress_speed_color_fg, progress_speed_color_bg, progress_speed_style |
add_eta | the estimated time to the end, ETA hh:mm:ss | eta_color_fg, eta_color_bg, eta_style |
add_scale_bar | at the start, a line with min_value and max_value above the bar | scale_bar_color_fg, scale_bar_color_bg, scale_bar_style |
add_date_time | at the end, a line [start - end], yyyy/mm/dd hh:mm:ss each | date_time_color_fg, date_time_color_bg, date_time_style |
add_summary | at the end, a line [done in <duration>, <mean speed>/s] | summary_color_fg, summary_color_bg, summary_style |
Numbers and switches.
| Keyword | Type | Default | Meaning |
|---|---|---|---|
width | integer(int32) | 32 | Characters between the brackets; 0 for no bar body. At least 22 with add_scale_bar, or the program stops (error stop). |
min_value | real(real64) | 0 | Start of the range. |
max_value | real(real64) | 1 | End of the range. |
partial_blocks | logical | .false. | Draw the done part with full and partial blocks, eight steps per character. |
bar_profile | character(*) | 'flat' | The glyph of each cell: 'flat', the filled and empty strings; 'ramp', blocks rising along the body. See Profiles. |
pulse_trail | character(*) | none | With indeterminate: the moving block becomes a one-cell head with a trail of fading colours, e.g. 'red_intense red black_intense'. See Pulse trail. |
digits | character(*) | 'plain' | 'segment' writes the digits of the percent, count, ETA and elapsed time as seven-segment digits; the font must have them. See Segment digits. |
digits_unlit_color | character(*) | none | With digits='segment', the padding of the numbers as unlit 🯸s in this colour. |
theme | character(*) | none | The colours and glyphs of a 1980s dashboard display, 'vfd', 'amber' or 'kitt', for every keyword not passed. See Themes. |
bar_zones | character(*) | none | Colour each filled cell by its position: limit:colour items, e.g. '0.7:green 0.9:yellow 1:red'. See Zones. |
min_interval | real(real64) | 0.1 | Minimum time between two drawings on a terminal, in seconds; FORBEAR_MIN_INTERVAL replaces the default. |
log_interval | real(real64) | 0 | In a log, also write a line when this many seconds have passed since the last one; 0 for none. FORBEAR_LOG_INTERVAL replaces the default. On a terminal it does nothing. |
frequency | integer(int32) | 1 | With 1, draw at every update due; with f > 1, only when the progress enters a new multiple of f% (and at 100%). In a log, a line every f% (every 10% with 1). |
smoothing | real(real64) | 0.3 | Weight of the last speed in its moving average: 1 the momentary speed, 0 the average since the start. |
position | integer(int32) | 0 | Line of the bar, counted below the current one; a bar at a position larger than 0 is cleared when it completes. |
interactive | logical | detected | Draw for a terminal (.true.) or write a plain log (.false.). Not passed: FORBEAR_INTERACTIVE, else whether output_unit is a terminal. See Terminals and logs. |
disabled | logical | .false. | Draw nothing; write still prints. FORBEAR_DISABLE turns every bar off. |
template | character(*) | none | The layout of the bar line: see Layout templates. Without it, the keywords above describe the line. |
indeterminate | logical | .false. | The total is unknown: current counts what is done from min_value, the bar body is a block going back and forth, and only finish ends the bar. max_value and frequency are ignored; a percent, an ETA or a scale stops the program (error stop). |
hide_cursor | logical | .true. | Hide the cursor while the bar runs on a terminal, show it again at 100%. .false. leaves it visible: see If the program stops. |
output_unit | integer(int32) | standard output | The unit the bar is written to, e.g. error_unit. |
The line
Without a template, update draws one line, in this order:
prefix bracket_left done part remaining part bracket_right suffix spinner percent count speed ETA messageThe done part has nint(progress / 100 * width) characters; with partial_blocks, int(fraction * width) full blocks and a partial block for the eighths of the next character, in the foreground of filled_char and the background of empty_char. With bar_zones, each filled cell, the partial one included, takes the foreground of its zone. The filled and empty strings are repeated as they are: with more than one character each, the bar is wider than width. With width=0 the bar body is empty and the line is the rest: a spinner or a percentage alone.
The prefix and the suffix can change while the bar runs: assign bar%prefix%string or bar%suffix%string (any string, as prefix_string), and the next drawing shows it, in the colours set by initialize (or by the template). For instance, the name of the phase of a solver: bar%prefix%string = 'assemble '.
On a terminal, every drawing starts with the ANSI sequence that hides the cursor (unless hide_cursor=.false.) and ends with "erase to the end of the line" and a carriage return: the next drawing overwrites it, and a shorter line leaves nothing behind. Line wrapping is off while a drawing is written, so a line wider than the terminal is cut at its right edge (see Lines wider than the terminal). The end of the bar shows the cursor again. In a log, the line is written as it is, with no colours and no control sequences.
Profiles
bar_profile='ramp' draws every cell of the body as a block rising from ▁ (one eighth high) in the first cell to █ in the last, as the bar graph of a dashboard tachometer. Done cells are lit, in the colours of filled_char (or of their zone); remaining cells keep their blocks, unlit, in the colours of empty_char, whose foreground is black_intense unless empty_char_color_fg is given. The filled and empty strings are not used. A log has no colours, so its remaining cells are blank: ▁▃ for 4 cells half done. partial_blocks draws blocks of its own: together with a ramp, it stops the program; so does a profile that is not flat or ramp.
call bar%initialize(prefix_string='RPM ', prefix_color_fg='#2EF5C0', prefix_style='bold_on', &
bar_profile='ramp', empty_char_color_fg='#0E342C', &
bar_zones='0.7:#2EF5C0 0.88:#FFB000 1:#FF3B30', &
add_progress_percent=.true., progress_percent_color_fg='#FFB000', &
width=40, max_value=real(steps, R8P))At 80%:
$ tach
RPM ▁▁▁▁▁▁▂▂▂▂▂▂▃▃▃▃▃▄▄▄▄▄▄▅▅▅▅▅▆▆▆▆▆▆▇▇▇▇▇█ 80%Pulse trail
The body of an indeterminate bar is a block going back and forth, one cell per drawing. pulse_trail turns it into a scanner: a head of one cell, in the colours of filled_char (or of its zone), and behind it the cells where the head was on the last drawings, one colour per drawing back, from the first colour of the list (the brightest, as a rule) to the last. The colours are names or #rrggbb, separated by blanks; they replace the foreground of filled_char. At each end the head turns back over its own trail, as a real scanner does; where two drawings meet in a cell, the later one is drawn. The 17 named colours give two or three shades of one hue ('red_intense red black_intense'); #rrggbb gives as many as wanted. A log draws no pulse, so the trail changes nothing there. On a bar that is not indeterminate, or with an unknown colour, pulse_trail stops the program.
call bar%initialize(prefix_string='solve ', prefix_color_fg='#FF3B30', &
indeterminate=.true., width=24, add_progress_count=.true., progress_count_color_fg='#FFB000', &
filled_char_string='▬', filled_char_color_fg='#FF3B30', &
empty_char_string='▬', empty_char_color_fg='#3C0C0A', &
pulse_trail='#C0281E #7A1912 #4A0F0B')On the way back:
$ solve
solve ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ 31Segment digits
digits='segment' writes the digits of the percent, the count, the ETA and the elapsed time as seven-segment digits, 🯰🯱🯲🯳🯴🯵🯶🯷🯸🯹, as the speedometer of a dashboard; colons, slashes, points and the % stay as they are. Each number has a fixed width, padded on the left with blanks: with digits_unlit_color, the padding becomes unlit 🯸s in that colour, as the unlit segments of a display. The speed and the summary keep plain digits (their compact forms have letters), and so does a log, so that it can be searched.
The font must have them
The seven-segment digits are U+1FBF0–U+1FBF9, in the Symbols for Legacy Computing block (Unicode 13, 2020). Cascadia Code and Cascadia Mono, Iosevka, JuliaMono and GNU Unifont have them; DejaVu Sans Mono, Menlo, Consolas and most default terminal fonts do not, and show empty boxes or a fallback font whose digits may be wider, which shifts the line. forbear cannot know the font of the terminal: use digits='segment' only where you know it. This site ships the ten digits as a web font, so the outputs below look right whatever your fonts.
call bar%initialize(prefix_string='march ', prefix_color_fg='#2EF5C0', width=30, &
filled_char_string='▌', filled_char_color_fg='#2EF5C0', &
empty_char_string='▌', empty_char_color_fg='#0E342C', &
add_progress_percent=.true., progress_percent_color_fg='#FFB000', &
add_progress_count=.true., progress_count_color_fg='#FFB000', &
digits='segment', digits_unlit_color='#3A2800', max_value=real(steps, R8P))At 5%:
$ march
march ▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌▌ 🯸🯸🯵% 🯸🯸🯷/🯱🯲🯰start
call bar%startResets the run state of the bar (timer, speed, spinner, message, completion) and takes the start time. Unless the bar is disabled, it prints the scale line (with add_scale_bar, at position 0 only), marks the terminal as taken (is_stdout_locked becomes true, on a terminal) and draws the bar at min_value. A bar can be started again after it has completed.
update
call bar%update(current=value [, message=text]) ! value: real(real64); text: any string- If the bar is disabled or complete, nothing happens.
- A
message, if passed, replaces the one shown at the end of the line. A suspended bar stops here: it recordscurrentandmessage, andresumedraws them. - The progress is
(current - min_value) / (max_value - min_value), clamped to [0, 1], truncated to an integer percent: 100% only whencurrentreachesmax_value. - On a terminal, the bar is drawn when the update is due: always at 0% and 100%; otherwise if at least
min_intervalseconds have passed since the last drawing and, withfrequency > 1, the progress has entered a new multiple offrequency. In a log, a line is written when the progress enters a new multiple of 10% (offrequency%), or whenlog_intervalseconds have passed since the last line. - At each drawing, the spinner moves one frame and the speed is updated: the progress made since the previous drawing, over the time elapsed since then, averaged with
smoothing. - At 100% the bar is complete: on a terminal, the cursor is shown again and the line ended (a bar at a position larger than 0 is cleared instead); the start and end line and the summary are printed, if asked;
is_stdout_lockedbecomes false.
An indeterminate bar counts current - min_value (not below 0) and never reaches 100%: it is drawn at most once every min_interval seconds on a terminal; in a log, at the start, at finish, and every log_interval seconds if set.
finish
call bar%finish([message=text]) ! text: any stringEnds a running bar where it is, as 100% would: it draws the last update (an indeterminate bar with its track full), with the message if passed, then ends the line, shows the cursor and writes the date and summary lines, if asked. The rate of the summary is that of what was done. In a log, it writes a last line unless the previous one already shows that progress. A suspended bar is drawn again to end. After finish, updates do nothing until the next start. On a bar not running (not started, already complete, disabled) it does nothing. Call it after a loop that may exit early, and to end an indeterminate bar.
write
call bar%write(text) ! text: any stringWhile the bar runs on a terminal, write clears the bar line, writes the text in its place and draws the bar again on the line below: the text scrolls up with the output, the bar stays at the bottom. Otherwise (before start, after 100%, in a log, with the bar disabled) it writes the text as a line of the unit of the bar. With nested bars, call the write of the bar at position 0.
suspend and resume
call bar%suspend
call solver_library_step ! prints with print, write, or from C: anything
call bar%resumewrite covers the lines your program prints. Output you do not control, from a library, the MPI runtime or a print deep in a call, would be drawn over the bar: suspend steps aside first. It clears the bar line, shows the cursor at its start and frees the terminal (is_stdout_locked becomes false); the other output goes there and below. Meanwhile updates record the progress without drawing it, and resume draws the bar again, on the line where the cursor is, with the last update; if that update reached 100%, the bar completes there. A bar at a position larger than 0 clears its own line: suspend every running bar of the terminal, nested ones included, and resume them after.
In a log, on a disabled bar, or on a bar not running, suspend and resume do nothing: there, other output and the lines of the bar never overlap. finish ends a suspended bar too.
is_stdout_locked
logical :: running
running = bar%is_stdout_locked()True from start until the bar reaches 100% or is finished, on a terminal, except while it is suspended: while it is true, anything written to the terminal but through write is drawn over the bar. It reports the state of the bar, and locks nothing.
destroy
call bar%destroyFrees the strings and resets the defaults: width=32, range [0, 1], frequency=1, min_interval=0.1, log_interval=0, smoothing=0.3, position 0, standard output, hide_cursor=.true., no reports, a flat profile, plain digits, no zones, no pulse trail. initialize calls it first.
Character kinds
ASCII and UCS4 are the kinds of the strings of the elements. With the GNU modes of the fobos file they are the ASCII and ISO 10646 kinds; with the Intel and NVIDIA modes, and with fpm, they fall back to the default kind. Plain string literals, Unicode ones included in a UTF-8 source, work in every case: forbear converts every string to UCS4 internally.