Бар На Python Прогресс

Как использовать индикатор выполнения, когда мой скрипт выполняет какую-то задачу, которая, вероятно, займет время?

например, функция, которая занимает некоторое время для завершения и возвращает True когда сделано. Как я могу отобразить индикатор выполнения во время выполнения функции?

обратите внимание, что мне нужно, чтобы это было в режиме реального времени, поэтому я не могу понять, что с этим делать. Нужна ли мне thread для этого? Я понятия не имею.

прямо сейчас я ничего не печатаю в то время как функция выполняется, однако индикатор будет приятно. Также меня больше интересует, как это можно сделать с точки зрения кода.

21 ответов


существуют определенные библиотеки (как вот этот) но, может быть, что-то очень простое сделает:

import time
import sys

toolbar_width = 40

# setup toolbar
sys.stdout.write("[%s]" % (" " * toolbar_width))
sys.stdout.flush()
sys.stdout.write("\b" * (toolbar_width+1)) # return to start of line, after '['

for i in xrange(toolbar_width):
    time.sleep(0.1) # do real work here
    # update the bar
    sys.stdout.write("-")
    sys.stdout.flush()

sys.stdout.write("\n")

Примечание: этот progressbar является вилкой progressbar который не поддерживался в течение многих лет.


С tqdm вы можете добавить индикатор прогресса в свои петли за секунду:

In [20]: import time

In [21]: from tqdm import tqdm

In [23]: for i in tqdm(range(10)):
   ....:     time.sleep(3)

 60%|██████    | 6/10 [00:18<00:12,  0.33 it/s]

вышеуказанные предложения довольно хороши, но я думаю, что большинство людей просто хотят готовое решение, без зависимостей от внешних пакетов, но также многоразовое.

Я получил лучшие моменты из всего вышесказанного и превратил его в функцию вместе с тестовыми случаями.

чтобы использовать его, просто скопируйте строки в разделе "def update_progress(progress)", но не тестовый скрипт. Не забудьте импортировать sys. Назвать это всякий раз, когда вам нужно отобразить или обновить прогресс бар.

это работает, напрямую отправляя символ "\r " в консоль, чтобы переместить курсор обратно в начало. "print" в python не пересчитывает вышеуказанный символ для этой цели, поэтому нам нужен "sys"

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 = 10 # 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()


# update_progress test script
print "progress : 'hello'"
update_progress("hello")
time.sleep(1)

print "progress : 3"
update_progress(3)
time.sleep(1)

print "progress : [23]"
update_progress([23])
time.sleep(1)

print ""
print "progress : -10"
update_progress(-10)
time.sleep(2)

print ""
print "progress : 10"
update_progress(10)
time.sleep(2)

print ""
print "progress : 0->1"
for i in range(100):
    time.sleep(0.1)
    update_progress(i/100.0)

print ""
print "Test completed"
time.sleep(10)

вот что показывает результат тестового сценария (анимируется последний индикатор выполнения):

progress : 'hello'
Percent: [----------] 0% error: progress var must be float
progress : 3
Percent: [##########] 100% Done...
progress : [23]
Percent: [----------] 0% error: progress var must be float

progress : -10
Percent: [----------] 0% Halt...

progress : 10
Percent: [##########] 100% Done...

progress : 0->1
Percent: [##########] 99.0%
Test completed

для подобного приложения (отслеживание прогресса в цикле) я просто использовал python-progressbar:

их примеру идет что-то вроде этого,

from progressbar import *               # just a simple progress bar


widgets = ['Test: ', Percentage(), ' ', Bar(marker='0',left='[',right=']'),
           ' ', ETA(), ' ', FileTransferSpeed()] #see docs for other options

pbar = ProgressBar(widgets=widgets, maxval=500)
pbar.start()

for i in range(100,500+1,50):
    # here do something long at each iteration
    pbar.update(i) #this adds a little symbol at each iteration
pbar.finish()
print

Я только что сделал простой класс прогресса для моих нужд после поиска здесь эквивалентного решения. Я подумал, что неплохо было бы его отправить.

from __future__ import print_function
import sys
import re


class ProgressBar(object):
    DEFAULT = 'Progress: %(bar)s %(percent)3d%%'
    FULL = '%(bar)s %(current)d/%(total)d (%(percent)3d%%) %(remaining)d to go'

    def __init__(self, total, width=40, fmt=DEFAULT, symbol='=',
                 output=sys.stderr):
        assert len(symbol) == 1

        self.total = total
        self.width = width
        self.symbol = symbol
        self.output = output
        self.fmt = re.sub(r'(?P<name>%\(.+?\))d',
            r'\g<name>%dd' % len(str(total)), fmt)

        self.current = 0

    def __call__(self):
        percent = self.current / float(self.total)
        size = int(self.width * percent)
        remaining = self.total - self.current
        bar = '[' + self.symbol * size + ' ' * (self.width - size) + ']'

        args = {
            'total': self.total,
            'bar': bar,
            'current': self.current,
            'percent': percent * 100,
            'remaining': remaining
        }
        print('\r' + self.fmt % args, file=self.output, end='')

    def done(self):
        self.current = self.total
        self()
        print('', file=self.output)

пример :

from time import sleep

progress = ProgressBar(80, fmt=ProgressBar.FULL)

for x in xrange(progress.total):
    progress.current += 1
    progress()
    sleep(0.1)
progress.done()

напечатает следующее:

[======== ] 17/80 ( 21%) 63 to go


попробуйте прогресс от https://pypi.python.org/pypi/progress.

from progress.bar import Bar

bar = Bar('Processing', max=20)
for i in range(20):
    # Do some work
    bar.next()
bar.finish()

результатом будет следующий бар:

Processing |#############                   | 42/100

мне нравится Брайан Khuu это для своей простоты и не нужны внешние пакеты. Я немного изменил его, поэтому я добавляю свою версию здесь:

import sys
import time


def updt(total, progress):
    """
    Displays or updates a console progress bar.

    Original source: https://stackoverflow.com/a/15860757/1391441
    """
    barLength, status = 20, ""
    progress = float(progress) / float(total)
    if progress >= 1.:
        progress, status = 1, "\r\n"
    block = int(round(barLength * progress))
    text = "\r[{}] {:.0f}% {}".format(
        "#" * block + "-" * (barLength - block), round(progress * 100, 0),
        status)
    sys.stdout.write(text)
    sys.stdout.flush()


runs = 300
for run_num in range(runs):
    time.sleep(.1)
    updt(runs, run_num + 1)

требуется общее количество запусков (total) и количество запусков, обработанных до сих пор (progress), полагая total >= progress. Результат выглядит так:

[#####---------------] 27%

мне очень нравится python-progressbar, так как он очень прост в использовании.

для самого простого случая это просто:

import progressbar
import time

progress = progressbar.ProgressBar()
for i in progress(range(80)):
    time.sleep(0.01)

возникновение можно подгонять, и это может показать всего оставшегося времени. Для примера используем тот же код, что и выше, но с:

progress = progressbar.ProgressBar(widgets=[progressbar.Bar('=', '[', ']'), ' ',
                                            progressbar.Percentage(), ' ',
                                            progressbar.ETA()])

многие из ответов выше полагаются на внешние пакеты, но я также думаю (как указано выше), что большинство людей просто хотят готовое решение. Приведенный ниже код можно адаптировать в соответствии с вашими потребностями, настроив строковую часть.

это проще и работает без необходимости второго потока для обновления панели. Некоторые пакеты делать. Второй поток может быть проблемой, например, для ноутбука ipython.

приведенный ниже код работает только с итераторами, которые укажите длину(т. е. LEN (итератор) должен быть определен).

import sys

def progressbar(it, prefix="", size=60):
    count = len(it)
    def _show(_i):
        x = int(size*_i/count)
        sys.stdout.write("%s[%s%s] %i/%i\r" % (prefix, "#"*x, "."*(size-x), _i, count))
        sys.stdout.flush()

    _show(0)
    for i, item in enumerate(it):
        yield item
        _show(i+1)
    sys.stdout.write("\n")
    sys.stdout.flush()

пример:

import time

for i in progressbar(range(15), "Computing: ", 40):
    time.sleep(0.1) # any calculation you need

выход:

Computing: [........................................] 0/15

...

Computing: [########................................] 3/15

...

Computing: [########################################] 15/15

it может быть любой итерируемый объект с len, например ['a', 'b',` c'] ' работает просто отлично.


использовать эту библиотеку: fish (GitHub).

использование:

>>> import fish
>>> while churning:
...     churn_churn()
...     fish.animate()

удачи!


Я такой страница.

начинается с простого примера и переходит на многопоточную версию. Работает из коробки. Не требуется пакетов сторонних производителей.

код будет выглядеть примерно так:

import time
import sys

def do_task():
    time.sleep(1)

def example_1(n):
    for i in range(n):
        do_task()
        print '\b.',
        sys.stdout.flush()
    print ' Done!'

print 'Starting ',
example_1(10)

или вот пример использования потоков для запуска вращающейся панели загрузки во время работы программы:

import sys
import time
import threading

class progress_bar_loading(threading.Thread):

    def run(self):
            global stop
            global kill
            print 'Loading....  ',
            sys.stdout.flush()
            i = 0
            while stop != True:
                    if (i%4) == 0: 
                        sys.stdout.write('\b/')
                    elif (i%4) == 1: 
                        sys.stdout.write('\b-')
                    elif (i%4) == 2: 
                        sys.stdout.write('\b\')
                    elif (i%4) == 3: 
                        sys.stdout.write('\b|')

                    sys.stdout.flush()
                    time.sleep(0.2)
                    i+=1

            if kill == True: 
                print '\b\b\b\b ABORT!',
            else: 
                print '\b\b done!',


kill = False      
stop = False
p = progress_bar_loading()
p.start()

try:
    #anything you want to run. 
    time.sleep(1)
    stop = True
except KeyboardInterrupt or EOFError:
         kill = True
         stop = True

Если это большой цикл с фиксированным количеством итераций, что занимает много времени вы можете использовать эту функцию я сделал. Каждая итерация цикла добавляет прогресс. Где count-текущая итерация цикла, total-это значение, которое вы зацикливаете, а size (int) - это то, насколько большой вы хотите бар с шагом 10 т. е. (размер 1 =10 символов, размер 2 =20 символов)

import sys
def loadingBar(count,total,size):
    percent = float(count)/float(total)*100
    sys.stdout.write("\r" + str(int(count)).rjust(3,'0')+"/"+str(int(total)).rjust(3,'0') + ' [' + '='*int(percent/10)*size + ' '*(10-int(percent/10))*size + ']')

пример:

for i in range(0,100):
     loadingBar(i,100,2)
     #do some code 

выход:

i = 50
>> 050/100 [==========          ]

Если ваша работа не может быть разбита на измеримые куски, вы можете вызвать свою функцию в новом потоке и время, сколько это займет:

import thread
import time
import sys

def work():
    time.sleep( 5 )

def locked_call( func, lock ):
    lock.acquire()
    func()
    lock.release()

lock = thread.allocate_lock()
thread.start_new_thread( locked_call, ( work, lock, ) )

# This part is icky...
while( not lock.locked() ):
    time.sleep( 0.1 )

while( lock.locked() ):
    sys.stdout.write( "*" )
    sys.stdout.flush()
    time.sleep( 1 )
print "\nWork Done"

вы можете, очевидно, увеличить точность синхронизации по мере необходимости.


вот короткое решение, которое программно строит панель загрузки (вы должны решить, как долго вы этого хотите).

import time

n = 33  # or however many loading slots you want to have
load = 0.01  # artificial loading time!
loading = '.' * n  # for strings, * is the repeat operator

for i in range(n+1):
    # this loop replaces each dot with a hash!
    print('\r%s Loading at %3d percent!' % (loading, i*100/n), end='')
    loading = loading[:i] + '#' + loading[i+1:]
    time.sleep(load)

Мне нравится Габриэль ответ, но я изменил его, чтобы быть гибким. Вы можете отправить bar-length в функцию и получить индикатор выполнения с любой длиной, которую вы хотите. И вы не можете иметь индикатор с нулевой или отрицательной длины. Кроме того, вы можете использовать эту функцию, как Габриэль ответ (см. Пример #2).

import sys
import time

def ProgressBar(Total, Progress, BarLength=20, ProgressIcon="#", BarIcon="-"):
    try:
        # You can't have a progress bar with zero or negative length.
        if BarLength <1:
            BarLength = 20
        # Use status variable for going to the next line after progress completion.
        Status = ""
        # Calcuting progress between 0 and 1 for percentage.
        Progress = float(Progress) / float(Total)
        # Doing this conditions at final progressing.
        if Progress >= 1.:
            Progress = 1
            Status = "\r\n"    # Going to the next line
        # Calculating how many places should be filled
        Block = int(round(BarLength * Progress))
        # Show this
        Bar = "[{}] {:.0f}% {}".format(ProgressIcon * Block + BarIcon * (BarLength - Block), round(Progress * 100, 0), Status)
        return Bar
    except:
        return "ERROR"

def ShowBar(Bar):
    sys.stdout.write(Bar)
    sys.stdout.flush()

if __name__ == '__main__':
    print("This is a simple progress bar.\n")

    # Example #1:
    print('Example #1')
    Runs = 10
    for i in range(Runs + 1):
        progressBar = "\rProgress: " + ProgressBar(10, i, Runs)
        ShowBar(progressBar)
        time.sleep(1)

    # Example #2:
    print('\nExample #2')
    Runs = 10
    for i in range(Runs + 1):
        progressBar = "\rProgress: " + ProgressBar(10, i, 20, '|', '.')
        ShowBar(progressBar)
        time.sleep(1)

    print('\nDone.')

# Example #2:
Runs = 10
for i in range(Runs + 1):
    ProgressBar(10, i)
    time.sleep(1)

результат:

Это простой индикатор выполнения.

Пример 1

прогресс: [###-------] 30%

Пример #2

прогресс: [||||||||||||........] 60%

сделано.


Попробуйте PyProg. PyProg-это библиотека с открытым исходным кодом для Python для создания супер настраиваемых индикаторов прогресса и баров.

В настоящее время он находится в версии 1.0.2; он размещен на Github и доступен на PyPI (ссылки ниже). Он совместим с Python 3 & 2, и его также можно использовать с консолью Qt.

Это очень простой в использовании. Следующий код:

import pyprog
from time import sleep

# Create Object
prog = pyprog.ProgressBar(" ", "", 34)
# 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()

будет:

Initial State:
Progress: 0% --------------------------------------------------

When half done:
Progress: 50% #########################-------------------------

Final State:
Progress: 100% ##################################################

Я на самом деле сделал PyProg, потому что мне нужен простой, но супер настраиваемая библиотека прогресс-бар. Вы можете легко установить его с: pip install pyprog.

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


Это довольно просто в Python3:

   import time
   import math

    def show_progress_bar(bar_length, completed, total):
        bar_length_unit_value = (total / bar_length)
        completed_bar_part = math.ceil(completed / bar_length_unit_value)
        progress = "*" * completed_bar_part
        remaining = " " * (bar_length - completed_bar_part)
        percent_done = "%.2f" % ((completed / total) * 100)
        print(f'[{progress}{remaining}] {percent_done}%', end='\r')

    bar_length = 30
    total = 100
    for i in range(0, total + 1):
        show_progress_bar(bar_length, i, total)
        time.sleep(0.1)

    print('\n')

приведенный ниже код является довольно общим решением, а также имеет истекшее время и оставшееся время оценки. Вы можете использовать любой iterable с ним. Индикатор выполнения имеет фиксированный размер 25 символов, но он может отображать обновления в 1% шагов, используя полные, половинные и четвертные символы блока. Вывод выглядит следующим образом:

 18% |████▌                    | [0:00:01, 0:00:07]

код, например:

import sys, time
from numpy import linspace

def ProgressBar(iterObj, refreshTime=10):
  #refreshTime=10: refresh the time estimate at least every 10 sec.
  def SecToStr(sec):
    m, s = divmod(sec, 60)
    h, m = divmod(m,   60)
    return u'%d:%02d:%02d'%(h,m,s)
  L       = len(iterObj)
  steps   = {int(x):y for x,y in zip(np.linspace(0,L,  min(100,L),endpoint=False), 
                                     np.linspace(0,100,min(100,L),endpoint=False))}
  qSteps  = ['', u'\u258E',u'\u258C',u'\u258A'] # quarter and half block chars
  startT  = endT = time.time()
  timeStr = ' [0:00:00, -:--:--]'
  for nn,item in enumerate(iterObj):
    if nn in steps:
      done    = u'\u2588'*int(steps[nn]/4.0)+qSteps[int(steps[nn]%4)]
      todo    = ' '*(25-len(done))
      barStr  = u'%4d%% |%s%s|'%(steps[nn], done, todo)
      if nn>0:
        endT    = time.time()
        timeStr = ' [%s, %s]'%(SecToStr(endT-startT), SecToStr((endT-startT)*(L/float(nn)-1)))
      sys.stdout.write('\r'+barStr+timeStr); sys.stdout.flush()
    elif time.time()-endT > refreshTime:
      endT    = time.time()
      timeStr = ' [%s, %s]'%(SecToStr(endT-startT), SecToStr((endT-startT)*(L/float(nn)-1)))
      sys.stdout.write('\r'+barStr+timeStr); sys.stdout.flush()
    yield item
  barStr  = u'%4d%% |%s|'%(100, u'\u2588'*25)
  timeStr = ' [%s, 0:00:00]\n'%(SecToStr(time.time()-startT))
  sys.stdout.write('\r'+barStr+timeStr); sys.stdout.flush()

# Example
s = ''
for op in ProgressBar(list('Disassemble and reassemble this string')):
  time.sleep(0.5)
  s += op
print s

предложения по улучшению или другие комментарии приветствуются. Повеселись.


вы должны связать индикатор выполнения с задачей (чтобы он измерял прогресс: D). Например, если вы FTPing файл, вы можете сказать ftplib захватить буфер определенного размера, скажем, 128K, а затем добавить к вашему прогресс-бару любой процент размера файла 128k представляет. Если вы используете CLI, и ваш индикатор прогресса составляет 20 символов, вы добавите один символ, когда 1/20th файла будет передан.


@Massagran: он хорошо работает в моих программах. Кроме того, нам нужно добавить счетчик, чтобы указать время цикла. Этот счетчик играет роль аргумента метода update. Например: прочитайте все строки тестового файла и обработайте их на чем-нибудь. Предположим, что функция dosth() не относится к переменной i.

lines = open(sys.argv[1]).readlines()
i = 0
widgets=[Percentage(), Bar()]
pbar = ProgressBar(widgets=widgets,maxval=len(lines)).start()
pbar.start()
for line in lines:<pre>
    dosth();
    i += 1
    pbar.update(i)</pre>
pbar.finish()

переменная i контролирует состояние pbar методом update


вы также можете использовать просвети. Основное преимущество заключается в том, что вы можете одновременно войти в систему, не перезаписывая индикатор выполнения.

import time
import enlighten

manager = enlighten.Manager()
pbar = manager.counter(total=100)

for num in range(1, 101):
    time.sleep(0.05)
    print('Step %d complete' % num)
    pbar.update()

Он также обрабатывает несколько прогресс-баров.

import time
import enlighten

manager = enlighten.Manager()
odds = manager.counter(total=50)
evens = manager.counter(total=50)

for num in range(1, 101):
    time.sleep(0.05)
    if num % 2:
        odds.update()
    else:
        evens.update()