Gibt es eine Möglichkeit, einen sich drehenden Cursor in einem Terminal mit Python zu drucken?
Antworten:
So etwas unter der Annahme, dass Ihr Terminal \ b handhabt
import sys
import time
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
spinner = spinning_cursor()
for _ in range(50):
sys.stdout.write(next(spinner))
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write('\b')
spinner.next()
durch ersetzt wird next(spinner)
; Siehe stackoverflow.com/a/1073582/5025060 .
Einfach zu verwendende API (dies führt den Spinner in einem separaten Thread aus):
import sys
import time
import threading
class Spinner:
busy = False
delay = 0.1
@staticmethod
def spinning_cursor():
while 1:
for cursor in '|/-\\': yield cursor
def __init__(self, delay=None):
self.spinner_generator = self.spinning_cursor()
if delay and float(delay): self.delay = delay
def spinner_task(self):
while self.busy:
sys.stdout.write(next(self.spinner_generator))
sys.stdout.flush()
time.sleep(self.delay)
sys.stdout.write('\b')
sys.stdout.flush()
def __enter__(self):
self.busy = True
threading.Thread(target=self.spinner_task).start()
def __exit__(self, exception, value, tb):
self.busy = False
time.sleep(self.delay)
if exception is not None:
return False
Verwenden Sie es jetzt in einem with
Block an einer beliebigen Stelle im Code:
with Spinner():
# ... some long-running operations
# time.sleep(3)
float(delay)
wird erhöhen, wenn nicht schweben, sollte sein isinstance(delay, Number)
, mit Number
von numbers
.
Ein guter pythonischer Weg ist die Verwendung von itertools.cycle:
import itertools, sys
spinner = itertools.cycle(['-', '/', '|', '\\'])
while True:
sys.stdout.write(next(spinner)) # write the next character
sys.stdout.flush() # flush stdout buffer (actual character display)
sys.stdout.write('\b') # erase the last written char
Möglicherweise möchten Sie auch Threading verwenden, um den Spinner während eines langen Funktionsaufrufs anzuzeigen, wie in http://www.interclasse.com/scripts/spin.php
next(spinner)
anstelle vonspinner.next()
spinner = itertools.cycle('-/|\\')
.
Eine Lösung:
import sys
import time
print "processing...\\",
syms = ['\\', '|', '/', '-']
bs = '\b'
for _ in range(10):
for sym in syms:
sys.stdout.write("\b%s" % sym)
sys.stdout.flush()
time.sleep(.5)
Der Schlüssel besteht darin, das Rücktastezeichen '\ b' zu verwenden und stdout zu leeren.
Die verbesserte Version von @Victor Moyseenko als Originalversion hatte nur wenige Probleme
import sys
import threading
import itertools
import time
class Spinner:
def __init__(self, message, delay=0.1):
self.spinner = itertools.cycle(['-', '/', '|', '\\'])
self.delay = delay
self.busy = False
self.spinner_visible = False
sys.stdout.write(message)
def write_next(self):
with self._screen_lock:
if not self.spinner_visible:
sys.stdout.write(next(self.spinner))
self.spinner_visible = True
sys.stdout.flush()
def remove_spinner(self, cleanup=False):
with self._screen_lock:
if self.spinner_visible:
sys.stdout.write('\b')
self.spinner_visible = False
if cleanup:
sys.stdout.write(' ') # overwrite spinner with blank
sys.stdout.write('\r') # move to next line
sys.stdout.flush()
def spinner_task(self):
while self.busy:
self.write_next()
time.sleep(self.delay)
self.remove_spinner()
def __enter__(self):
if sys.stdout.isatty():
self._screen_lock = threading.Lock()
self.busy = True
self.thread = threading.Thread(target=self.spinner_task)
self.thread.start()
def __exit__(self, exception, value, tb):
if sys.stdout.isatty():
self.busy = False
self.remove_spinner(cleanup=True)
else:
sys.stdout.write('\r')
Beispiel für die Verwendung der obigen Spinner-Klasse:
with Spinner("just waiting a bit.. "):
time.sleep(3)
Code auf https://github.com/Tagar/stuff/blob/master/spinner.py hochgeladen
Der Vollständigkeit halber möchte ich das großartige Paket Halo hinzufügen . Es bietet viele voreingestellte Spinner und Anpassungsoptionen auf höherer Ebene.
from halo import Halo
spinner = Halo(text='Loading', spinner='dots')
spinner.start()
# Run time consuming work here
# You can also change properties for spinner as and when you want
spinner.stop()
Alternativ können Sie Halo mit Pythons with-Anweisung verwenden:
from halo import Halo
with Halo(text='Loading', spinner='dots'):
# Run time consuming work here
Schließlich können Sie Halo als Dekorateur verwenden:
from halo import Halo
@Halo(text='Loading', spinner='dots')
def long_running_function():
# Run time consuming work here
pass
long_running_function()
Sicher ist es möglich. Es ist nur eine Frage von Druck auf die Rück Zeichen ( \b
) zwischen den vier Zeichen, die den „Cursor“ schauen , wie es der Spinnen machen würden ( -
, \
, |
, /
).
Fluchmodul. Ich würde mir die Funktionen addstr () und addch () ansehen. Ich habe es aber nie benutzt.
Für erweiterte Konsolenmanipulationen können Sie unter Unix das Curses-Python-Modul und unter Windows WConio verwenden, das die gleiche Funktionalität wie die Curses-Bibliothek bietet.
Holen Sie sich das fantastische progressbar
Modul - http://code.google.com/p/python-progressbar/
verwenden RotatingMarker
.
Ich habe ein py-spin- Paket auf GitHub gefunden. Es hat viele schöne Spinnstile. Hier sind einige Beispiele zur Verwendung, Spin1
ist der \-/
Stil:
from __future__ import print_function
import time
from pyspin.spin import make_spin, Spin1
# Choose a spin style and the words when showing the spin.
@make_spin(Spin1, "Downloading...")
def download_video():
time.sleep(10)
if __name__ == '__main__':
print("I'm going to download a video, and it'll cost much time.")
download_video()
print("Done!")
time.sleep(0.1)
Es ist auch möglich, das Spin-Handbuch manuell zu steuern:
from __future__ import print_function
import sys
import time
from pyspin.spin import Spin1, Spinner
# Choose a spin style.
spin = Spinner(Spin1)
# Spin it now.
for i in range(50):
print(u"\r{0}".format(spin.next()), end="")
sys.stdout.flush()
time.sleep(0.1)
Andere Stile im folgenden GIF.
#!/usr/bin/env python
import sys
chars = '|/-\\'
for i in xrange(1,1000):
for c in chars:
sys.stdout.write(c)
sys.stdout.write('\b')
sys.stdout.flush()
CAVEATS: Nach meiner Erfahrung funktioniert dies nicht in allen Terminals. Eine robustere Möglichkeit, dies unter Unix / Linux zu tun, sei es komplizierter, ist die Verwendung des Curses- Moduls, das unter Windows nicht funktioniert. Sie möchten es wahrscheinlich etwas verlangsamen, wenn die eigentliche Verarbeitung im Hintergrund stattfindet.
Los geht's - einfach und klar.
import sys
import time
idx = 0
cursor = ['|','/','-','\\'] #frames of an animated cursor
while True:
sys.stdout.write(cursor[idx])
sys.stdout.write('\b')
idx = idx + 1
if idx > 3:
idx = 0
time.sleep(.1)
Grobe aber einfache Lösung:
import sys
import time
cursor = ['|','/','-','\\']
for count in range(0,1000):
sys.stdout.write('\b{}'.format(cursor[count%4]))
sys.stdout.flush()
# replace time.sleep() with some logic
time.sleep(.1)
Es gibt offensichtliche Einschränkungen, aber auch hier sind sie grob.
Ich schlage eine Lösung mit Dekorateuren vor
from itertools import cycle
import functools
import threading
import time
def spinner(message, spinner_symbols: list = None):
spinner_symbols = spinner_symbols or list("|/-\\")
spinner_symbols = cycle(spinner_symbols)
global spinner_event
spinner_event = True
def start():
global spinner_event
while spinner_event:
symbol = next(spinner_symbols)
print("\r{message} {symbol}".format(message=message, symbol=symbol), end="")
time.sleep(0.3)
def stop():
global spinner_event
spinner_event = False
print("\r", end="")
def external(fct):
@functools.wraps(fct)
def wrapper(*args):
spinner_thread = threading.Thread(target=start, daemon=True)
spinner_thread.start()
result = fct(*args)
stop()
spinner_thread.join()
return result
return wrapper
return external
Einfache Bedienung
@spinner("Downloading")
def f():
time.sleep(10)
Ich habe einen generischen Singleton erstellt, der von der gesamten Anwendung gemeinsam genutzt wird
from itertools import cycle
import threading
import time
class Spinner:
__default_spinner_symbols_list = ['|-----|', '|#----|', '|-#---|', '|--#--|', '|---#-|', '|----#|']
def __init__(self, spinner_symbols_list: [str] = None):
spinner_symbols_list = spinner_symbols_list if spinner_symbols_list else Spinner.__default_spinner_symbols_list
self.__screen_lock = threading.Event()
self.__spinner = cycle(spinner_symbols_list)
self.__stop_event = False
self.__thread = None
def get_spin(self):
return self.__spinner
def start(self, spinner_message: str):
self.__stop_event = False
time.sleep(0.3)
def run_spinner(message):
while not self.__stop_event:
print("\r{message} {spinner}".format(message=message, spinner=next(self.__spinner)), end="")
time.sleep(0.3)
self.__screen_lock.set()
self.__thread = threading.Thread(target=run_spinner, args=(spinner_message,), daemon=True)
self.__thread.start()
def stop(self):
self.__stop_event = True
if self.__screen_lock.is_set():
self.__screen_lock.wait()
self.__screen_lock.clear()
print("\r", end="")
print("\r", end="")
if __name__ == '__main__':
import time
# Testing
spinner = Spinner()
spinner.start("Downloading")
# Make actions
time.sleep(5) # Simulate a process
#
spinner.stop()
import sys
def DrowWaitCursor(counter):
if counter % 4 == 0:
print("/",end = "")
elif counter % 4 == 1:
print("-",end = "")
elif counter % 4 == 2:
print("\\",end = "")
elif counter % 4 == 3:
print("|",end = "")
sys.stdout.flush()
sys.stdout.write('\b')
Dies kann auch eine andere Lösung sein, die eine Funktion mit einem Parameter verwendet.
Sie können schreiben '\r\033[K'
, um die aktuelle Zeile zu löschen. Das folgende Beispiel wurde von @nos geändert.
import sys
import time
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
spinner = spinning_cursor()
for _ in range(1, 10):
content = f'\r{next(spinner)} Downloading...'
print(content, end="")
time.sleep(0.1)
print('\r\033[K', end="")
Für alle, die sich für nodejs interessieren, schreibe ich auch ein nodejs-Beispiel.
function* makeSpinner(start = 0, end = 100, step = 1) {
let iterationCount = 0;
while (true) {
for (const char of '|/-\\') {
yield char;
}
}
return iterationCount;
}
async function sleep(seconds) {
return new Promise((resolve) => {
setTimeout(resolve, seconds * 1000);
});
}
(async () => {
const spinner = makeSpinner();
for (let i = 0; i < 10; i++) {
content = `\r${spinner.next().value} Downloading...`;
process.stdout.write(content);
await sleep(0.1);
process.stdout.write('\r\033[K');
}
})();
Ich habe gerade vor ungefähr einer Woche mit Python angefangen und diesen Beitrag gefunden. Ich habe ein bisschen von dem, was ich hier gefunden habe, mit Dingen kombiniert, die ich an anderer Stelle über Threads und Warteschlangen gelernt habe, um meiner Meinung nach eine viel bessere Implementierung zu ermöglichen. In meiner Lösung wird das Schreiben auf den Bildschirm von einem Thread behandelt, der eine Warteschlange auf Inhalt überprüft. Wenn diese Warteschlange Inhalt enthält, kann der sich drehende Cursorthread anhalten. Auf der Rückseite verwendet der sich drehende Cursor-Thread eine Warteschlange als Sperre, sodass der Druck-Thread nicht drucken kann, bis der vollständige Durchlauf des Spinner-Codes abgeschlossen ist. Dies verhindert Rennbedingungen und viele überschüssige Codes, die verwendet werden, um die Konsole sauber zu halten.
Siehe unten:
import threading, queue, itertools, sys, time # all required for my version of spinner
import datetime #not required for spinning cursor solution, only my example
console_queue = queue.Queue() # this queue should be initialized before functions
screenlock = queue.Queue() # this queue too...
def main():
threading.Thread(target=spinner).start()
threading.Thread(target=consoleprint).start()
while True:
# instead of invoking print or stdout.write, we just add items to the console_queue
# The next three lines are an example of my code in practice.
time.sleep(.5) # wait half a second
currenttime = "[" + datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S") + "] "
console_queue.put(currenttime) # The most important part. Substitute your print and stdout functions with this.
def spinner(console_queue = console_queue, screenlock = screenlock):
spinnerlist = itertools.cycle(['|', '/', '-', '\\'])
while True:
if console_queue.empty():
screenlock.put("locked")
sys.stdout.write(next(spinnerlist))
sys.stdout.flush()
sys.stdout.write('\b')
sys.stdout.flush()
screenlock.get()
time.sleep(.1)
def consoleprint(console_queue = console_queue, screenlock = screenlock):
while True:
if not console_queue.empty():
while screenlock.empty() == False:
time.sleep(.1)
sys.stdout.flush()
print(console_queue.get())
sys.stdout.flush()
if __name__ == "__main__":
main()
Nachdem ich alles gesagt und geschrieben habe, was ich geschrieben habe, mache ich erst seit einer Woche Python-Sachen. Wenn es sauberere Möglichkeiten gibt oder ich einige Best Practices verpasst habe, würde ich gerne lernen. Vielen Dank.
import requests
import time
import sys
weathercity = input("What city are you in? ")
weather = requests.get('http://api.openweathermap.org/data/2.5/weather?q='+weathercity+'&appid=886705b4c1182eb1c69f28eb8c520e20')
url = ('http://api.openweathermap.org/data/2.5/weather?q='+weathercity+'&appid=886705b4c1182eb1c69f28eb8c520e20')
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
data = weather.json()
temp = data['main']['temp']
description = data['weather'][0]['description']
weatherprint ="In {}, it is currently {}°C with {}."
spinner = spinning_cursor()
for _ in range(25):
sys.stdout.write(next(spinner))
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write('\b')
convert = int(temp - 273.15)
print(weatherprint.format(weathercity, convert, description))
def spinning_cursor()...
und sys.stdout.write(next(spinner)) ....
] des Codes in der 9 Jahre alten Antwort von nos . Bitte kopieren Sie nicht (Teile) der Antworten anderer Personen und geben Sie sie als Ihre eigenen weiter. Und wenn Sie ältere Fragen beantworten, stellen Sie bitte sicher, dass Sie entweder eine andere Lösung oder zumindest eine wesentlich bessere Erklärung als die aktuellen Antworten angeben.
spinner = itertools.cycle(['-', '/', '|', '\\'])
anstatt eine Generatorfunktion (weniger ausführlich und mehr Wiederverwendung) wie hier