2016-03-24 25 views
8

Używam potężnego edytora Sublime Text 3 na MacOSX do uruchamiania kodów Pythona. Chcę, aby wyświetlić pasek postępu w pętli for oraz następujące polecenie:Pasek postępu w Sublime Text z Pythonem

sys.stdout.write('\rProgress : %03.2f %%' % (100*float(i)/(N))) 
sys.flush() 

nie wyczyścić wcześniej wydrukowany wiersz w oknie wyjściowym zgodnie z oczekiwaniami (\r), ale produkuje N linie:

Progress : 0.25 % 
Progress : 0.50 % 
Progress : 0.75 % 
Progress : 1.00 % 
Progress : 1.25 % 
Progress : 1.50 % 
Progress : 1.75 % 
Progress : 2.00 % 
Progress : 2.25 % 
Progress : 2.50 % 
... 

Co nie jest zbyt miłe do przeczytania - wnioskuję, że okno wyjściowe może być tylko do odczytu.

Czy ktoś ma sugestie, aby poprawić wykorzystanie pasków postępu w Sublime Text?

+0

Nie jestem do końca pewien, o co pytasz - czy miałeś na myśli to, że ze względu na dużą liczbę linii wynik wygląda naprawdę "spamowo", powodując nieprzyjemne wyniki? – Jerrybibo

+0

Tak, miałem na myśli. Edytuję pytanie w celu wyjaśnienia. –

Odpowiedz

5

Przyjrzeniu sublime.py widzimy, że metoda flush faktycznie nic nie robi:

class _LogWriter: 
    def flush(self): 
     pass 

    def write(self, s): 
     sublime_api.log_message(s) 

sys.stdout = _LogWriter() 
sys.stderr = _LogWriter() 

Jednak nie polecam korzystania z konsoli do wyniki użytkownika i tak. Zwykle korzystasz z paneli wyjściowych/widoków lub komunikatów o stanie.

Komunikaty o stanie są łatwiejsze w użyciu, ale mniej skuteczne. SergioFC zademonstrował to w his answer.

Pokazuje to, jak korzystać z panelu wyjściowego. Jest bardzo elastyczny, ale musisz napisać własne polecenie tekstowe, aby wstawić tekst. Jest to konieczne, ponieważ potrzebny jest obiekt do edycji, aby zmienić zawartość widoku.

import sublime 
import sublime_plugin 


class MyInsertProgressBarCommand(sublime_plugin.TextCommand): 
    def run(self, edit, value): 
     view = self.view 
     width, _ = view.viewport_extent() 
     em_width = view.em_width() 
     # the number of columns are the width divided by the width of a char 
     # subtract two to add a little border 
     columns = int(width/em_width) - 2 

     # subtract two, because we surround it with [ and ] 
     bar_length = columns - 2 
     # calculate the size of the filled and the remaining part 
     filled_length = int(bar_length * value/100) 
     remaining_length = bar_length - filled_length 
     # assemble the string for the progress bar 
     text = "[{0}{1}]\n".format("=" * filled_length, "." * remaining_length) 
     # add the text for the percentages 
     if value >= 100: 
      percentage_text = "finished!" 
     else: 
      percentage_text = "{:3.2f} %".format(value) 
     text += " " * (columns - len(percentage_text)) + percentage_text 

     # replace the content of the view 
     view.replace(edit, sublime.Region(0, view.size()), text) 
     # reset sels 
     view.sel().clear() 
     view.sel().add(sublime.Region(0, 0)) 


class ProgressBarCommand(sublime_plugin.WindowCommand): 
    def run(self): 
     self.window.create_output_panel("progess_bar") 
     self.window.run_command("show_panel", {"panel": "output.progess_bar"}) 

     def test_progress_bar(): 
      import random 
      test_progress_bar.value += 2 * random.random() 
      if test_progress_bar.value >= 100: 
       self.finish_progress() 
       return 
      self.show_progress(test_progress_bar.value) 

      sublime.set_timeout(test_progress_bar, 100) 
     test_progress_bar.value = 0 

     sublime.set_timeout_async(test_progress_bar, 1) 

    def show_progress(self, progess): 
     view = self.window.find_output_panel("progess_bar") 
     view.run_command("my_insert_progress_bar", {"value": progess}) 

    def finish_progress(self): 
     self.show_progress(100) 
     sublime.set_timeout(self._destroy, 5000) 

    def _destroy(self): 
     self.window.destroy_output_panel("progess_bar") 

Wyjście:

Progress bar

+0

Fajne rozwiązanie:) Nie pomyślałbym o tym. Pracowałem z pokoleniem [__unicode generation] (https://github.com/Enteleform/Presentations/tree/master/Writing%20Your%20Own%20Sublime%20Text%20Plugins/2015-05-26#parkour) przez pewien czas , ale twoja metoda daje mi wgląd w pracę z animowanym tekstem. 'mdpopups' jest całkiem fajny również dla efektów wizualnych, po prostu wypróbowałem go po raz pierwszy w [__my answer__] (http://stackoverflow.com/a/36234685/4955183) – Enteleform

+1

@Enteleform Thanks =) Ja też nie byłem świadomy, że w widoku można wyświetlić okno HTML. 'mdpopups' wydaje się być bardzo miłym dodatkiem do tej funkcji. –

+0

Gdzie powinienem podać kod dla MyInsertProgressBarCommand - czy mogę dodać go do mojego projektu, czy jest to coś, co powinienem dodać w folderze Sublime? Jeśli jest to rozszerzenie Sublime, gdzie powinno być połączenie z tym modułem? Podaj również przykład tego, jak wywołać te metody z pętli, której postępy mierzymy. Dzięki. –

2

To, czego możesz szukać, to sposób na zachowanie wyników w wielu liniach. Możesz drukować \b (znak Backspace) tyle razy, ile było wcześniej wydrukowanych znaków. Napisałem to jako przykład:

(Python 2.7.6)

from __future__ import print_function 
import time, sys 

for i in range(1, 6): 
    print(i, end='') 
    sys.stdout.flush() 
    time.sleep(0.5) 
    print('\b', end='') 

spróbuj uruchomić, które można dostosować go do swoich potrzeb.

+1

Dziękuję za tę odpowiedź, ale twoja propozycja jest bardzo bliska mojej pierwszej próbie (używałem '\ r', karetki powrotnej). To rozwiązanie działa dobrze w terminalu, ale nie działa w wewnętrznym oknie wyjściowym Sublime Text. Oto wynik twojego rozwiązania: '12345 [Finished in 2.6s]'. –

+0

Och, przykro mi, do tej pory nie zdawałem sobie sprawy, że mówisz o panelu wyjściowym w Sublime Text. W sesji terminalowej będzie to wydrukować 1, zastąpić go 2, zastąpić 3, itd. – thedouglenz

1

Niestety, nie jest to możliwe w panelu wyników Sublime. Panel nie jest prawdziwą konsolą ani terminalem, a między innymi różnice nie interpretują sekwencji interpretacji, takich jak \r i (). Jeśli chcesz zobaczyć, jak dokładnie działa, zainstaluj PackageResourceViewer, a następnie otwórz Packages/Default/exec.py.

Aby to zadziałało, musisz utworzyć nowy build system, aby uruchomić go w Terminalu. Z powodu kaprysów OS X, musisz utworzyć dwa pliki. Pierwszym z nich jest skryptem powłoki:

#!/bin/sh 
osascript -e '  
    on run parameters   
    tell application "Terminal"    
     activate    
     do script with command "/path/to/python " & parameters   
    end tell  
    end run 
' [email protected] 

Zmień /path/to rzeczywistą ścieżkę do python (lub python3). Zapisz go, gdzie chcesz, jako PythonTerminal.sh. Następnie wybierz Tools -> Build System -> New Build System i wklej następujący:

{ 
    "cmd": ["/bin/sh /path/to/Python3Terminal.sh \"$file\""], 
    "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)", 
    "selector": "source.python", 
    "shell": true 
} 

znowu zmiany /path/to do rzeczywistej ścieżki do PythonTerminal.sh. Zapisz plik jako Packages/User/PythonTerminal.sublime-build (powinien automatycznie otworzyć właściwy katalog podczas zapisywania).

koniec wybierz Tools -> Build System -> PythonTerminal, przejdź do pliku Python i budować z B. Otworzy się nowe okno terminala i powinien zostać uruchomiony pasek postępu.

4

Jako alternatywne rozwiązanie można użyć paska stanu. Po ustawieniu komunikatu paska stanu poprzedni tekst zostanie wyczyszczony. Kontrola pakietów używa również paska stanu podczas instalowania pakietów.

Przykład:

Sublime text progress bar using status bar

import sublime, sublime_plugin 
import time 

class ExampleCommand(sublime_plugin.WindowCommand): 
    def run(self, args): 
     sublime.set_timeout_async(self.test,1) 

    def test(self): 
     i=80 
     while i <= 100: 
      sublime.status_message('%03.2f %%' % i) 
      time.sleep(0.15) 
      i+=0.25 
     sublime.status_message('100% Stackoverflow!') 
+0

Dzięki człowieku! Jest to również przyjemna implementacja, prawdopodobnie bardziej przydatna dla czegoś, co działa asynchronicznie, nie wymagając tak dużej ilości wyraźnego odniesienia wzrokowego. Może powiadomienie 'mdpopup' po zakończeniu? :) – Enteleform

+1

Fajnie :) Jeśli chodzi o nagrodę, nagrodzę ją w niedzielę lub poniedziałek (ze względu na zasady stackoverfow muszę poczekać 24 godziny przed przyznaniem nagrody) – sergioFC

+0

Awesome, nie zdawałem sobie sprawy, że był bounty. Bardzo doceniane:) – Enteleform

5

Można tworzyć wizualny pasek postępu przy użyciu:

 



Demo:

Demo

 



Kod:

@ GitHub

(uruchomić plugin wpisując Progress Bar Demo @ palety poleceń)

   



Notatki:

Istnieje , który kontroluje styl mdpopups. Z jakiegoś powodu właściwość color nie ma żadnego wpływu.

Ponadto parametr location przyjmuje wartość -1, aby wyskakujące okienko było ustawione w pozycji dozoru. W przeciwnym razie nie jestem pewien, w jaki sposób location wpływa na wyskakujące okienko, ponieważ przyjmuje tylko jedną liczbę całkowitą.  

 

ja zapytałem o te kwestie w wątku:

[Proof Of Concept] Visual Progress Bar

 

+1

Wow, dobra robota. Nawet nie wiedziałem, że to możliwe. Chcę spróbować "mdpopups" – sergioFC

+1

To wygląda naprawdę świetnie! Jestem całkiem początkującym (przepraszam), ale jeśli dobrze rozumiem: to może być używane jako plugin Sublime Text, ale czy można go dołączyć do zewnętrznego/osobistego kodu Pythona? –

+0

@ Léonard: Skrypt, który napisałem, jest przeznaczony do integracji z wtyczkami SublimeText. Myślę, że jedynym sposobem śledzenia postępów w regularnym kodzie Pythona byłoby podejście bardziej zbliżone do linii [__r-stein's answer__] (http://stackoverflow.com/a/36234502/4955183). Oczywiście musielibyśmy go zmienić, aby wykluczyć aspekty specyficzne dla SublimeText i przekonwertować jego funkcjonalność na wyjście konsoli. – Enteleform

2

Można użyć biblioteki Progressbar. znajduje się tutaj: https://pypi.python.org/pypi/progressbar/2.3-dev

Ponadto można zainstalować go z easy_install typu tylko: easy_install progressbar

Przykład użycia:

jeśli chcesz prosty progressbar z obecnie informacji na temat funkcji:

from progressbar import * 
from time import sleep 

progress = ProgressBar() 
for i in progress(range(80)): 
    sleep(0.01) 

indziej jeśli chcesz uzyskać pasek postępu z informacjami o funkcji:

from progressbar import * 
from time import sleep 

widgets = ['Something: ', Percentage(), ' ', Bar(marker=RotatingMarker()), 
      ' ', ETA(), ' ', FileTransferSpeed()] 
pbar = ProgressBar(widgets=widgets, maxval=10000000).start() 
for i in range(1000000): 
    # do something 
    pbar.update(10*i+1) 
    sleep(0.000001) 
pbar.finish()