Python untuk mencetak bilah status dan persentase

160

Untuk menerapkan bilah status seperti di bawah ini:

[==========                ]  45%
[================          ]  60%
[==========================] 100%

Saya ingin ini dicetak ke stdout, dan terus menyegarkan, bukan mencetak ke baris lain. Bagaimana cara melakukannya?

Stan
sumber
Saya telah menerbitkan jenis progress bar baru, yang dapat Anda cetak, lihat throughput dan eta, bahkan menghentikannya, di samping animasi yang sangat keren! Silakan lihat: github.com/rsalmei/alive-progress ! hidup-kemajuan
rsalmei

Jawaban:

146

Ada modul Python yang bisa Anda dapatkan dari PyPI yang disebut progressbaryang mengimplementasikan fungsi tersebut. Jika Anda tidak keberatan menambahkan ketergantungan, itu adalah solusi yang bagus. Kalau tidak, pergilah dengan salah satu jawaban lain.

Contoh sederhana cara menggunakannya:

import progressbar
from time import sleep
bar = progressbar.ProgressBar(maxval=20, \
    widgets=[progressbar.Bar('=', '[', ']'), ' ', progressbar.Percentage()])
bar.start()
for i in xrange(20):
    bar.update(i+1)
    sleep(0.1)
bar.finish()

Untuk menginstalnya, Anda dapat menggunakan easy_install progressbar, atau pip install progressbarjika Anda lebih suka pip.

icktoofay
sumber
Semua jawaban itu mengagumkan, namun, saya paling suka modul. Terima kasih semuanya.
Stan
8
Mungkin contoh ini membutuhkan sesuatu seperti bar.start()?
Jim Raynor
2
Tidak berfungsi pada mesin saya — perlu ditambahkanbar.start()
Zach
1
Modul ini belum diperbarui dalam lebih dari 2 tahun. Jangan menggunakannya untuk perangkat lunak baru!
Navin
4
Instalasi: sudo -H pip install progressbar2.
Martin Thoma
254

The '\r'karakter (carriage return) me-reset kursor ke awal baris dan memungkinkan Anda untuk menulis lebih apa yang sebelumnya di telepon.

from time import sleep
import sys

for i in range(21):
    sys.stdout.write('\r')
    # the exact output you're looking for:
    sys.stdout.write("[%-20s] %d%%" % ('='*i, 5*i))
    sys.stdout.flush()
    sleep(0.25)

Saya tidak 100% yakin apakah ini sepenuhnya portabel di semua sistem, tetapi paling tidak bekerja di Linux dan OSX.

Mark Rushakoff
sumber
7
Ini lebih baik daripada jawaban yang ditandai, karena ia bekerja dengan Python 3 juga.
Gerhard Hagerer
tahu mengapa saya mendapatkan karakter aneh di akhir baris kadang-kadang, bahkan jika saya null-mengakhiri string yang sedang ditulis?
reservoirman
23
Seperti yang tertulis, kode tidak menjelaskan bagaimana mengadaptasi ini dengan ukuran apa pun yang Anda iterasi (bukan 21). Aku akan membiarkan n=21, ganti range(21)dengan range(n), kemudian tambahkan j = (i + 1) / ndalam loop, dan mengganti writepernyataan dengan sedikit modifikasi ini: sys.stdout.write("[%-20s] %d%%" % ('='*int(20*j), 100*j)). Sekarang satu-satunya perubahan yang perlu Anda lakukan adalah n=21sebelum loop (lebih mungkin n=len(iterable)), kemudian menghitung lebih dari objek iterable. Saya merekomendasikan suntingan ini tetapi ditolak; rupanya fungsionalitas "menyimpang dari maksud asli kiriman".
Steven C. Howell
1
Adaptif dengan ukuran acak n,, sys.stdout.write("[{:{}}] {:.1f}%".format("="*i, n-1, (100/(n-1)*i)))hanya Python 3
GabrielChu
3
@ StevenC.Namun mengapa Anda tidak mempostingnya sebagai jawaban mengutip Mark? ini versi lain dan sangat membantu untuk menyiapkannya
GM
91

Saya menemukan tqdm perpustakaan yang bermanfaat ( https://github.com/tqdm/tqdm/ , sebelumnya: https://github.com/noamraph/tqdm ). Secara otomatis memperkirakan waktu penyelesaian dan dapat digunakan sebagai iterator.

Pemakaian:

import tqdm
import time

for i in tqdm.tqdm(range(1000)):
    time.sleep(0.01)
    # or other long operations

Hasil dalam:

|####------| 450/1000  45% [elapsed: 00:04 left: 00:05, 99.15 iters/sec]

tqdm dapat membungkus iterable.

omikron
sumber
4
Proyek tqdm sekarang dikelola di sini oleh tim pengembang baru.
Gaborous
1
waw, tqdm ini luar biasa, dan sangat mudah digunakan :).
Sidahmed
6
ini adalah solusi yang paling mudah
kd88
2
itu dibundel dengan distrib Anaconda juga! (Adapun python 3.5 dan di atas setidaknya)
Jacquot
Terima kasih, luar biasa, sederhana dan efektif
DavideL
23

Anda dapat menggunakan \r( carriage return ). Demo:

import sys
total = 10000000
point = total / 100
increment = total / 20
for i in xrange(total):
    if(i % (5 * point) == 0):
        sys.stdout.write("\r[" + "=" * (i / increment) +  " " * ((total - i)/ increment) + "]" +  str(i / point) + "%")
        sys.stdout.flush()
Matthew Flaschen
sumber
Coba totalsebagai 10, maka Anda mendapatkan pesan kesalahanZeroDivisionError: long division or modulo by zero
unseen_rider
19

Di sini Anda dapat menggunakan kode berikut sebagai fungsi:

def drawProgressBar(percent, barLen = 20):
    sys.stdout.write("\r")
    progress = ""
    for i in range(barLen):
        if i < int(barLen * percent):
            progress += "="
        else:
            progress += " "
    sys.stdout.write("[ %s ] %.2f%%" % (progress, percent * 100))
    sys.stdout.flush()

Dengan menggunakan .format:

def drawProgressBar(percent, barLen = 20):
    # percent float from 0 to 1. 
    sys.stdout.write("\r")
    sys.stdout.write("[{:<{}}] {:.0f}%".format("=" * int(barLen * percent), barLen, percent * 100))
    sys.stdout.flush()
Jacob CUI
sumber
Bagi saya, baris pertama tidak diubah oleh kursor - hanya baris kedua yang diubah. Oleh karena itu beberapa baris diperoleh untuk bilah kemajuan, dan satu untuk misalnya] percent * 100 %
unseen_rider
6

berdasarkan jawaban di atas dan pertanyaan serupa lainnya tentang bilah kemajuan CLI, saya pikir saya mendapat jawaban umum yang umum untuk semuanya. Periksa di https://stackoverflow.com/a/15860757/2254146

Berikut adalah salinan fungsi, tetapi dimodifikasi agar sesuai dengan gaya Anda:

import time, sys

# update_progress() : Displays or updates a console progress bar
## Accepts a float between 0 and 1. Any int will be converted to a float.
## A value under 0 represents a 'halt'.
## A value at 1 or bigger represents 100%
def update_progress(progress):
    barLength = 20 # Modify this to change the length of the progress bar
    status = ""
    if isinstance(progress, int):
        progress = float(progress)
    if not isinstance(progress, float):
        progress = 0
        status = "error: progress var must be float\r\n"
    if progress < 0:
        progress = 0
        status = "Halt...\r\n"
    if progress >= 1:
        progress = 1
        status = "Done...\r\n"
    block = int(round(barLength*progress))
    text = "\rPercent: [{0}] {1}% {2}".format( "="*block + " "*(barLength-block), progress*100, status)
    sys.stdout.write(text)
    sys.stdout.flush()

Seperti

Persen: [====================] 99,0%

Brian Khuu
sumber
Bilah progres tidak muncul untuk saya
unseen_rider
Saya pikir itu tidak membutuhkan banyak tempat desimal yaitu putaran (kemajuan * 100,2)
Andrés Sánchez
4

Jika Anda mengembangkan antarmuka baris perintah, saya sarankan Anda untuk melihat clickyang sangat bagus:

import click
import time

for filename in range(3):
    with click.progressbar(range(100), fill_char='=', empty_char=' ') as bar:
        for user in bar:
            time.sleep(0.01)

Di sini output yang Anda dapatkan:

$ python test.py
  [====================================]  100%
  [====================================]  100%
  [=========                           ]   27%
sekarang
sumber
4

Peningkatan lebih lanjut, menggunakan fungsi sebagai:

import sys

def printProgressBar(i,max,postText):
    n_bar =10 #size of progress bar
    j= i/max
    sys.stdout.write('\r')
    sys.stdout.write(f"[{'=' * int(n_bar * j):{n_bar}s}] {int(100 * j)}%  {postText}")
    sys.stdout.flush()

contoh panggilan:

total=33
for i in range(total):
    printProgressBar(i,total,"blah")
    sleep(0.05)  

keluaran:

[================================================  ] 96%  blah  
STG
sumber
sempurna! dan tidak ada modul yang diperlukan
yurividal
3

Saya menemukan utas ini hari ini dan setelah mencoba solusi ini dari Mark Rushakoff

from time import sleep
import sys

for i in range(21):
sys.stdout.write('\r')
# the exact output you're looking for:
sys.stdout.write("[%-20s] %d%%" % ('='*i, 5*i))
sys.stdout.flush()
sleep(0.25)

Saya dapat mengatakan bahwa ini berfungsi dengan baik pada W7-64 dengan python 3.4.3 64-bit tetapi hanya di konsol asli. Namun ketika menggunakan konsol built-in spyder 3.0.0dev, jeda baris masih ada / lagi. Karena ini perlu waktu bagi saya untuk mencari tahu, saya ingin melaporkan pengamatan ini di sini.

Mr.Bum
sumber
2

Membangun beberapa jawaban di sini dan di tempat lain, saya telah menulis fungsi sederhana ini yang menampilkan bilah kemajuan dan sisa waktu yang diperkirakan. Harus bekerja pada sebagian besar mesin berbasis unix.

import time
import sys

percent = 50.0
start = time.time()
draw_progress_bar(percent, start)


def draw_progress_bar(percent, start, barLen=20):
sys.stdout.write("\r")
progress = ""
for i in range(barLen):
    if i < int(barLen * percent):
        progress += "="
    else:
        progress += " "

elapsedTime = time.time() - start;
estimatedRemaining = int(elapsedTime * (1.0/percent) - elapsedTime)

if (percent == 1.0):
    sys.stdout.write("[ %s ] %.1f%% Elapsed: %im %02is ETA: Done!\n" % 
        (progress, percent * 100, int(elapsedTime)/60, int(elapsedTime)%60))
    sys.stdout.flush()
    return
else:
    sys.stdout.write("[ %s ] %.1f%% Elapsed: %im %02is ETA: %im%02is " % 
        (progress, percent * 100, int(elapsedTime)/60, int(elapsedTime)%60,
         estimatedRemaining/60, estimatedRemaining%60))
    sys.stdout.flush()
    return
Sam Jordan
sumber
2

Ini cukup pendekatan sederhana yang dapat digunakan dengan loop apa pun.

#!/usr/bin/python
for i in range(100001):
    s =  ((i/5000)*'#')+str(i)+(' %')
    print ('\r'+s),
NILESH KUMAR
sumber
Apa yang dilakukan '#'?
unseen_rider
@unseen_rider Here '#' hanyalah simbol yang akan diulang seiring iterasi loop meningkat.
NILESH KUMAR
2

Paling mudah masih

import sys
total_records = 1000
for i in range (total_records):
    sys.stdout.write('\rUpdated record: ' + str(i) + ' of ' + str(total_records))
    sys.stdout.flush()

Kuncinya adalah untuk mengkonversi tipe integer ke string.

bfree67
sumber
2

Seperti dijelaskan dalam solusi Mark Rushakoff , Anda dapat menampilkan karakter carriage return sys.stdout.write('\r'),, untuk mengatur ulang kursor ke awal baris. Untuk menggeneralisasi solusi itu, sambil juga mengimplementasikan f-Strings Python 3 , Anda bisa menggunakan

from time import sleep
import sys

n_bar = 50
iterable = range(33)  # for demo purposes
n_iter = len(iterable)
for i, item in enumerate(iterable):
    j = (i + 1) / n_iter

    sys.stdout.write('\r')
    sys.stdout.write(f"[{'=' * int(n_bar * j):{n_bar}s}] {int(100 * j)}%")
    sys.stdout.flush()

    sleep(0.05)  
    # do something with <item> here
Steven C. Howell
sumber
1

Coba PyProg. PyProg adalah pustaka sumber terbuka untuk Python untuk membuat indikator & bilah kemajuan yang dapat disesuaikan.

Saat ini di versi 1.0.2; di-host di Github dan tersedia di PyPI (Tautan di bawah). Ini kompatibel dengan Python 3 & 2 dan juga dapat digunakan dengan Qt Console.

Ini sangat mudah digunakan. Kode berikut:

import pyprog
from time import sleep

# Create Object
prog = pyprog.ProgressBar(" ", " ", total=34, bar_length=26, complete_symbol="=", not_complete_symbol=" ", wrap_bar_prefix=" [", wrap_bar_suffix="] ", progress_explain="", progress_loc=pyprog.ProgressBar.PROGRESS_LOC_END)
# Update Progress Bar
prog.update()

for i in range(34):
    # Do something
    sleep(0.1)
    # Set current status
    prog.set_stat(i + 1)
    # Update Progress Bar again
    prog.update()

# Make the Progress Bar final
prog.end()

akan menghasilkan apa yang Anda inginkan (bahkan panjang bilah!):

[===========               ] 45%
[===============           ] 60%
[==========================] 100%

Untuk opsi lainnya untuk menyesuaikan bilah kemajuan, buka halaman Github dari situs web ini.

Saya benar-benar membuat PyProg karena saya membutuhkan pustaka kemajuan sederhana namun super dapat dikustomisasi. Anda dapat dengan mudah menginstalnya dengan: pip install pyprog.

PyProg Github: https://github.com/Bill13579/pyprog
PyPI: https://pypi.python.org/pypi/pyprog/

Bill Kudo
sumber
1

Menggunakan jawaban @ Mark-Rushakoff, saya berhasil menggunakan pendekatan yang lebih sederhana, tidak perlu memanggil perpustakaan sys. Ini bekerja dengan Python 3. Diuji di Windows:

from time import sleep
for i in range(21):
    # the exact output you're looking for:
    print ("\r[%-20s] %d%%" % ('='*i, 5*i), end='')
    sleep(0.25)
Gelas bir
sumber
Seperti dijelaskan dalam jawaban untuk pertanyaan yang berbeda print adalah pembungkus tipis yang memformat input dan memanggil fungsi tulis dari objek yang diberikan. Secara default objek ini adalah sys.stdout.
Steven C. Howell
0

Ini adalah sesuatu yang saya buat menggunakan solusi oleh @ Mark-Rushakoff. Untuk menyesuaikan dengan lebar terminal secara adaptif.

from time import sleep
import os
import sys
from math import ceil

l = list(map(int,os.popen('stty size','r').read().split()))
col = l[1]
col = col - 6

for i in range(col):
    sys.stdout.write('\r')
    getStr = "[%s " % ('='*i)
    sys.stdout.write(getStr.ljust(col)+"]"+"%d%%" % (ceil((100/col)*i)))
    sys.stdout.flush()
    sleep(0.25)
print("")
swarnava112
sumber