Ausgabe in dieselbe Zeile, die die vorherige Ausgabe überschreibt?


94

Ich schreibe einen FTP-Downloader. Ein Teil des Codes ist ungefähr so:

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

Ich rufe den Funktionsprozess auf, um den Rückruf zu behandeln:

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

und die Ausgabe ist ungefähr so:

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

Ich möchte jedoch, dass diese Zeile gedruckt und beim nächsten Mal erneut gedruckt / aktualisiert wird, sodass sie nur einmal angezeigt wird und der Fortschritt dieses Downloads angezeigt wird.

Wie geht das?



Antworten:


185

Hier ist Code für Python 3.x:

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

Das end=Schlüsselwort ist das, was hier funktioniert - standardmäßig print()endet es mit einem newline ( \n) - Zeichen, das jedoch durch eine andere Zeichenfolge ersetzt werden kann. In diesem Fall setzt das Beenden der Zeile mit einem Wagenrücklauf stattdessen den Cursor an den Anfang der aktuellen Zeile. Daher ist es nicht erforderlich, das sysModul für diese einfache Verwendung zu importieren . print()hat tatsächlich eine Reihe von Schlüsselwortargumenten, mit denen Code erheblich vereinfacht werden kann.

Um denselben Code unter Python 2.6+ zu verwenden, setzen Sie die folgende Zeile oben in die Datei:

from __future__ import print_function

6
Beachten Sie, dass die Druckfunktion auch in Python 2.6+ verwendet werden kann, indem der folgende Import oben in die Datei eingefügt wird : from __future__ import print_function.
Janin

12
In Python <3.0 verhindert ein Komma am Ende der Anweisung ein "\ n": print "foo",Sie müssen jedoch danach noch spülen, um die Änderung zu sehen:sys.stdout.flush()
Tobias Domhan

29
Ich stellte fest, dass ich das \ram Anfang der Zeichenfolge einfügen und end=''stattdessen festlegen musste , damit dies funktioniert. Ich glaube nicht, dass es meinem Terminal gefällt, wenn ich mit\r
Jezzamon

2
In allen aktuellen Python 3-Versionen können Sie flush=Truedie print()Funktion erweitern, um sicherzustellen, dass der Puffer \ngeleert wird (der Standardzeilenpuffer wird nur geleert, wenn eine neue Zeile geschrieben wird).
Martijn Pieters

4
In einem Jupyter-Notebook mit dem \ram Anfang und am end=''besten und reibungslosesten funktioniert. Verwenden Sie flush=Truemit \ram Ende, wenn die Zeichenfolge und end='\r'auch funktioniert hat, aber nicht glatt war und die Linie und ihren Zeilenvorschub gelöscht hat.
Dave X

40

Wenn Sie nur eine einzelne Zeile ändern möchten, verwenden Sie \r. \rbedeutet Wagenrücklauf. Der Effekt besteht ausschließlich darin, das Caret wieder an den Anfang der aktuellen Zeile zu setzen. Es löscht nichts. Ebenso \bkann verwendet werden, um ein Zeichen rückwärts zu gehen. (Einige Terminals unterstützen möglicherweise nicht alle diese Funktionen.)

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)

1
Ich würde hinzufügen, dass \rdies Wagenrücklauf bedeutet. Der Effekt besteht ausschließlich darin, das Caret wieder an den Anfang der aktuellen Zeile zu setzen. es löscht nichts. In ähnlicher Weise \bkann ein Zeichen rückwärts gehen. (Einige Terminals unterstützen möglicherweise nicht alle diese Funktionen)
Adrien Plisson

sys.stdout.write('%s\r', size_str')Ich denke du sys.stdout.write('%s\r' % size_str)meintest , aber es funktioniert nicht.
Kristian

@ Adrien: Cool, fügte das hinzu. @Kristian: Danke, aktualisiert, es war ziemlich spät.
Sam Dolan

Beachten Sie auch, dass Sie weiterhin verwenden können, printanstatt, sys.stdout.writewenn Sie es vorziehen: print size_str + "\r",funktioniert gut. Die ,Anweisung am Ende der print-Anweisung unterdrückt den Zeilenumbruch. sys.stdout.flush()wird noch benötigt
Jeff

19

Schauen Sie sich die Dokumentation zum Curses-Modul und das HOWTO des Curses-Moduls an .

Wirklich einfaches Beispiel:

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

10
Flüche sind leider nur unter Unix verfügbar. Das OP hat uns nicht mitgeteilt, auf welches Betriebssystem seine Anwendung abzielt ...
Adrien Plisson

Ich bin es so gewohnt, unter Linux zu arbeiten, dass ich in den Moduldokumenten nicht einmal die Warnung bemerkt habe, dass das Modul nur für UNIX ist. Vielen Dank für den Hinweis, @Adrien.
Andrea Spadaccini

3
@AdrienPlisson, ich weiß, dass diese Frage vor Jahren gestellt wurde, aber Sie können tatsächlich Flüche auf Windows bekommen: lfd.uci.edu/~gohlke/pythonlibs/#curses
Cold Diamondz

Als ich dies in meinen Python-Interpreter einfügte, wurde mein Terminal fubar. Das Verlassen des Dolmetschers half nicht. WTF?!
Allyourcode

Verwendung clrtoeolfür , wenn die zweite Linie kürzer ist als die erste.
Serge Stroobandt

9

Hier ist meine kleine Klasse, die Textblöcke nachdrucken kann. Der vorherige Text wird ordnungsgemäß gelöscht, sodass Sie Ihren alten Text mit kürzerem neuen Text überschreiben können, ohne dass es zu Unordnung kommt.

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

2
Das wird nicht funktionieren in Windows bis Windows 10, da Windows 10 die ersten Fenster ist diese Steuerzeichen zu unterstützen en.wikipedia.org/wiki/ANSI_escape_code
user136036

8

Ich habe festgestellt, dass für eine einfache print-Anweisung in Python 2.7 einfach ein Komma am Ende nach Ihrem steht '\r'.

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

Dies ist kürzer als bei anderen Nicht-Python-3-Lösungen, aber auch schwieriger zu warten.


1
Python 2.7.10, und es druckt nichts. Vielleicht können Sie es online kompilieren und uns einen Link senden, um zu sehen, wie es funktioniert ...?
Shougo Makishima

5

Sie können einfach '\ r' am Ende der Zeichenfolge und ein Komma am Ende der Druckfunktion hinzufügen. Beispielsweise:

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

1
Dies ist für Python 3? Wenn ja, was ist mit dem Standardparameter so endeingestellt, \ndass Sie drucken (...)KB downloaded!\r\n. Liege ich falsch?
SR

Die Verwendung von end = '\r'stattdessen behebt das Problem in Python 3.
Abhimanyu Pallavi Sudhir

4

Ich verwende Spyder 3.3.1 - Windows 7 - Python 3.6, obwohl möglicherweise kein Flush erforderlich ist. basierend auf diesem Beitrag - 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()

1

Um die vorherige Zeile in Python zu überschreiben, müssen Sie lediglich end = '\ r' zur Druckfunktion hinzufügen. Testen Sie dieses Beispiel:

import time
for j in range(1,5):
   print('waiting : '+j, end='\r')
   time.sleep(1)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.