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
|
# Progress Bar
If you are executing an operation that can take some time, you can inform it to the user. 🤓
## Progress Bar
You can use <a href="https://rich.readthedocs.io/en/stable/progress.html" class="external-link" target="_blank">Rich's Progress Display</a> to show a progress bar, for example:
{* docs_src/progressbar/tutorial001.py hl[4,9] *}
You put the thing that you want to iterate over inside of Rich's `track()`, and then iterate over that.
Check it:
<div class="termy">
```console
$ python main.py
---> 100%
Processed 100 things.
```
</div>
...actually, it will look a lot prettier. ✨ But I can't show you the animation here in the docs. 😅
The colors and information will look something like this:
<div class="termy">
```console
$ python main.py
Processing... <font color="#F92672">━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸</font><font color="#3A3A3A">━━━━━━━━━━</font> <font color="#AE81FF"> 74%</font> <font color="#A1EFE4">0:00:01</font>
```
</div>
## Spinner
When you don't know how long the operation will take, you can use a spinner instead.
Rich allows you to display many things in complex and advanced ways.
For example, this will show two spinners:
{* docs_src/progressbar/tutorial002.py hl[4,8:15] *}
I can't show you the beautiful animation here in the docs. 😅
But at some point in time it will look like this (imagine it's spinning). 🤓
<div class="termy">
```console
$ python main.py
<font color="#A6E22E">⠹</font> Processing...
<font color="#A6E22E">⠹</font> Preparing...
```
</div>
You can learn more about it in the <a href="https://rich.readthedocs.io/en/stable/progress.html" class="external-link" target="_blank">Rich docs for Progress Display</a>.
## Typer `progressbar`
If you can, you should use **Rich** as explained above, it has more features, it's more advanced, and can display information more beautifully. ✨
/// tip
If you can use Rich, use the information above, the Rich docs, and skip the rest of this page. 😎
///
But if you can't use Rich, Typer (actually Click) comes with a simple utility to show progress bars.
/// info
`typer.progressbar()` comes directly from Click, you can read more about it in <a href="https://click.palletsprojects.com/en/8.1.x/utils/#showing-progress-bars" class="external-link" target="_blank">Click's docs</a>.
///
### Use `typer.progressbar`
/// tip
Remember, you are much better off using <a href="https://rich.readthedocs.io/" class="external-link" target="_blank">Rich</a> for this. 😎
///
You can use `typer.progressbar()` with a `with` statement, as in:
```Python
with typer.progressbar(something) as progress:
pass
```
And you pass as function argument to `typer.progressbar()` the thing that you would normally iterate over.
{* docs_src/progressbar/tutorial003.py hl[8] *}
So, if you have a list of users, this could be:
```Python
users = ["Camila", "Rick", "Morty"]
with typer.progressbar(users) as progress:
pass
```
And the `with` statement using `typer.progressbar()` gives you an object that you can iterate over, just like if it was the same thing that you would iterate over normally.
But by iterating over this object **Typer** (actually Click) will know to update the progress bar:
```Python
users = ["Camila", "Rick", "Morty"]
with typer.progressbar(users) as progress:
for user in progress:
typer.echo(user)
```
/// tip
Notice that there are 2 levels of code blocks. One for the `with` statement and one for the `for` statement.
///
/// info
This is mostly useful for operations that take some time.
In the example above we are faking it with `time.sleep()`.
///
Check it:
<div class="termy">
```console
$ python main.py
---> 100%
Processed 100 things.
```
</div>
### Setting a Progress Bar `length`
/// tip
Remember, you are much better off using <a href="https://rich.readthedocs.io/" class="external-link" target="_blank">Rich</a> for this. 😎
///
The progress bar is generated from the length of the iterable (e.g. the list of users).
But if the length is not available (for example, with something that fetches a new user from a web API each time) you can pass an explicit `length` to `typer.progressbar()`.
{* docs_src/progressbar/tutorial004.py hl[14] *}
Check it:
<div class="termy">
```console
$ python main.py
---> 100%
Processed 100 user IDs.
```
</div>
#### About the function with `yield`
If you hadn't seen something like that `yield` above, that's a "<a href="https://docs.python.org/3/glossary.html#term-generator" class="external-link" target="_blank">generator</a>".
You can iterate over that function with a `for` and at each iteration it will give you the value at `yield`.
`yield` is like a `return` that gives values multiple times and let's you use the function in a `for` loop.
For example:
```Python
def iterate_user_ids():
# Let's imagine this is a web API, not a range()
for i in range(100):
yield i
for i in iterate_user_ids():
print(i)
```
would print each of the "user IDs" (here it's just the numbers from `0` to `99`).
### Add a `label`
/// tip
Remember, you are much better off using <a href="https://rich.readthedocs.io/" class="external-link" target="_blank">Rich</a> for this. 😎
///
You can also set a `label`:
{* docs_src/progressbar/tutorial005.py hl[8] *}
Check it:
<div class="use-termynal">
<span data-ty="input">python main.py</span>
<span data-ty="progress" data-ty-prompt="Processing"></span>
<span data-ty>Processed 100 things.</span>
</div>
## Iterate manually
If you need to manually iterate over something and update the progress bar irregularly, you can do it by not passing an iterable but just a `length` to `typer.progressbar()`.
And then calling the `.update()` method in the object from the `with` statement:
{* docs_src/progressbar/tutorial006.py hl[8,12] *}
Check it:
<div class="use-termynal">
<span data-ty="input">python main.py</span>
<span data-ty="progress" data-ty-prompt="Batches"></span>
<span data-ty>Processed 1000 things in batches.</span>
</div>
|