Как воспроизвести интерфейс скорости, подобный wget, на терминале Linux?

Wget очень четко выводит информацию о скорости на стандартный вывод, где скорость отображается и обновляется во время загрузки файла, а полоса прокручивается по экрану. Я хотел бы воспроизвести такой вывод в программе Python. Как это возможно?

Я думал, что библиотека curses должна это делать; вот что я придумал: -

import curses, time

class Speeder( object ):
    """Show and refresh time and download speed in a curses interface."""
    t1 = 0. # start time
    t  = 0. # current time
    tf = 0. # end time
    def start(self, filename=None ):
        """Start timer"""
        self.t1 = self.t = time.time()
        curses.use_env(True)
        curses.initscr()
        self.win = curses.newwin(4, 0)
        if filename is not None:
            self.win.addnstr(filename, 50 )
            self.win.refresh()

    def update(self, rbytes):
        """Refresh speed."""
        t = time.time()
        td = t - self.t
        self.t = t
        speed = rbytes / td 
        self.win.addstr(0,54,str('{0:.02f} B/s'.format(speed)))
        self.win.refresh()

    def end(self):
        """End timer"""
        self.tf = time.time() 
        curses.endwin()


try:
    speed = Speeder()
    speed.start(filename='foo.bar')
    for i in xrange(10):
        time.sleep(0.5)
        speed.update(200)
finally:
    speed.end()

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

Поэтому я просмотрел tty и termios, но я не могу найти примеры того, что я хочу сделать.

Наконец, я наткнулся на сообщение в блоге, которое делает это, просто записывая специальные символы в sys.stdout. Применив его код к моему классу Speeder, я пришел к следующему:

import sys
class Speeder( object ):
    """Show and refresh time and download speed."""
    t1 = 0. # start time
    t  = 0. # current time
    tf = 0. # end time
    def start(self, filename=None ):
        """Start timer"""
        self.t1 = self.t = time.time()
        if filename is not None:
            sys.stdout.write( '\r\x1b[K' + filename.ljust(50) )
            sys.stdout.flush()

    def update(self, rbytes):
        """Refresh speed."""
        t = time.time()
        td = t - self.t
        self.t = t
        speed = '{0} B/s'.format( rbytes / td )
        sys.stdout.write( "\r\x1b[K" + speed.rjust(65) )
        sys.stdout.flush()

    def end(self):
        """End timer"""
        self.tf = time.time()
        sys.stdout.write('\n')

В нем меньше строк кода, что мне нравится, но я понятия не имею, что происходит, или как изменить его, чтобы обновить только часть строки, подобно функции curses.addstr. В настоящее время имя файла перезаписывается пробелом. Я мог бы, конечно, изменить класс, чтобы каждый раз строить всю строку из python, но я хотел бы понять это, чтобы адаптировать его к своим потребностям.

Автор говорит, что это возможно только на терминалах VT100. Итак, я думаю, тогда это будет работать только на терминалах Unix? Это хорошо, но есть ли терминалы Unix, на которых это не работает?

Есть ли лучшая документация по этому вопросу или более сложные примеры (например, обновление нескольких строк, одной строки из многих или отдельных символов в строке), предпочтительно для Python? Это похоже не на функцию Python, а на tty, поэтому я думаю, что мне ничего не удалось найти. Есть ли справочная страница или что-то, что я должен прочитать?


person Alex Leach    schedule 08.06.2012    source источник


Ответы (2)


В приведенном выше коде используются escape-коды ANSI: https://en.wikipedia.org/wiki/ANSI_escape_code. Я думаю, что это документация, которую вы ищете, когда пытаетесь найти код, который вы нашли.

person Specksynder    schedule 08.06.2012
comment
Спасибо, теперь я начал ценить escape-коды ANSI и понял, как с их помощью перемещать курсор. Замена escape-кода в последнем методе update выше на "\033[15D" позволяет мне переместить курсор на 15 символов назад. Тогда я могу перезаписать то, что уже есть. Бум! - person Alex Leach; 08.06.2012
comment
Даю вам правильный ответ, чтобы помочь мне найти то, что кажется самым простым решением. - person Alex Leach; 08.06.2012

Вы можете взглянуть на Благословения, похоже, это именно то, что вам нужно:

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

person mensi    schedule 08.06.2012
comment
Благословение выглядит потрясающе! Пример кода, который он предоставляет особенно! Спасибо за рекомендацию - person Alex Leach; 08.06.2012
comment
Думал то же самое, когда он случайно появился на /r/Python некоторое время назад;) - person mensi; 08.06.2012