Safe Haskell | None |
---|---|
Language | Haskell2010 |
A simple progress bar in the terminal.
A progress bar is used to convey the progress of a task. This module implements a very simple textual progress bar.
Synopsis
- data ProgressBar s
- newProgressBar :: Style s -> Double -> Progress s -> IO (ProgressBar s)
- hNewProgressBar :: Handle -> Style s -> Double -> Progress s -> IO (ProgressBar s)
- renderProgressBar :: Style s -> Progress s -> Timing -> Text
- updateProgress :: forall s. ProgressBar s -> (Progress s -> Progress s) -> IO ()
- incProgress :: ProgressBar s -> Int -> IO ()
- data Style s = Style {
- styleOpen :: !Text
- styleClose :: !Text
- styleDone :: !Char
- styleCurrent :: !Char
- styleTodo :: !Char
- stylePrefix :: Label s
- stylePostfix :: Label s
- styleWidth :: !ProgressBarWidth
- styleEscapeOpen :: EscapeCode s
- styleEscapeClose :: EscapeCode s
- styleEscapeDone :: EscapeCode s
- styleEscapeCurrent :: EscapeCode s
- styleEscapeTodo :: EscapeCode s
- styleEscapePrefix :: EscapeCode s
- styleEscapePostfix :: EscapeCode s
- type EscapeCode s = Progress s -> Text
- defStyle :: Style s
- data ProgressBarWidth
- = ConstantWidth !Int
- | TerminalWidth !Int
- data Progress s = Progress {
- progressDone :: !Int
- progressTodo :: !Int
- progressCustom :: !s
- newtype Label s = Label {}
- data Timing = Timing {}
- msg :: Text -> Label s
- percentage :: Label s
- exact :: Label s
- elapsedTime :: (NominalDiffTime -> Text) -> Label s
- remainingTime :: (NominalDiffTime -> Text) -> Text -> Label s
- totalTime :: (NominalDiffTime -> Text) -> Text -> Label s
- renderDuration :: NominalDiffTime -> Text
How to use this library
We want to perform some task which we expect to take some time. We wish to show the progress of this task in the terminal.
First we write a dummy function which represents a unit of work. This could be a file copy operation, a network operation or some other expensive calculation. In this example we simply wait 1 second.
work :: IO () work = threadDelay 1000000 -- 1 second
And we define some work to be done.
toBeDone :: [()] toBeDone = replicate 20 ()
Now we create a progress bar in the terminal. We use the default style and choose a maximum refresh rate of 10 Hz. The initial progress is 0 work done out of 20.
pb <-newProgressBar
defStyle
10 (Progress
0 20 ())
Let's start working while keeping the user informed of the progress:
for_ toBeDone $ () -> do
work -- perform 1 unit of work
incProgress
pb 1 -- increment progress by 1
That's it! You get a nice animated progress bar in your terminal. It will look something like this:
[==========>................................] 25%
Explore the Style
and the Label
types to see various ways in which
you can customize the way the progress bar looks.
You do not have to close the progress bar, or even finish the task. It is perfectly fine to stop half way (maybe your task throws an exception).
Just remember to avoid outputting text to the terminal while a progress bar is active. It will mess up the output a bit.
Progress bars
data ProgressBar s Source #
A terminal progress bar.
A ProgressBar
value contains the state of a progress bar.
It is produced by newProgressBar
and hNewProgressBar
.
It is updated by updateProgress
and incProgress
.
Instances
NFData s => NFData (ProgressBar s) Source # | |
Defined in System.ProgressBar rnf :: ProgressBar s -> () # |
:: Style s | Visual style of the progress bar. |
-> Double | Maximum refresh rate in Hertz. |
-> Progress s | Initial progress. |
-> IO (ProgressBar s) |
Creates a new progress bar.
The progress bar is drawn immediately. You can update the progress
bar using updateProgress
or incProgress
. You shouldn't output
anything to your terminal between updates. It will mess up the
animation.
The progress bar is written to stderr
. Use hNewProgressBar
if
you would like the progress bar output send to another handle.
:: Handle | File handle on which the progress bar is drawn. Usually
you would select a standard stream like |
-> Style s | Visual style of the progress bar. |
-> Double | Maximum refresh rate in Hertz. |
-> Progress s | Initial progress. |
-> IO (ProgressBar s) |
Creates a new progress bar on a given handle.
See newProgressBar
for more information.
renderProgressBar :: Style s -> Progress s -> Timing -> Text Source #
Renders a progress bar.
>>>
let t = UTCTime (ModifiedJulianDay 0) 0
>>>
renderProgressBar defStyle (Progress 30 100 ()) (Timing t t)
"[============>..............................] 30%"
Not that this function can not use TerminalWidth
because it
doesn't use IO
. Use progressBar
or hProgressBar
to get
automatic width.
:: ProgressBar s | Progress bar which needs an update. |
-> (Progress s -> Progress s) | Function to change the progress. |
-> IO () |
Change the progress of an existing progress bar.
This will cause the progress bar to be redrawn. If updates occur to fast some updates will not be drawn.
This function is thread safe, but blocking. Multiple threads may update a single progress bar at the same time.
:: ProgressBar s | Progress bar which needs an update. |
-> Int | Amount by which the increment the progress. |
-> IO () |
Increment the progress of an existing progress bar.
See updateProgress
for more information.
Options
Options that determine the textual representation of a progress bar.
The textual representation of a progress bar follows the following template:
<prefix><open><done><current><todo><close><postfix>
Where <done> and <todo> are repeated as often as necessary.
Consider the following progress bar
"Working [=======>.................] 30%"
This bar can be specified using the following style:
Style
{styleOpen
= "[" ,styleClose
= "]" ,styleDone
= '=' ,styleCurrent
= '>' ,styleTodo
= '.' ,stylePrefix
=msg
"Working" ,stylePostfix
=percentage
,styleWidth
=ConstantWidth
40 ,styleEscapeOpen
= constempty
,styleEscapeClose
= constempty
,styleEscapeDone
= constempty
,styleEscapeCurrent
= constempty
,styleEscapeTodo
= constempty
,styleEscapePrefix
= constempty
,styleEscapePostfix
= constempty
}
Style | |
|
Instances
type EscapeCode s Source #
An escape code is a sequence of bytes which the terminal looks for and interprets as commands, not as character codes.
It is vital that the output of this function, when send to the terminal, does not result in characters being drawn.
A default style.
You can override some fields of the default instead of specifying
all the fields of a Style
record.
The default does not use any escape sequences.
data ProgressBarWidth Source #
Width of progress bar in characters.
ConstantWidth !Int | A constant width. |
TerminalWidth !Int | Use the entire width of the terminal. Identical to |
Instances
Generic ProgressBarWidth Source # | |
Defined in System.ProgressBar type Rep ProgressBarWidth :: Type -> Type # from :: ProgressBarWidth -> Rep ProgressBarWidth x # to :: Rep ProgressBarWidth x -> ProgressBarWidth # | |
NFData ProgressBarWidth Source # | |
Defined in System.ProgressBar rnf :: ProgressBarWidth -> () # | |
type Rep ProgressBarWidth Source # | |
Defined in System.ProgressBar type Rep ProgressBarWidth = D1 (MetaData "ProgressBarWidth" "System.ProgressBar" "terminal-progress-bar-0.4.0.1-GP7WacvU9SLabrqXY70gz" False) (C1 (MetaCons "ConstantWidth" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 Int)) :+: C1 (MetaCons "TerminalWidth" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 Int))) |
Progress
An amount of progress.
Progress | |
|
Labels
A label that can be pre- or postfixed to a progress bar.
Timing information related to a ProgressBar
.
This information is used by Label
s to calculate elapsed time, remaining time, total time, etc.
Timing | |
|
percentage :: Label s Source #
A label which displays the progress as a percentage.
>>>
runLabel $ percentage (Progress 30 100 ()) someTiming
" 30%"
Note: if no work is to be done (todo == 0) the percentage will always be 100%.
A label which displays the progress as a fraction of the total amount of work.
Equal width property - the length of the resulting label is a function of the total amount of work:
>>>
runLabel $ exact (Progress 30 100 ()) someTiming
" 30/100"
elapsedTime :: (NominalDiffTime -> Text) -> Label s Source #
A label which displays the amount of time that has elapsed.
Time starts when a progress bar is created.
The user must supply a function which actually renders the amount
of time that has elapsed. You can use renderDuration
or
formatTime
from time >= 1.9.
:: (NominalDiffTime -> Text) | |
-> Text | Alternative message when remaining time can't be calculated (yet). |
-> Label s |
Displays the estimated remaining time until all work is done.
Tells you how much longer some task will take.
This label uses a really simple estimation algorithm. It assumes progress is linear. To prevent nonsense results it won't estimate remaining time until at least 1 second of work has been done.
When it refuses to estimate the remaining time it will show an alternative message instead.
The user must supply a function which actually renders the amount
of time that has elapsed. You can use renderDuration
or
formatTime
from time >= 1.9.
:: (NominalDiffTime -> Text) | |
-> Text | Alternative message when total time can't be calculated (yet). |
-> Label s |
Displays the estimated total time a task will take.
This label uses a really simple estimation algorithm. It assumes progress is linear. To prevent nonsense results it won't estimate the total time until at least 1 second of work has been done.
When it refuses to estimate the total time it will show an alternative message instead.
The user must supply a function which actually renders the total
amount of time that a task will take. You can use renderDuration
or formatTime
from time >= 1.9.
renderDuration :: NominalDiffTime -> Text Source #
Show amount of time.
renderDuration (fromInteger 42)
42
renderDuration (fromInteger $ 5 * 60 + 42)
05:42
renderDuration (fromInteger $ 8 * 60 * 60 + 5 * 60 + 42)
08:05:42
Use the time >= 1.9 package to get a formatTime function which
accepts NominalDiffTime
.