Sortie sur la même ligne écrasant la sortie précédente?

97

J'écris un téléchargeur FTP. Une partie du code est quelque chose comme ceci:

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

J'appelle le processus de fonction pour gérer le rappel:

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

et la sortie est quelque chose comme ceci:

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

mais je veux qu'il imprime cette ligne et la réimprime / actualise la prochaine fois pour qu'il ne l'affiche qu'une seule fois et je verrai la progression de ce téléchargement.

Comment ceci peut être fait?

Kristian
la source
Duplication de la barre de progression du texte dans la console .
Alexey
Copie possible de la barre de progression
Alexey

Réponses:

189

Voici le code pour Python 3.x:

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

Le end=mot-clé est ce qui fait le travail ici - par défaut, print()se termine par un caractère newline ( \n), mais cela peut être remplacé par une chaîne différente. Dans ce cas, terminer la ligne par un retour chariot ramène le curseur au début de la ligne courante. Ainsi, il n'est pas nécessaire d'importer le sysmodule pour ce type d'utilisation simple. print()a en fait un certain nombre d'arguments de mots clés qui peuvent être utilisés pour simplifier considérablement le code.

Pour utiliser le même code sur Python 2.6+, placez la ligne suivante en haut du fichier:

from __future__ import print_function
Kudzu
la source
6
Notez que la fonction d'impression peut également être utilisé en python 2.6+ en ajoutant l'importation suivante en haut du fichier: from __future__ import print_function.
janin le
12
en python <3.0 une virgule à la fin de l'instruction empêchera un "\ n": print "foo",Cependant, vous devez encore vider après cela pour voir le changement:sys.stdout.flush()
Tobias Domhan
31
J'ai trouvé que je devais inclure le \rau début de la chaîne, et définir à la end=''place pour que cela fonctionne. Je ne pense pas que mon terminal aime ça quand je termine avec\r
Jezzamon
2
Dans toutes les versions actuelles de Python 3, vous pouvez ajouter flush=Trueà la print()fonction pour vous assurer que la mémoire tampon est vidée (la mémoire tampon de ligne standard ne se viderait que lorsqu'une \nnouvelle ligne est écrite).
Martijn Pieters
4
Dans un ordinateur portable jupyter, en utilisant le \rau début et a end=''fonctionné le mieux et le plus facilement. Utiliser flush=Trueavec \rà la fin si la chaîne end='\r'fonctionnait également mais n'était pas lisse et effaçait la ligne et son saut de ligne.
Dave X
40

Si tout ce que vous voulez faire est de changer une seule ligne, utilisez \r. \rsignifie retour chariot. Son effet est uniquement de remettre le curseur au début de la ligne actuelle. Cela n'efface rien. De même, \bpeut être utilisé pour reculer d'un caractère. (certains terminaux peuvent ne pas prendre en charge toutes ces fonctionnalités)

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
la source
1
j'ajouterais que cela \rsignifie le retour chariot. son effet est uniquement de remettre le curseur au début de la ligne courante. il n'efface rien. de même, \bpeut être utilisé pour reculer d'un caractère. (certains terminaux peuvent ne pas prendre en charge toutes ces fonctionnalités)
Adrien Plisson
sys.stdout.write('%s\r', size_str')Je pense que vous vouliez dire sys.stdout.write('%s\r' % size_str)mais cela ne fonctionne pas.
Kristian le
@Adrien: Cool, a ajouté ça. @Kristian: Merci, mis à jour, il était assez tard.
Sam Dolan le
Notez également que vous pouvez toujours utiliser à la printplace de sys.stdout.writesi vous préférez: print size_str + "\r",fonctionne bien. Le ,à la fin de l'instruction d'impression supprime la nouvelle ligne; sys.stdout.flush()est encore nécessaire
Jeff
19

Jetez un œil à la documentation du module curses et au HOWTO du module curses .

Exemple vraiment basique:

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
la source
10
malheureusement, curses n'est disponible que sous Unix. l'OP ne nous a pas dit quel système d'exploitation son application cible ...
Adrien Plisson le
J'ai tellement l'habitude de travailler sous Linux que je n'ai même pas remarqué l'avertissement concernant le module étant uniquement pour UNIX dans la documentation du module. Merci de l'avoir signalé, @Adrien.
Andrea Spadaccini le
3
@AdrienPlisson, je sais que cette question a été posée il y a des années, mais vous pouvez réellement obtenir des malédictions sur Windows: lfd.uci.edu/~gohlke/pythonlibs/#curses
Cold Diamondz
Quand je l'ai collé dans mon interpréteur Python, mon terminal est devenu fubar. Sortir de l'interprète n'a pas aidé. WTF?!
allyourcode le
Utilisation clrtoeolpour quand la deuxième ligne est plus courte que la première.
Serge Stroobandt
9

Voici ma petite classe qui peut réimprimer des blocs de texte. Il efface correctement le texte précédent afin que vous puissiez écraser votre ancien texte avec un nouveau texte plus court sans créer de désordre.

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
la source
2
Cela ne fonctionnera pas sous Windows jusqu'à Windows 10, car Windows 10 est la première fenêtre à prendre en charge ces caractères de contrôle en.wikipedia.org/wiki/ANSI_escape_code
user136036
8

J'ai trouvé que pour une simple instruction print en python 2.7, il suffit de mettre une virgule à la fin après votre '\r'.

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

C'est plus court que les autres solutions non-python 3, mais aussi plus difficile à maintenir.

Matt Ellen
la source
1
Python 2.7.10, et il n'imprime rien. Peut-être que vous pouvez le compiler en ligne et nous envoyer un lien pour voir comment cela fonctionne ...?
Shougo Makishima
5

Vous pouvez simplement ajouter '\ r' à la fin de la chaîne plus une virgule à la fin de la fonction d'impression. Par exemple:

print(os.path.getsize(file_name)/1024+'KB / '+size+' KB downloaded!\r'),
Moustafa Saleh
la source
1
C'est pour python 3? Si tel est le cas, avec le endparamètre par défaut défini sur \ndonc u imprimer (...)KB downloaded!\r\n. Ai-je tort?
SR
Utiliser à la end = '\r'place corrige le problème dans Python 3.
Abhimanyu Pallavi Sudhir
4

J'utilise spyder 3.3.1 - windows 7 - python 3.6 bien que le flush ne soit pas nécessaire. basé sur cette publication - 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
la source
1

pour écraser la ligne précédente en python, tout ce dont vous avez besoin est d'ajouter end = '\ r' à la fonction d'impression, testez cet exemple:

import time
for j in range(1,5):
   print('waiting : '+j, end='\r')
   time.sleep(1)
Amirouche Zeggagh
la source