Wyjście do tej samej linii nadpisując poprzednie wyjście?

102

Piszę program do pobierania FTP. Część kodu wygląda mniej więcej tak:

ftp.retrbinary("RETR " + file_name, process)

Wzywam proces funkcji do obsługi wywołania zwrotnego:

def process(data):
    print os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!'
    file.write(data)

a wynik wygląda mniej więcej tak:

1784  KB / KB 1829 downloaded!
1788  KB / KB 1829 downloaded!
etc...   

ale chcę, aby wydrukowało tę linię i następnym razem ponownie wydrukowało / odświeżyło ją, aby wyświetliło się tylko raz i zobaczę postęp pobierania.

Jak można to zrobić?

Kristian
źródło
Możliwy duplikat paska postępu tekstu w konsoli
Alexey

Odpowiedzi:

200

Oto kod dla Pythona 3.x:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!', end='\r')

Tutaj end=działa słowo kluczowe - domyślnie print()kończy się znakiem nowej linii ( \n), ale można go zastąpić innym ciągiem. W takim przypadku zakończenie wiersza znakiem powrotu karetki powoduje zamiast tego powrót kursora na początek bieżącego wiersza. Dlatego nie ma potrzeby importowania sysmodułu do tego rodzaju prostego użycia. print()w rzeczywistości ma wiele argumentów słów kluczowych których można użyć do znacznego uproszczenia kodu.

Aby użyć tego samego kodu w Pythonie 2.6+, umieść następujący wiersz na górze pliku:

from __future__ import print_function
Kudzu
źródło
6
Należy pamiętać, że funkcja drukowania może być również stosowany w Pythonie 2.6+ dodając następujący import na początku pliku: from __future__ import print_function.
Janin,
12
w pythonie <3.0 przecinek na końcu instrukcji zapobiegnie "\ n": print "foo",Jednak nadal musisz spłukać po tym, aby zobaczyć zmianę:sys.stdout.flush()
Tobias Domhan
32
Zauważyłem, że muszę dołączyć \rznak na początku ciągu i end=''zamiast tego ustawić, aby działało. Myślę, że mój terminal nie lubi, kiedy kończę\r
Jezzamon
3
We wszystkich aktualnych wydaniach Pythona 3 możesz dodać flush=Truedo print()funkcji, aby upewnić się, że bufor zostanie opróżniony (standardowy bufor linii zostanie opróżniony tylko wtedy, gdy \nzostanie zapisany znak nowej linii).
Martijn Pieters
4
W notebooku jupyter, używanie \rna początku end=''działało najlepiej i płynnie. Używanie flush=Truez \rna końcu, jeśli ciąg i end='\r'również działał, ale nie był gładki, i usunął linię i jej wysunięcie.
Dave X
40

Jeśli chcesz tylko zmienić jedną linię, użyj \r. \roznacza powrót karetki. Jego efektem jest wyłącznie umieszczenie karetki z powrotem na początku bieżącej linii. Nic nie kasuje. Podobnie, \bmożna użyć do cofnięcia się o jeden znak. (niektóre terminale mogą nie obsługiwać wszystkich tych funkcji)

import sys

def process(data):
    size_str = os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!'
    sys.stdout.write('%s\r' % size_str)
    sys.stdout.flush()
    file.write(data)
Sam Dolan
źródło
1
dodałbym, że \roznacza to powrót karetki. jego efekt polega wyłącznie na umieszczeniu karetki z powrotem na początku bieżącej linii. to niczego nie wymazuje. podobnie, \bmoże posłużyć do cofnięcia się o jeden znak. (niektóre terminale mogą nie obsługiwać wszystkich tych funkcji)
Adrien Plisson
sys.stdout.write('%s\r', size_str')Myślę, że miałeś na myśli, sys.stdout.write('%s\r' % size_str)ale to nie działa.
Kristian
@Adrien: Super, dodał. @Kristian: Dzięki, zaktualizowałem, było dość późno.
Sam Dolan
Pamiętaj również, że możesz nadal używać printzamiast, sys.stdout.writejeśli wolisz: print size_str + "\r",działa dobrze. Na ,końcu instrukcji print usuwa znak nowej linii; sys.stdout.flush()jest nadal potrzebny
Jeff
19

Zajrzyj do dokumentacji modułu curses i modułu curses HOWTO .

Naprawdę podstawowy przykład:

import time
import curses

stdscr = curses.initscr()

stdscr.addstr(0, 0, "Hello")
stdscr.refresh()

time.sleep(1)

stdscr.addstr(0, 0, "World! (with curses)")
stdscr.refresh()
Andrea Spadaccini
źródło
10
niestety curses jest dostępne tylko w systemie Unix. OP nie powiedział nam, który system operacyjny jest przeznaczony dla jego aplikacji ...
Adrien Plisson
Jestem tak przyzwyczajony do pracy pod Linuksem, że nawet nie zauważyłem ostrzeżenia o tym, że moduł jest przeznaczony tylko dla systemu UNIX w dokumentacji modułu. Dzięki za zwrócenie uwagi, @Adrien.
Andrea Spadaccini
3
@AdrienPlisson, wiem, że to pytanie zostało zadane lata temu, ale tak naprawdę możesz pobrać Curses na Windows: lfd.uci.edu/~gohlke/pythonlibs/#curses
Cold Diamondz
Kiedy wkleiłem to do mojego interpretera Pythona, mój terminal stał się fubar. Wyjście z tłumacza nie pomogło. WTF ?!
allyourcode
Zastosowanie clrtoeoldla gdy druga linia jest krótsza niż pierwsza.
Serge Stroobandt
9

Oto moja mała klasa, która może ponownie drukować bloki tekstu. Prawidłowo czyści poprzedni tekst, dzięki czemu można zastąpić stary tekst krótszym nowym tekstem bez tworzenia bałaganu.

import re, sys

class Reprinter:
    def __init__(self):
        self.text = ''

    def moveup(self, lines):
        for _ in range(lines):
            sys.stdout.write("\x1b[A")

    def reprint(self, text):
        # Clear previous text by overwritig non-spaces with spaces
        self.moveup(self.text.count("\n"))
        sys.stdout.write(re.sub(r"[^\s]", " ", self.text))

        # Print new text
        lines = min(self.text.count("\n"), text.count("\n"))
        self.moveup(lines)
        sys.stdout.write(text)
        self.text = text

reprinter = Reprinter()

reprinter.reprint("Foobar\nBazbar")
reprinter.reprint("Foo\nbar")
Bouke Versteegh
źródło
2
To nie zadziała w systemie Windows do Windows 10, ponieważ Windows 10 jest pierwszym oknem obsługującym te znaki sterujące en.wikipedia.org/wiki/ANSI_escape_code
user136036
8

Zauważyłem, że w przypadku prostej instrukcji print w Pythonie 2.7 po prostu umieść przecinek na końcu po '\r'.

print os.path.getsize(file_name)/1024, 'KB / ', size, 'KB downloaded!\r',

Jest to krótsze niż w przypadku innych rozwiązań innych niż Python 3, ale także trudniejsze w utrzymaniu.

Matt Ellen
źródło
1
Python 2.7.10 i nic nie drukuje. Może możesz skompilować go online i wysłać nam link, aby zobaczyć, jak to działa ...?
Shougo Makishima
5

Możesz po prostu dodać „\ r” na końcu ciągu oraz przecinek na końcu funkcji print. Na przykład:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!\r'),
Moustafa Saleh
źródło
1
To jest dla Pythona 3? Jeśli tak, co z domyślnym endparametrem ustawionym na \nso u print (...)KB downloaded!\r\n. Czy się mylę?
SR
Użycie end = '\r'zamiast tego rozwiązuje problem w Pythonie 3.
Abhimanyu Pallavi Sudhir
4

Używam Spyder 3.3.1 - Windows 7 - Python 3.6, chociaż flush może nie być potrzebny. na podstawie tego postu - https://github.com/spyder-ide/spyder/issues/3437

   #works in spyder ipython console - \r at start of string , end=""
import time
import sys
    for i in range(20):
        time.sleep(0.5)
        print(f"\rnumber{i}",end="")
        sys.stdout.flush()
JoePythonKing
źródło
1

aby nadpisać poprzednią linię w Pythonie, wystarczy dodać end = '\ r' do funkcji print, przetestuj ten przykład:

import time
for j in range(1,5):
   print('waiting : '+j, end='\r')
   time.sleep(1)
Amirouche Zeggagh
źródło