summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonathan Slenders <jonathan@slenders.be>2018-05-20 18:27:29 +0200
committerJonathan Slenders <jonathan@slenders.be>2018-05-20 18:27:29 +0200
commit7abd8acecb3e5f2e3100098c337b98f82f09c41f (patch)
tree4da869045d54b01cecb9f0282c3338b793935d94
parent2936281e191576c8d665fcf25a06324f2d4e6f60 (diff)
Added documentation about progress bars.
-rwxr-xr-xdocs/images/progress-bars/apt-get.pngbin0 -> 5411 bytes
-rwxr-xr-xdocs/images/progress-bars/colored-title-and-label.pngbin0 -> 8575 bytes
-rwxr-xr-xdocs/images/progress-bars/custom-key-bindings.pngbin0 -> 8191 bytes
-rwxr-xr-xdocs/images/progress-bars/simple-progress-bar.pngbin0 -> 13218 bytes
-rwxr-xr-xdocs/images/progress-bars/two-tasks.pngbin0 -> 9723 bytes
-rw-r--r--docs/index.rst1
-rw-r--r--docs/pages/progress_bars.rst246
-rwxr-xr-xexamples/progress-bar/colored-title-and-label.py22
-rwxr-xr-xexamples/progress-bar/custom-key-bindings.py12
-rwxr-xr-xexamples/progress-bar/styled-apt-get-install.py4
-rwxr-xr-xexamples/progress-bar/two-tasks.py39
11 files changed, 322 insertions, 2 deletions
diff --git a/docs/images/progress-bars/apt-get.png b/docs/images/progress-bars/apt-get.png
new file mode 100755
index 00000000..ce624649
--- /dev/null
+++ b/docs/images/progress-bars/apt-get.png
Binary files differ
diff --git a/docs/images/progress-bars/colored-title-and-label.png b/docs/images/progress-bars/colored-title-and-label.png
new file mode 100755
index 00000000..ace43936
--- /dev/null
+++ b/docs/images/progress-bars/colored-title-and-label.png
Binary files differ
diff --git a/docs/images/progress-bars/custom-key-bindings.png b/docs/images/progress-bars/custom-key-bindings.png
new file mode 100755
index 00000000..5f3610c4
--- /dev/null
+++ b/docs/images/progress-bars/custom-key-bindings.png
Binary files differ
diff --git a/docs/images/progress-bars/simple-progress-bar.png b/docs/images/progress-bars/simple-progress-bar.png
new file mode 100755
index 00000000..6ea3eac5
--- /dev/null
+++ b/docs/images/progress-bars/simple-progress-bar.png
Binary files differ
diff --git a/docs/images/progress-bars/two-tasks.png b/docs/images/progress-bars/two-tasks.png
new file mode 100755
index 00000000..0bb3f751
--- /dev/null
+++ b/docs/images/progress-bars/two-tasks.png
Binary files differ
diff --git a/docs/index.rst b/docs/index.rst
index a38870a2..ffcbe1db 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -76,6 +76,7 @@ Table of contents
pages/printing_text
pages/asking_for_input
pages/dialogs
+ pages/progress_bars
pages/full_screen_apps
pages/tutorials/index
pages/advanced_topics/index
diff --git a/docs/pages/progress_bars.rst b/docs/pages/progress_bars.rst
new file mode 100644
index 00000000..2a2c958d
--- /dev/null
+++ b/docs/pages/progress_bars.rst
@@ -0,0 +1,246 @@
+.. _progress_bars:
+
+Progress bars
+=============
+
+Prompt_toolkit ships with a high level API for displaying progress bars,
+inspired by `tqdm <https://github.com/tqdm/tqdm>`_
+
+.. warning::
+
+ The API for the prompt_toolkit progress bars is still very new and can
+ possibly change in the future. It is usable and tested, but keep this in
+ mind when upgrading.
+
+Remember that the examples directory of the prompt_toolkit repository ships
+with many progress bar examples as well.
+
+
+Simple progress bar
+-------------------
+
+Creating a new progress bar can be done by calling the
+:class:`~prompt_toolkit.shortcuts.ProgressBar` context manager.
+
+The progress can be displayed for any iterable. This works by wrapping the
+iterable (like ``range``) with the
+:class:`~prompt_toolkit.shortcuts.ProgressBar` context manager itself. This
+way, the progress bar knows when the next item is consumed by the forloop and
+when progress happens.
+
+.. code:: python
+
+ from prompt_toolkit.shortcuts import ProgressBar
+ import time
+
+
+ with ProgressBar() as pb:
+ for i in pb(range(800)):
+ time.sleep(.01)
+
+.. image:: ../images/progress-bars/simple-progress-bar.png
+
+Keep in mind that not all iterables can report their total length. This happens
+with a typical generator. In that case, you can still pass the total as follows
+in order to make displaying the progress possible:
+
+.. code:: python
+
+ def some_iterable():
+ yield ...
+
+ with ProgressBar() as pb:
+ for i in pb(some_iterable, total=1000):
+ time.sleep(.01)
+
+
+Multiple parallal tasks
+-----------------------
+
+A prompt_toolkit :class:`~prompt_toolkit.shortcuts.ProgressBar` can display the
+progress of multiple tasks running in parallel. Each task can run in a separate
+thread and the :class:`~prompt_toolkit.shortcuts.ProgressBar` user interface
+runs in its own thread.
+
+Notice that we set the "daemon" flag for both threads that run the tasks. This
+is because control-c will stop the progress and quit our application. We don't
+want the application to wait for the background threads to finish. Whether you
+want this depends on the application.
+
+.. code:: python
+
+ from prompt_toolkit.shortcuts import ProgressBar
+ import time
+ import threading
+
+
+ with ProgressBar() as pb:
+ # Two parallal tasks.
+ def task_1():
+ for i in pb(range(100)):
+ time.sleep(.05)
+
+ def task_2():
+ for i in pb(range(150)):
+ time.sleep(.08)
+
+ # Start threads.
+ t1 = threading.Thread(target=task_1)
+ t2 = threading.Thread(target=task_2)
+ t1.daemon = True
+ t2.daemon = True
+ t1.start()
+ t2.start()
+
+ # Wait for the threads to finish. We use a timeout for the join() call,
+ # because on Windows, join cannot be interrupted by Control-C or any other
+ # signal.
+ for t in [t1, t2]:
+ while t.is_alive():
+ t.join(timeout=.5)
+
+.. image:: ../images/progress-bars/two-tasks.png
+
+
+Adding a title and label
+------------------------
+
+Each progress bar can have one title, and for each task an individual label.
+Both the title and the labels can be :ref:`formatted text <formatted_text>`.
+
+.. code:: python
+
+ from prompt_toolkit.shortcuts import ProgressBar
+ from prompt_toolkit.formatted_text import HTML
+ import time
+
+
+ def main():
+ title = HTML('Downloading <style bg="yellow" fg="black">4 files...</style>')
+ label = HTML('<ansired>some file</ansired>: ')
+
+ with ProgressBar(title=title) as pb:
+ for i in pb(range(800), label=label):
+ time.sleep(.01)
+
+.. image:: ../images/progress-bars/colored-title-and-label.png
+
+
+Formatting the progress bar
+---------------------------
+
+The visualisation of a :class:`~prompt_toolkit.shortcuts.ProgressBar` can be
+customized by using a different sequence of formatters. The default formatting
+looks something like this:
+
+.. code:: python
+
+ from prompt_toolkit.shortcuts.progress_bar.formatters import *
+
+ default_formatting = [
+ Label(),
+ Text(' '),
+ Percentage(),
+ Text(' '),
+ Bar(),
+ Text(' '),
+ Progress(),
+ Text(' '),
+ Text('eta [', style='class:time-left'),
+ TimeLeft(),
+ Text(']', style='class:time-left'),
+ Text(' '),
+ ]
+
+That sequence of
+:class:`~prompt_toolkit.shortcuts.progress_bar.formatters.Formatter` can be
+passed to the `formatter` argument of
+:class:`~prompt_toolkit.shortcuts.ProgressBar`. So, we could change this and
+modify the progress bar to look like an apt-get style progress bar:
+
+.. code:: python
+
+ from prompt_toolkit.shortcuts import ProgressBar
+ from prompt_toolkit.styles import Style
+ from prompt_toolkit.shortcuts.progress_bar import formatters
+ import time
+
+ style = Style.from_dict({
+ 'label': 'bg:#ffff00 #000000',
+ 'percentage': 'bg:#ffff00 #000000',
+ 'current': '#448844',
+ 'bar': '',
+ })
+
+
+ custom_formatters = [
+ formatters.Label(),
+ formatters.Text(': [', style='class:percentage'),
+ formatters.Percentage(),
+ formatters.Text(']', style='class:percentage'),
+ formatters.Text(' '),
+ formatters.Bar(sym_a='#', sym_b='#', sym_c='.'),
+ formatters.Text(' '),
+ ]
+
+ with ProgressBar(style=style, formatters=custom_formatters) as pb:
+ for i in pb(range(1600), label='Installing'):
+ time.sleep(.01)
+
+.. image:: ../images/progress-bars/apt-get.png
+
+
+Adding key bindings and toolbar
+-------------------------------
+
+Like other prompt_toolkit applications, we can add custom key bindings, by
+passing a :class:`~prompt_toolkit.key_binding.KeyBindings` object:
+
+.. code:: python
+
+ from prompt_toolkit import HTML
+ from prompt_toolkit.key_binding import KeyBindings
+ from prompt_toolkit.patch_stdout import patch_stdout
+ from prompt_toolkit.shortcuts import ProgressBar
+
+ import time
+
+ bottom_toolbar = HTML(' <b>[f]</b> Print "f" <b>[x]</b> Abort.')
+
+ # Create custom key bindings first.
+ kb = KeyBindings()
+ cancel = [False]
+
+ @kb.add('f')
+ def _(event):
+ print('You pressed `f`.')
+
+ @kb.add('x')
+ def _(event):
+ " Send Abort (control-c) signal. "
+ cancel[0] = True
+ os.kill(os.getpid(), signal.SIGINT)
+
+ # Use `patch_stdout`, to make sure that prints go above the
+ # application.
+ with patch_stdout():
+ with ProgressBar(key_bindings=kb, bottom_toolbar=bottom_toolbar) as pb:
+ for i in pb(range(800)):
+ time.sleep(.01)
+
+ # Stop when the cancel flag has been set.
+ if cancel[0]:
+ break
+
+Notice that we use :func:`~prompt_toolkit.patch_stdout.patch_stdout` to make
+printing text possible while the progress bar is displayed. This ensures that
+printing happens above the progress bar.
+
+Further, when "x" is pressed, we set a cancel flag, which stops the progress.
+It would also be possible to send `SIGINT` to the mean thread, but that's not
+always considered a clean way of cancelling something.
+
+In the example above, we also display a toolbar at the bottom which shows the
+key bindings.
+
+.. image:: ../images/progress-bars/custom-key-bindings.png
diff --git a/examples/progress-bar/colored-title-and-label.py b/examples/progress-bar/colored-title-and-label.py
new file mode 100755
index 00000000..f7b7cb52
--- /dev/null
+++ b/examples/progress-bar/colored-title-and-label.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""
+A progress bar that displays a formatted title above the progress bar and has a
+colored label.
+"""
+from __future__ import unicode_literals
+from prompt_toolkit.shortcuts import ProgressBar
+from prompt_toolkit.formatted_text import HTML
+import time
+
+
+def main():
+ title = HTML('Downloading <style bg="yellow" fg="black">4 files...</style>')
+ label = HTML('<ansired>some file</ansired>: ')
+
+ with ProgressBar(title=title) as pb:
+ for i in pb(range(800), label=label):
+ time.sleep(.01)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/progress-bar/custom-key-bindings.py b/examples/progress-bar/custom-key-bindings.py
index cfedcdae..f22d5425 100755
--- a/examples/progress-bar/custom-key-bindings.py
+++ b/examples/progress-bar/custom-key-bindings.py
@@ -15,17 +15,24 @@ import signal
def main():
- bottom_toolbar = HTML(' <b>[f]</b> Print "f" <b>[x]</b> Abort.')
+ bottom_toolbar = HTML(' <b>[f]</b> Print "f" <b>[q]</b> Abort <b>[x]</b> Send Control-C.')
# Create custom key bindings first.
kb = KeyBindings()
+ cancel = [False]
@kb.add('f')
def _(event):
print('You pressed `f`.')
+ @kb.add('q')
+ def _(event):
+ " Quit by setting cancel flag. "
+ cancel[0] = True
+
@kb.add('x')
def _(event):
+ " Quit by sending SIGINT to the main thread. "
os.kill(os.getpid(), signal.SIGINT)
# Use `patch_stdout`, to make sure that prints go above the
@@ -35,6 +42,9 @@ def main():
for i in pb(range(800)):
time.sleep(.01)
+ if cancel[0]:
+ break
+
if __name__ == '__main__':
main()
diff --git a/examples/progress-bar/styled-apt-get-install.py b/examples/progress-bar/styled-apt-get-install.py
index e1591fe3..ea880617 100755
--- a/examples/progress-bar/styled-apt-get-install.py
+++ b/examples/progress-bar/styled-apt-get-install.py
@@ -9,6 +9,7 @@ from prompt_toolkit.shortcuts.progress_bar import formatters
import time
style = Style.from_dict({
+ 'label': 'bg:#ffff00 #000000',
'percentage': 'bg:#ffff00 #000000',
'current': '#448844',
'bar': '',
@@ -17,7 +18,8 @@ style = Style.from_dict({
def main():
custom_formatters = [
- formatters.Text('Progress: [', style='class:percentage'),
+ formatters.Label(),
+ formatters.Text(': [', style='class:percentage'),
formatters.Percentage(),
formatters.Text(']', style='class:percentage'),
formatters.Text(' '),
diff --git a/examples/progress-bar/two-tasks.py b/examples/progress-bar/two-tasks.py
new file mode 100755
index 00000000..01476d37
--- /dev/null
+++ b/examples/progress-bar/two-tasks.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+"""
+Two progress bars that run in parallel.
+"""
+from __future__ import unicode_literals
+from prompt_toolkit.shortcuts import ProgressBar
+import time
+import threading
+
+
+def main():
+ with ProgressBar() as pb:
+ # Two parallal tasks.
+ def task_1():
+ for i in pb(range(100)):
+ time.sleep(.05)
+
+ def task_2():
+ for i in pb(range(150)):
+ time.sleep(.08)
+
+ # Start threads.
+ t1 = threading.Thread(target=task_1)
+ t2 = threading.Thread(target=task_2)
+ t1.daemon = True
+ t2.daemon = True
+ t1.start()
+ t2.start()
+
+ # Wait for the threads to finish. We use a timeout for the join() call,
+ # because on Windows, join cannot be interrupted by Control-C or any other
+ # signal.
+ for t in [t1, t2]:
+ while t.is_alive():
+ t.join(timeout=.5)
+
+
+if __name__ == '__main__':
+ main()