2011-02-04 9 views
53

Piszę prosty downloader ftp. Część z kodem jest coś takiego:wyjście do tej samej linii, nadpisując poprzednie dane wyjściowe? python (2.5)

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

Wołam proces funkcyjny do obsługi callback:

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

i wyjście jest coś takiego:

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

ale Chcę, aby wydrukować tę linię, a następnie ponownie wydrukować/odświeżyć go tak, aby wyświetlał się tylko raz, a ja zobaczę postęp tego pobierania ...

Jak to zrobić?

Odpowiedz

95

Oto kod dla Pythona 3.x:

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

end= kluczowe jest to, co działa tutaj - domyślnie print() kończy się na znak nowej linii (\n), ale można go zastąpić innym ciągiem znaków. W takim przypadku zakończenie linii znakiem powrotu karetki powoduje powrót kursora do początku bieżącej linii. W związku z tym nie ma potrzeby importowania modułu sys dla tego rodzaju prostego użytkowania. print() faktycznie ma a number of keyword arguments, które mogą być wykorzystane do znacznego uproszczenia kodu.

Aby korzystać z tego samego kodu w Pythonie 2.6+, umieścić następującą linię na początku pliku:

from __future__ import print_function 
+6

Zauważ, że funkcję drukowania można również używać w pythonie 2.6+, dodając następujący import na górze pliku: 'from __future__ import print_function'. – janin

+11

w pytonie <3.0 przecinek na końcu instrukcji uniemożliwi "\ n": 'print" foo ",' Jednak nadal musisz przepłukać, aby zobaczyć zmianę: 'sys.stdout.flush() ' –

+9

Znalazłem, że muszę dołączyć' \ r' na początku łańcucha i zamiast tego ustawić 'end = ''', aby to działało. Nie sądzę, że mój terminal lubi to, gdy kończę z '\ r' – Jezzamon

14

Spójrz na curses module documentation i curses module HOWTO.

Naprawdę prosty 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() 
+7

Niestety, curses jest dostępny tylko na systemie Unix.OP nie powiedział nam, do którego systemu operacyjnego jest skierowana jego aplikacja ... –

+2

Potrzebuję go dla Windows – Kristian

+0

Jestem tak przyzwyczajony do pracy pod Linuksem, że nawet nie zauważyłem ostrzeżenia o tym, że moduł jest tylko dla Uniksa w moduł docs .. Dzięki za wskazanie, że @Adrien. –

32

Jeśli wszystko, co chcesz zrobić, to zmienić jedną linię, użyj \r. \r oznacza powrót karetki. Jego efekt polega jedynie na przywróceniu karetki na początku bieżącej linii. Nic nie usuwa. Podobnie, można użyć \b, aby przejść o jeden znak do tyłu. (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) 
+1

Dodam, że '\ r' oznacza powrót karetki. jego efekt polega wyłącznie na przywróceniu karetki na początku bieżącej linii. nic nie usuwa. podobnie, '\ b' może być użyty do przejścia jednego znaku do tyłu. (niektóre terminale mogą nie obsługiwać wszystkich tych funkcji) –

+0

'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

+0

z tą poprawką napisałem to DZIAŁA ;-), dzięki – Kristian

9

Oto moja mała klasa, która może przedruk fragmentów tekstu. Poprawnie usuwa 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") 
+1

Nie będzie działać w systemie Windows do wersji Windows 10, ponieważ system Windows 10 to pierwsze okno obsługujące znaki sterujące https://en.wikipedia.org/wiki/ANSI_escape_code – user136036

5

Okazało się, że z prostej instrukcji print w Pythonie 2.7, wystarczy umieścić przecinek na końcu po '\r'.

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

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

+0

Python 2.7.10, a to nie drukuje niczego . Może, możesz skompilować go online i wysłać nam link, aby zobaczyć, jak działa ...? –

3

Możesz dodać "\ r" na końcu ciągu znaków plus przecinek na końcu funkcji drukowania. Na przykład:

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

To jest dla python 3? Jeśli tak, to przy domyślnym parametrze 'end' ustawionym na' \ n' , więc drukujesz '(...) KB pobrane! \ R \ n'. Czy się mylę? –

Powiązane problemy