Python เพื่อพิมพ์แถบสถานะและเปอร์เซ็นต์


160

หากต้องการติดตั้งแถบสถานะด้านล่าง:

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

ฉันต้องการสิ่งนี้เพื่อพิมพ์ออกมาเพื่อ stdout และทำให้สดชื่นไม่พิมพ์ไปยังอีกบรรทัด ทำอย่างไร


ฉันได้เผยแพร่แถบความคืบหน้ารูปแบบใหม่ซึ่งคุณสามารถพิมพ์ดูปริมาณงานและอื่น ๆ ได้แม้กระทั่งหยุดชั่วขณะนอกเหนือจากภาพเคลื่อนไหวที่เจ๋งมาก! โปรดดู: github.com/rsalmei/alive-progress ! มีชีวิตอยู่ความคืบหน้า
rsalmei

คำตอบ:


146

มีโมดูล Python ที่คุณสามารถรับได้จากPyPI ที่เรียกprogressbarว่าใช้งานฟังก์ชันดังกล่าว หากคุณไม่รังเกียจที่จะเพิ่มการพึ่งพามันเป็นทางออกที่ดี มิฉะนั้นไปด้วยคำตอบอย่างใดอย่างหนึ่ง

ตัวอย่างง่ายๆของการใช้งาน:

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

ในการติดตั้งคุณสามารถใช้easy_install progressbarหรือpip install progressbarถ้าคุณชอบ pip


คำตอบทั้งหมดนั้นยอดเยี่ยม แต่ฉันชอบโมดูลมากที่สุด ขอบคุณทุกคน.
Stan

8
บางทีตัวอย่างนี้อาจต้องการอะไรbar.start()?
Jim Raynor

2
ไม่ทำงานบนเครื่องของฉัน - จำเป็นต้องเพิ่มbar.start()
Zach

1
โมดูลนี้ไม่ได้รับการปรับปรุงใน 2 ปี อย่าใช้มันกับซอฟต์แวร์ใหม่!
Navin

4
การติดตั้ง: sudo -H pip install progressbar2.
Martin Thoma

254

'\r'ตัวอักษร (กลับรถ) รีเซ็ตเคอร์เซอร์ไปยังจุดเริ่มต้นของบรรทัดและช่วยให้คุณเขียนสิ่งที่ก่อนหน้านี้ในบรรทัด

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)

ฉันไม่แน่ใจ 100% ว่านี่สามารถพกพาได้อย่างสมบูรณ์ในทุกระบบ แต่ใช้ได้บน Linux และ OSX เป็นอย่างน้อย


7
นี่มันดีกว่าคำตอบที่ทำเครื่องหมายไว้เพราะมันใช้ได้กับ Python 3 ด้วย
แกร์ฮาร์ดฮาเกอร์เกอร์

ความคิดใดที่ว่าทำไมฉันถึงได้ตัวละครแปลก ๆ ในตอนท้ายของบรรทัดในบางครั้งแม้ว่าฉันจะยกเลิกสตริงที่กำลังเขียนอยู่?
เก็บน้ำ

23
ตามที่เขียนไว้โค้ดไม่ได้ทำให้ชัดเจนว่าจะปรับขนาดนี้ให้เหมาะกับขนาดที่คุณวนซ้ำ (ไม่ใช่ 21) ฉันจะปล่อยให้n=21แทนที่range(21)ด้วยrange(n)แล้วเพิ่มj = (i + 1) / nภายในวงและแทนที่คำสั่งที่มีการปรับเปลี่ยนเล็กน้อยนี้:write sys.stdout.write("[%-20s] %d%%" % ('='*int(20*j), 100*j))ทีนี้การเปลี่ยนแปลงเพียงอย่างเดียวที่คุณต้องทำก็คือทำn=21ก่อนที่จะวนซ้ำ (มีโอกาสมากขึ้นn=len(iterable)) จากนั้นระบุจำนวนวัตถุที่ทำซ้ำ ฉันแนะนำการแก้ไขนี้ แต่มันถูกปฏิเสธ; ฟังก์ชั่นที่เห็นได้ชัด "เบี่ยงเบนไปจากความตั้งใจเดิมของโพสต์"
สตีเวนซีโฮเวล

1
ปรับให้เข้ากับขนาดโดยพลการของ n, sys.stdout.write("[{:{}}] {:.1f}%".format("="*i, n-1, (100/(n-1)*i)))Python 3 เท่านั้น
GabrielChu

3
@ StevenCHowell ทำไมคุณไม่โพสต์มันเป็นคำตอบที่อ้างถึงมาร์ค? เป็นรุ่นอื่นและเป็นประโยชน์อย่างมากในการทำให้พร้อม
GM

91

ฉันพบไลบรารี่ที่มีประโยชน์ tqdm ( https://github.com/tqdm/tqdm/ก่อนหน้านี้: https://github.com/noamraph/tqdm ) โดยประมาณเวลาที่เสร็จสมบูรณ์โดยอัตโนมัติและสามารถใช้เป็นตัววนซ้ำ

การใช้งาน:

import tqdm
import time

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

ผลลัพธ์ใน:

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

tqdm สามารถห่อใด ๆ iterable



1
ว้าว tqdm นี้น่าทึ่งมากและใช้งานง่ายมาก :)
Sidahmed

6
นี่เป็นวิธีที่ง่ายที่สุดในการแก้ปัญหา
kd88

2
มันมาพร้อมกับอนาคอนดา distrib เกินไป! (สำหรับไพ ธ อน 3.5 และสูงกว่าอย่างน้อย)
Jacquot

ขอบคุณน่าอัศจรรย์เรียบง่ายและมีประสิทธิภาพ
DavideL

23

คุณสามารถใช้\r( carriage return ) การสาธิต:

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

ลองtotalเป็น10แล้วคุณจะได้รับข้อความแสดงข้อผิดพลาดZeroDivisionError: long division or modulo by zero
unseen_rider

19

ที่นี่คุณสามารถใช้รหัสต่อไปนี้เป็นฟังก์ชั่น:

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

ด้วยการใช้. 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()

สำหรับฉันบรรทัดแรกไม่ได้รับการแก้ไขโดยเคอร์เซอร์ - เฉพาะบรรทัดที่สองเท่านั้น ดังนั้นหลายบรรทัดที่ได้รับสำหรับแถบความคืบหน้าและอีกหนึ่งบรรทัดสำหรับเช่น] percent * 100 %
unseen_rider

6

จากคำตอบข้างต้นและคำถามที่คล้ายกันอื่น ๆ เกี่ยวกับแถบความคืบหน้าของ CLI ฉันคิดว่าฉันได้รับคำตอบทั่วไปสำหรับพวกเขาทั้งหมด ตรวจสอบได้ที่https://stackoverflow.com/a/15860757/2254146

นี่คือสำเนาของฟังก์ชั่น แต่ปรับเปลี่ยนให้เหมาะกับสไตล์ของคุณ

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

ดูเหมือนกับ

เปอร์เซ็นต์: [====================] 99.0%


แถบความคืบหน้าไม่ปรากฏขึ้นสำหรับฉัน
unseen_rider

ฉันคิดว่ามันไม่จำเป็นที่ทศนิยมหลายตำแหน่งเช่นรอบ (ความคืบหน้า * 100,2)
AndrésSánchez

4

หากคุณกำลังพัฒนาอินเตอร์เฟสบรรทัดคำสั่งฉันขอแนะนำให้คุณดูที่clickดีมาก:

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)

ผลลัพธ์ที่คุณจะได้รับ:

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

4

ปรับปรุงเพิ่มเติมโดยใช้ฟังก์ชั่นเป็น:

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

ตัวอย่างการโทร:

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

เอาท์พุท:

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

ที่สมบูรณ์แบบ! และไม่มีโมดูลที่จำเป็น
yurividal

3

ฉันมาที่หัวข้อนี้ในวันนี้และหลังจากได้ลองวิธีแก้ปัญหานี้จาก 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)

ฉันสามารถพูดได้ว่ามันใช้งานได้ดีบน W7-64 ด้วย python 3.4.3 64- บิต แต่เฉพาะในคอนโซลท้องถิ่น อย่างไรก็ตามเมื่อใช้คอนโซลของ Spyder ในตัว 3.0.0dev ตัวแบ่งบรรทัดจะยังคงอยู่ / นำเสนออีกครั้ง เนื่องจากนี่ใช้เวลาพอสมควรแล้วฉันจึงอยากรายงานข้อสังเกตนี้ที่นี่


2

จากคำตอบบางส่วนที่นี่และที่อื่น ๆ ฉันได้เขียนฟังก์ชั่นง่ายๆนี้ซึ่งแสดงแถบความคืบหน้าและเวลาที่เหลือ / ประมาณที่ผ่านไป ควรทำงานกับเครื่องที่ใช้ระบบปฏิบัติการยูนิกซ์เป็นส่วนใหญ่

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

นี่เป็นวิธีการง่ายๆที่สามารถใช้กับลูปใดก็ได้

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

'#' ทำอะไร
unseen_rider

@unseen_rider ที่นี่ '#' เป็นเพียงสัญลักษณ์ที่จะทำซ้ำเมื่อการวนซ้ำวนซ้ำเพิ่มขึ้น
NILESH KUMAR

2

ที่ง่ายที่สุดคือยัง

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

กุญแจสำคัญคือการแปลงประเภทจำนวนเต็มเป็นสตริง


2

ตามที่อธิบายไว้ในโซลูชันของ Mark Rushakoffคุณสามารถส่งออกอักขระคืนค่าขนส่งsys.stdout.write('\r')เพื่อรีเซ็ตเคอร์เซอร์ไปที่จุดเริ่มต้นของบรรทัด เพื่อสรุปวิธีแก้ปัญหานั้นในขณะที่ยังใช้f-Strings ของ Python 3 ด้วยคุณสามารถใช้

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

ลอง PyProg PyProg เป็นไลบรารี่แบบโอเพ่นซอร์สสำหรับ Python เพื่อสร้างตัวบ่งชี้และแถบความคืบหน้าที่สามารถปรับแต่งได้พิเศษ

ขณะนี้อยู่ในเวอร์ชัน 1.0.2; มันเป็นเจ้าภาพใน Github และสามารถใช้ได้ใน PyPI (ลิงค์ลงด้านล่าง) มันเข้ากันได้กับ Python 3 และ 2 และยังสามารถใช้กับ Qt Console

มันใช้งานง่ายมาก รหัสต่อไปนี้:

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

จะผลิตสิ่งที่คุณต้องการ (แม้ความยาวของแท่ง!):

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

สำหรับตัวเลือกเพิ่มเติมในการปรับแต่งแถบความคืบหน้าให้ไปที่หน้า Github ของเว็บไซต์นี้

ฉันทำ PyProg จริง ๆ เพราะฉันต้องการไลบรารีแถบความคืบหน้าที่เรียบง่าย pip install pyprogคุณสามารถติดตั้งได้ด้วย:

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


1

การใช้ @ Mark-Rushakoff คำตอบฉันหาวิธีที่ง่ายกว่าไม่จำเป็นต้องเรียก sys ไลบรารี่ ใช้งานได้กับ Python 3 ผ่านการทดสอบใน 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)

ตามที่อธิบายไว้ในคำตอบของคำถามที่แตกต่างกัน printคือเสื้อคลุมบาง ๆ ที่จัดรูปแบบอินพุตและเรียกใช้ฟังก์ชันการเขียนของวัตถุที่กำหนด sys.stdoutโดยค่าเริ่มต้นวัตถุนี้คือ
สตีเวนซีโฮเวล

0

นี่คือสิ่งที่ฉันทำโดยใช้วิธีการแก้ปัญหาโดย @ Mark-Rushakoff เพื่อปรับให้เข้ากับความกว้างของเทอร์มินัล

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("")
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.