Here we include a guide how to modify the progress printing. This chapter is intended for advanced users or developers of the package.
The progress printer is a framework built from a collection of printer moduls that are solely responsible to display a specific information about the current state of a process e.g. the number of completed iterations or the total runtime. Moreover it specifies an abstract layout e.g. the positional relations between the individual moduls.
In the following we explain the terminology of the framework and the technical implementation. See also Section 2.1 for some basic terminology.
In the end we include an example how to adjust the progress printer.
We can think of the terminal as a finite grid of cells each indexed by an (x, y)
-coordinate and assigned a single character. The cursor is an object that occupies the space of exactly one cell. We are able to perform the following actions:
move the cursor to an adjacent cell
overwrite the character of the cell occupied by the cursor
The coordinate (1, 1)
denotes the cell in the terminal where the usual Print
command would start to write a character, i.e. the initial position of the cursor after hitting the "enter key" on the gap prompt. We think of this cell as the top left corner of the space that we reserve to print the progress. Increasing the x
-coordinate can be understood as movement to the right, whereas increasing the y
-coordinate as movement to the bottom.
A block is a virtually allocated space in the terminal (see Section 4.1) in form of a rectangle, a record rec(x, y, w, h)
, where the top left corner of the interior is the cell at the coordinate (x, y)
, the width is given by w
and the height by h
, i.e. the bottom right corner of the interior is the cell at the coordinate (x + w - 1, y + h - 1)
.
For example we would see something akin to the following if we fill the inside of the block (x := 3, y := 2, w := 5, h := 2)
with the symbols "O
" and use a simple dot ".
" for the outside:
.......... ..OOOOO... ..OOOOO... .......... ..........
The main purpose of a pattern is to encode a coherent display of the progress. We use patterns to create blocks in a structured and uniform manner of which a few special ones are defining bounds for printer modules. The progress printer has a pattern tree whose nodes are patterns, records that contain the following entries:
Unique identifier string of the pattern.
A pattern or fail
.
List of patterns.
A function that takes as input a process and returns whether the branch starting in this pattern is active.
"horizontal"
all blocks created from children of this pattern are aligned horizontally, i.e. they have equal y
-coordinates and heights.
"vertical"
all blocks created from children of this pattern are aligned vertically, i.e. they have equal x
-coordinates and widths.
["dim", ...]
all blocks created from this pattern have equal values for the dimension dim, etc.
If a pattern is a leaf node, it must additionally contain the following entries:
A printer module.
An options record for the printer.
For each process we create blocks from the pattern tree using a recursive strategy: If the pattern is active for the given process (pattern.isActive(process) = True
), we create a block with the given id (stored in process.blocks.id
) and apply the same strategy for all children of the pattern.
A block created from the root of the pattern tree is called a process block. The width of a process block spans the whole terminal screen. Furthermore we require that the process blocks are stacked from top to bottom following the natural ordering of the process tree given by DFS (depth-first search).
A block created from an inner node of the pattern tree is called a composition block. The interior of such a block must be the disjoint union of the interior of its children, and either the children are aligned horizontally within the block, or vertically. Visually speaking, we split a composition block into smaller blocks by either using only vertical or horizontal cuts.
A block created from a leaf node of the pattern tree is called a printer block. This block corresponds to a printer module that is capable of displaying a specific information about the current state of a process inside the block bounds. (See 4.5)
The layout is a record containing the following entries:
A function that setups the following entries of the progress printer using the current options in ProgressPrinter.Options
:
ProgressPrinter.Pattern
ProgressPrinter.InitialConfiguration
The default options record for Setup
.
A printer module is responsible for displaying a specific information about the current state of a process e.g. the number of completed iterations or the total runtime. Technically, it is a record that must contain the following functions as entries where the argument options must be valid printer options for this module. For a description of all available printer modules, see Chapter 5.
‣ dimensions ( process, options ) | ( function ) |
Returns a dimensions record rec(w, h)
where the dimensions are positive integers or fail
.
If the values are integers, they specify the smallest dimensions of a block that is capable to display the information about the current state of this process.
A value fail
indicates that the dimension is unspecified and can be replaced by any arbitrary positive integer.
‣ generate ( process, id, options ) | ( function ) |
Displays the information of this process in the block with identifier id under the assumption that the block contains no characters and is large enough to display the information.
Therefore the printer usually needs to write characters for most of the interior of the block.
‣ update ( process, id, options ) | ( function ) |
Displays the information of this process in the block with identifier id under the assumption that generate
was called beforehand and the block bounds haven't changed since then.
Therefore the printer usually just updates a few characters in the terminal.
Returns true
if it is capable to display the information in the block, otherwise false
.
The progress printer, as described in the introduction of this chapter, is implemented as a record that eventually contains the following entries:
A record rec(Setup, DefaultOptions)
for setting up a layout.
The process tree containing the main process as the root.
A record rec(w, h)
encoding the virtual bounds in the terminal.
A record rec(x, y)
encoding the cursor position.
An internal time stamp that is changed whenever a process is refreshed.
The current options record for Layout.Setup
.
The tree consisting of patterns.
A configuration is a list containing entries describing linear equations. These describe additional relations between the blocks created from the patterns of the layout.
An entry is of the form [ factor, rec(id, param), ..., a ]
which is interpreted as factor * id.param + ... = a
. Here, factor
and a
must be integers, id
the identifier of a pattern in the layout and param
a parameter of a block, i.e. either "x"
, "y"
, "w"
or "h"
.
The process that is currently running.
See Section 2.1 for the basic terminology. An abstract process is encoded via a process record, but we usually omit the distinction between them. The record has the following entries:
Unique identifier string of the process.
A process record or fail
.
List of process records.
The depth is a non-negative integer showing how far nested this process is in the root. The root has depth = 0
. Any other process has depth = parent.depth + 1
.
Total runtime of process up to the most recent refresh.
The main intent is to mark for the RefreshProcess
command how to update the total runtime of a process.
A permanent status is one that can last after a refresh:
"active"
, "inactive"
, "complete"
.
A temporary status is one that transforms immediately into a new one after a refresh:
"started"
, "stopped"
, "terminated"
.
Below is a flow chart showcasing how each status can be transformed into one another, given that doRefresh is true. Therefore we only deal with the commands StartProcess
, StopProcess
and TerminateProcess
for a permanent status. We ommit the arrows for those commands that do not change the status.
+----------+ +--------> | | Refresh | | inactive | <--------------+ +----------+ | | Refresh +----+-----+ | | | | Start | | | v | +----------+ | | | | | started | | | | | +----+-----+ | | | | Refresh | | | v | +----------+ +-----+----+ +--------> | | Stop | | | | active +--------> | stopped | +----------+ | | | Refresh +----+-----+ +----------+ | | Terminate | v +----------+ | | |terminated| | | +----+-----+ | | Refresh | v +----------+ +--------> | | | | complete | +----------+ | Refresh +----------+
We mention one edge case for completeness. If a process is defined via an iterator that doesn't complete after totalSteps
, we change back from the complete
status to the active
status, and set totalSteps := infinity
and blocks := rec()
.
The number of completed steps of a process. It is initialized with -1. This indicates that the process hasn't started yet.
Number of comleted steps for a process to be marked as terminated.
Extra content of the process that may be used by the progress printer or printer modules.
A record containing entries id := block
, where id
is a string corresponding to an id in the used pattern, and a block
is a record of the form rec(x, y, w, h)
. A printer block may be popullated with additional entries by the corresponding printer module.
TODO
generated by GAPDoc2HTML