Python để in thanh trạng thái và tỷ lệ phần trăm


160

Để thực hiện một thanh trạng thái như dưới đây:

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

Tôi muốn nó được in ra stdout, và tiếp tục làm mới nó, không in sang dòng khác. làm như thế nào?


Tôi đã xuất bản một loại thanh tiến trình mới, mà bạn có thể in, xem thông lượng và eta, thậm chí tạm dừng nó, bên cạnh các hình ảnh động rất tuyệt! Xin hãy xem: github.com/rsalmei/alive-proceed ! sống tiến bộ
rsalmei

Câu trả lời:


146

Có một mô-đun Python mà bạn có thể nhận được từ PyPI được gọi là progressbarthực hiện chức năng đó. Nếu bạn không ngại thêm một phụ thuộc, đó là một giải pháp tốt. Nếu không, đi với một trong những câu trả lời khác.

Một ví dụ đơn giản về cách sử dụng nó:

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()

Để cài đặt nó, bạn có thể sử dụng easy_install progressbar, hoặc pip install progressbarnếu bạn thích pip.


Tất cả các câu trả lời là tuyệt vời, tuy nhiên, tôi thích mô-đun nhất. Cảm ơn mọi người.
Stan

8
Có lẽ ví dụ này cần một cái gì đó như thế bar.start()nào?
Jim Raynor

2
Không hoạt động trên máy của tôi, tôi cần thêmbar.start()
Zach

1
Mô-đun này đã không được cập nhật trong hơn 2 năm. Đừng sử dụng nó cho phần mềm mới!
Navin

4
Cài đặt : sudo -H pip install progressbar2.
Martin Thoma

254

'\r'tự (trở về vận chuyển) đặt lại con trỏ về đầu dòng và cho phép bạn viết lên những gì trước đó trên dòng.

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)

Tôi không chắc chắn 100% nếu điều này hoàn toàn di động trên tất cả các hệ thống, nhưng ít nhất nó cũng hoạt động trên Linux và OSX.


7
Điều này tốt hơn câu trả lời được đánh dấu, vì nó cũng hoạt động với Python 3.
Gerhard Hagerer

bất cứ ý tưởng tại sao đôi khi tôi nhận được các ký tự lạ ở cuối dòng, ngay cả khi tôi chấm dứt chuỗi đang được viết?
hồ chứa

23
Như đã viết, mã không làm rõ cách thích ứng với bất kỳ kích thước nào bạn đang lặp lại (không phải 21). Tôi sẽ cho phép n=21, thay thế range(21)bằng range(n), sau đó thêm j = (i + 1) / nvào bên trong vòng lặp và thay thế writecâu lệnh bằng sửa đổi nhỏ này : sys.stdout.write("[%-20s] %d%%" % ('='*int(20*j), 100*j)). Bây giờ thay đổi duy nhất bạn cần thực hiện là n=21trước vòng lặp (nhiều khả năng hơn n=len(iterable)), sau đó liệt kê đối tượng lặp lại. Tôi đề nghị chỉnh sửa này nhưng nó đã bị từ chối; rõ ràng chức năng "đi lệch khỏi mục đích ban đầu của bài viết".
Steven C. Howell

1
Thích nghi với kích thước tùy ý n, sys.stdout.write("[{:{}}] {:.1f}%".format("="*i, n-1, (100/(n-1)*i)))chỉ Python 3
GabrielChu

3
@ StevenC.Howell tại sao bạn không đăng nó dưới dạng câu trả lời trích dẫn Mark? đó là một phiên bản khác và nó rất hữu ích để sẵn sàng
GM

91

Tôi tìm thấy thư viện tqdm hữu ích ( https://github.com/tqdm/tqdm/ , trước đây: https://github.com/noamraph/tqdm ). Nó tự động ước tính thời gian hoàn thành và có thể được sử dụng như iterator.

Sử dụng:

import tqdm
import time

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

Kết quả trong:

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

tqdm có thể bọc bất kỳ lặp đi lặp lại.


4
Các dự án tqdm hiện đang duy trì ở đây do một nhóm mới của developpers.
gabious

1
waw, tqdm này là tuyệt vời, và rất dễ sử dụng :).
Sidahmed

6
đây dễ dàng là giải pháp đơn giản nhất
kd88

2
nó đi kèm với phân phối Anaconda quá! (ít nhất là đối với python 3.5 trở lên)
Jacquot

Cảm ơn, tuyệt vời, đơn giản và hiệu quả
DavideL

23

Bạn có thể sử dụng \r( vận chuyển trở lại ). Bản giới thiệu:

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()

Hãy thử totalnhư 10, sau đó bạn nhận được thông báo lỗiZeroDivisionError: long division or modulo by zero
unseen_rider

19

Tại đây bạn có thể sử dụng mã sau đây làm hàm:

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()

Với việc sử dụng .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()

Đối với tôi, dòng đầu tiên không được sửa đổi bởi con trỏ - chỉ dòng thứ hai mới có. Do đó, nhiều dòng thu được cho thanh tiến trình và một dòng ví dụ] percent * 100 %
unseen_rider

6

dựa trên các câu trả lời ở trên và các câu hỏi tương tự khác về thanh tiến trình CLI, tôi nghĩ rằng tôi đã có câu trả lời chung cho tất cả chúng. Kiểm tra nó tại https://stackoverflow.com/a/15860757/2254146

Đây là một bản sao của chức năng, nhưng được sửa đổi để phù hợp với phong cách của bạn:

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()

Giống như

Phần trăm: [====================] 99,0%


Thanh tiến trình không xuất hiện đối với tôi
unseen_rider

Tôi nghĩ rằng nó không cần nhiều vị trí thập phân, tức là vòng (tiến độ * 100,2)
Andrés Sánchez

4

Nếu bạn đang phát triển giao diện dòng lệnh, tôi khuyên bạn nên xem cái clicknào rất đẹp:

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)

Ở đây đầu ra bạn nhận được:

$ python test.py
  [====================================]  100%
  [====================================]  100%
  [=========                           ]   27%

4

Cải thiện hơn nữa, sử dụng một chức năng như:

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()

ví dụ gọi:

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

đầu ra:

[================================================  ] 96%  blah  

hoàn hảo! và không cần mô-đun
yurividal

3

Tôi đã tìm thấy chủ đề này ngày hôm nay và sau khi đã thử giải pháp này từ 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)

Tôi có thể nói rằng điều này hoạt động tốt trên W7-64 với python 3.4.3 64-bit nhưng chỉ trong bảng điều khiển gốc. Tuy nhiên, khi sử dụng bảng điều khiển tích hợp của spyder 3.0.0dev, các ngắt dòng vẫn còn / hiện diện một lần nữa. Vì điều này khiến tôi mất một thời gian để tìm hiểu, tôi muốn báo cáo quan sát này ở đây.


2

Dựa trên một số câu trả lời ở đây và các nơi khác, tôi đã viết hàm đơn giản này hiển thị thanh tiến trình và thời gian còn lại ước tính / đã trôi qua. Nên làm việc trên hầu hết các máy dựa trên 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

2

Đây là một cách tiếp cận khá đơn giản có thể được sử dụng với bất kỳ vòng lặp.

#!/usr/bin/python
for i in range(100001):
    s =  ((i/5000)*'#')+str(i)+(' %')
    print ('\r'+s),

'#' Làm gì?
unseen_rider

@unseen_rider Ở đây '#' chỉ là một biểu tượng sẽ được lặp lại khi số lần lặp lặp tăng lên.
NILESH KUMAR

2

Dễ nhất vẫn là

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()

Điều quan trọng là chuyển đổi kiểu số nguyên thành chuỗi.


2

Như được mô tả trong giải pháp của Mark Rushakoff , bạn có thể xuất ký tự trả về vận chuyển sys.stdout.write('\r'), để đặt lại con trỏ về đầu dòng. Để khái quát hóa giải pháp đó, đồng thời triển khai chuỗi f của Python 3 , bạn có thể sử dụng

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

1

Hãy thử PyProg. PyProg là một thư viện mã nguồn mở cho Python để tạo các thanh & chỉ báo tiến trình siêu tùy biến.

Nó hiện đang ở phiên bản 1.0.2; nó được lưu trữ trên Github và có sẵn trên PyPI (Liên kết xuống bên dưới). Nó tương thích với Python 3 & 2 và nó cũng có thể được sử dụng với Qt Console.

Nó thực sự dễ sử dụng. Các mã sau đây:

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()

sẽ tạo ra chính xác những gì bạn muốn (ngay cả chiều dài thanh!):

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

Để biết thêm tùy chọn để tùy chỉnh thanh tiến trình, hãy truy cập trang Github của trang web này.

Tôi thực sự đã tạo PyProg vì tôi cần một thư viện thanh tiến trình đơn giản nhưng siêu tùy biến. Bạn có thể dễ dàng cài đặt nó với : pip install pyprog.

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


1

Sử dụng câu trả lời @ Mark-Rushakoff, tôi đã tìm ra một cách tiếp cận đơn giản hơn, không cần phải gọi thư viện sys. Nó hoạt động với Python 3. Đã thử nghiệm trong 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)

Như được mô tả trong câu trả lời cho một câu hỏi khác print là một trình bao bọc mỏng định dạng đầu vào và gọi hàm ghi của một đối tượng nhất định. Theo mặc định, đối tượng này là sys.stdout.
Steven C. Howell

0

Đây là một cái gì đó tôi đã thực hiện bằng cách sử dụng giải pháp của @ Mark-Rushakoff. Để thích ứng điều chỉnh theo chiều rộng thiết bị đầu cuối.

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("")
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.