Thay thế đầu ra bảng điều khiển bằng Python


106

Tôi tự hỏi làm thế nào tôi có thể tạo một trong những bộ đếm bảng điều khiển tiện lợi đó bằng Python như trong một số chương trình C / C ++.

Tôi có một vòng lặp đang thực hiện mọi việc và đầu ra hiện tại nằm dọc theo các dòng:

Doing thing 0
Doing thing 1
Doing thing 2
...

điều gì sẽ gọn gàng hơn nếu chỉ có cập nhật dòng cuối cùng;

X things done.

Tôi đã thấy điều này trong một số chương trình giao diện điều khiển và đang tự hỏi liệu / làm cách nào để thực hiện điều này trong Python.


3
Bạn nên xem xét những lời nguyền rủa .
Björn Pollex

2
Chỉ cần sử dụng print: stackoverflow.com/a/8436827/1959808
Ioannis Filippidis

1
@ BjörnPollex, curseslà quá mức cần thiết (xem câu trả lời được chấp nhận).
Alexey

Câu trả lời:


151

Một giải pháp dễ dàng là chỉ viết "\r"trước chuỗi và không thêm dòng mới; nếu chuỗi không bao giờ ngắn hơn thì điều này là đủ ...

sys.stdout.write("\rDoing thing %i" % i)
sys.stdout.flush()

Tinh vi hơn một chút là thanh tiến trình ... đây là thứ tôi đang sử dụng:

def startProgress(title):
    global progress_x
    sys.stdout.write(title + ": [" + "-"*40 + "]" + chr(8)*41)
    sys.stdout.flush()
    progress_x = 0

def progress(x):
    global progress_x
    x = int(x * 40 // 100)
    sys.stdout.write("#" * (x - progress_x))
    sys.stdout.flush()
    progress_x = x

def endProgress():
    sys.stdout.write("#" * (40 - progress_x) + "]\n")
    sys.stdout.flush()

Bạn gọi startProgressđi qua các mô tả về các hoạt động, sau đó progress(x)ở đâu xlà tỷ lệ phần trăm và cuối cùngendProgress()


2
Điều gì sẽ xảy ra nếu chuỗi ngắn hơn chuỗi trước đó?
math2001

6
@ math2001 padding với khoảng trắng.
felipsmartins

Chỉ bình chọn cho 2 dòng mã đầu tiên. Phần thanh tiến trình đang trở nên chậm trong một số trường hợp. Dù sao cám ơn @ 6502
WaterRocket8236

Một số chương trình ( restic, flatpak) có thể cập nhật một số dòng đầu ra của bảng điều khiển. Bạn có biết cách nào có thể đạt được điều này không?
Alexey

1
@Alexey: bạn có thể sử dụng mã thoát ANSI để di chuyển con trỏ, xóa các phần của màn hình và thay đổi màu sắc ... xem en.wikipedia.org/wiki/ANSI_escape_code
6502

39

Một giải pháp thanh lịch hơn có thể là:

def progressBar(current, total, barLength = 20):
    percent = float(current) * 100 / total
    arrow   = '-' * int(percent/100 * barLength - 1) + '>'
    spaces  = ' ' * (barLength - len(arrow))

    print('Progress: [%s%s] %d %%' % (arrow, spaces, percent), end='\r')

gọi hàm này với valueendvalue, kết quả phải là

Progress: [------------->      ] 69 %

Lưu ý: Phiên bản Python 2.x tại đây .


Bạn nên sử dụng Halo để có thanh tiến trình và bộ quay tốt hơn.
Aravind Voggu

17

Trong python 3, bạn có thể làm điều này để in trên cùng một dòng:

print('', end='\r')

Đặc biệt hữu ích để theo dõi các cập nhật mới nhất và tiến trình.

Tôi cũng sẽ giới thiệu tqdm từ đây nếu ai đó muốn xem tiến trình của một vòng lặp. Nó in lần lặp hiện tại và tổng số lần lặp dưới dạng thanh tiến trình với thời gian hoàn thành dự kiến. Siêu hữu ích và nhanh chóng. Hoạt động cho python2 và python3.


7

Câu trả lời khác có thể tốt hơn, nhưng đây là những gì tôi đang làm. Đầu tiên, tôi đã tạo một hàm có tên là tiến trình in ra ký tự xóa lùi:

def progress(x):
    out = '%s things done' % x  # The output
    bs = '\b' * 1000            # The backspace
    print bs,
    print out,

Sau đó, tôi đã gọi nó trong một vòng lặp trong hàm chính của mình như sau:

def main():
    for x in range(20):
        progress(x)
    return

Điều này tất nhiên sẽ xóa toàn bộ dòng, nhưng bạn có thể làm rối với nó để làm chính xác những gì bạn muốn. Tôi đã tạo một thanh tiến trình bằng phương pháp này.


4
Hoạt động, nhưng nếu dòng trước đó có nhiều ký tự hơn dòng tiếp theo, thì các ký tự sau khi kết thúc dòng mới vẫn ở dòng trước: "Bản ghi kiểm tra chính tả 417/701 [serfice đã thay đổi thành bề mặt] khi] uminescence] cence] shmentarianism] "
Lil 'Bits

7

Đối với bất kỳ ai tình cờ gặp phải điều này nhiều năm sau (như tôi đã làm), tôi đã điều chỉnh các phương pháp của 6502 một chút để cho phép thanh tiến trình giảm cũng như tăng lên. Hữu ích trong các trường hợp hơn một chút. Cảm ơn 6502 cho một công cụ tuyệt vời!

Về cơ bản, sự khác biệt duy nhất là toàn bộ dòng #s và -s được viết mỗi khi tiến trình (x) được gọi, và con trỏ luôn được đưa trở lại đầu thanh.

def startprogress(title):
    """Creates a progress bar 40 chars long on the console
    and moves cursor back to beginning with BS character"""
    global progress_x
    sys.stdout.write(title + ": [" + "-" * 40 + "]" + chr(8) * 41)
    sys.stdout.flush()
    progress_x = 0


def progress(x):
    """Sets progress bar to a certain percentage x.
    Progress is given as whole percentage, i.e. 50% done
    is given by x = 50"""
    global progress_x
    x = int(x * 40 // 100)                      
    sys.stdout.write("#" * x + "-" * (40 - x) + "]" + chr(8) * 41)
    sys.stdout.flush()
    progress_x = x


def endprogress():
    """End of progress bar;
    Write full bar, then move to next line"""
    sys.stdout.write("#" * 40 + "]\n")
    sys.stdout.flush()

1
Tôi đã tìm thấy mặc dù, rằng điều này có thể gây ra một số chậm trễ nếu nó được gọi là quá thường xuyên bởi các mã, vì vậy tôi đoán YMMV
jat255

6

Nếu tôi hiểu rõ (không chắc) bạn có muốn in bằng cách sử dụng <CR>hay không <LR>?

Nếu vậy điều này là có thể, miễn là thiết bị đầu cuối của bảng điều khiển cho phép điều này (nó sẽ bị hỏng khi đầu ra si được chuyển hướng đến một tệp).

from __future__ import print_function
print("count x\r", file=sys.stdout, end=" ")

5

Nó có thể được thực hiện mà không cần sử dụng thư viện sys nếu chúng ta nhìn vào print()hàm

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

Đây là mã của tôi:

def update(n):
    for i in range(n):
        print("i:",i,sep='',end="\r",flush=True)
        #time.sleep(1)

5

Tôi đã viết điều này một thời gian trước đây và thực sự hài lòng với nó. Hãy sử dụng nó.

Nó mất một indextotalvà tùy chọn titlehoặc bar_length. Sau khi hoàn tất, thay mặt kính giờ bằng một dấu kiểm.

⏳ Calculating: [████░░░░░░░░░░░░░░░░░░░░░] 18.0% done

✅ Calculating: [█████████████████████████] 100.0% done

Tôi đã bao gồm một ví dụ có thể được chạy để kiểm tra nó.

import sys
import time

def print_percent_done(index, total, bar_len=50, title='Please wait'):
    '''
    index is expected to be 0 based index. 
    0 <= index < total
    '''
    percent_done = (index+1)/total*100
    percent_done = round(percent_done, 1)

    done = round(percent_done/(100/bar_len))
    togo = bar_len-done

    done_str = '█'*int(done)
    togo_str = '░'*int(togo)

    print(f'\t⏳{title}: [{done_str}{togo_str}] {percent_done}% done', end='\r')

    if round(percent_done) == 100:
        print('\t✅')


r = 50
for i in range(r):
    print_percent_done(i,r)
    time.sleep(.02)

Tôi cũng có một phiên bản với thanh tiến trình đáp ứng tùy thuộc vào chiều rộng thiết bị đầu cuối sử dụng shutil.get_terminal_size()nếu điều đó được quan tâm.


4

Đã thêm một chút chức năng vào ví dụ của Aravind Voggu :

def progressBar(name, value, endvalue, bar_length = 50, width = 20):
        percent = float(value) / endvalue
        arrow = '-' * int(round(percent*bar_length) - 1) + '>'
        spaces = ' ' * (bar_length - len(arrow))
        sys.stdout.write("\r{0: <{1}} : [{2}]{3}%".format(\
                         name, width, arrow + spaces, int(round(percent*100))))
        sys.stdout.flush()
        if value == endvalue:     
             sys.stdout.write('\n\n')

Bây giờ bạn có thể tạo nhiều thanh tiến trình mà không cần thay thế thanh trước đó.

Tôi cũng đã thêm namedưới dạng một giá trị có chiều rộng cố định.

Đối với hai vòng lặp và hai lần, việc sử dụng progressBar()kết quả sẽ như sau:

hoạt ảnh thanh tiến trình


-1

Mã dưới đây sẽ đếm Tin nhắn từ 0 đến 137 mỗi 0,3 giây thay thế cho số trước đó.

Số ký hiệu đến hậu trường = số chữ số.

stream = sys.stdout
for i in range(137):
    stream.write('\b' * (len(str(i)) + 10))
    stream.write("Message : " + str(i))
    stream.flush()
    time.sleep(0.3)
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.