Wie erstelle ich einen sich drehenden Befehlszeilencursor?

Antworten:

80

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')
nr
quelle
13
Vielleicht möchten Sie verwenden, spinner = itertools.cycle(['-', '/', '|', '\\'])anstatt eine Generatorfunktion (weniger ausführlich und mehr Wiederverwendung) wie hier
Damien
7
Beachten Sie, dass mit Python 3 spinner.next()durch ersetzt wird next(spinner); Siehe stackoverflow.com/a/1073582/5025060 .
CODE-REaD
49

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 withBlock an einer beliebigen Stelle im Code:

with Spinner():
  # ... some long-running operations
  # time.sleep(3) 
Victor Moyseenko
quelle
6
Das ist wirklich schön, bis Sie einen Fehler in Ihrem Code finden, der nicht behandelt wird. Dann können Sie es nicht zwingen aufzuhören.
DDuffy
float(delay)wird erhöhen, wenn nicht schweben, sollte sein isinstance(delay, Number), mit Numbervon numbers.
Levsa
Hey Chef, ich bin ziemlich amateurhaft, aber das scheint wirklich schön zu sein. Können Sie mir ein Beispiel für "with Spinner ()" geben? Ich verstehe es anscheinend nicht ganz. @ Victor Moyseenko
Mike_Leigh
41

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

Damien
quelle
4
Verwenden Sie gemäß dem Kommentar von CODE-REaD in der anderen Antwort in Python 3 next(spinner)anstelle vonspinner.next()
Siyh
2
Einfacher zu bedienen spinner = itertools.cycle('-/|\\').
Martineau
1
@martineau Prägnanter, aber wohl weniger verständlich.
Noctis Skytower
1
@ NoctisSkytower: Wohl.
Martineau
es läuft für immer, ich habe es mit Threading versucht, es läuft immer noch verrückt, loz. Wenn es ein Thread3 ist, wie kann ich ihn mit t1 Starts laufen lassen und mit t2 Endungen stoppen? irgendwelche Hinweise?
Mike_Leigh
11

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.

Nathan
quelle
6

Die verbesserte Version von @Victor Moyseenko als Originalversion hatte nur wenige Probleme

  1. hat die Charaktere des Spinners verlassen, nachdem das Spinnen abgeschlossen ist
  2. und manchmal dazu führen, dass auch das erste Zeichen der folgenden Ausgabe entfernt wird
  3. Vermeidet eine seltene Race-Bedingung, indem threading.Lock () auf die Ausgabe gesetzt wird
  4. fällt auf eine einfachere Ausgabe zurück, wenn kein tty verfügbar ist (kein Drehen)
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

Tagar
quelle
4

Schön, einfach und sauber ...

while True:
    for i in '|\\-/':
        print('\b' + i, end='')
Jakeup
quelle
4

Beispiel

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.

Auszug aus ihrer Readme

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()
Hans
quelle
3

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 ( -, \, |, /).

Klaus Byskov Pedersen
quelle
1

Fluchmodul. Ich würde mir die Funktionen addstr () und addch () ansehen. Ich habe es aber nie benutzt.

MattiaG
quelle
1

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.

David
quelle
1

Ich habe ein py-spin- Paket auf GitHub gefunden. Es hat viele schöne Spinnstile. Hier sind einige Beispiele zur Verwendung, Spin1ist 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.

Spinstile im Py-Spin-Paket.

eng.mrgh
quelle
0
#!/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.


quelle
0

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)
David Philipe Gil
quelle
0

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.

mbunch
quelle
0

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)
Dorcioman
quelle
0

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()
Dorcioman
quelle
0
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.

user3297919
quelle
0

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');
  }
})();

Donghua Liu
quelle
0

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.

virtual-ed
quelle
-1
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))

quelle
Bitte sei nett zu mir. Ich bin neu hier. ich bin 12 Jahre alt. Ich kann also nicht logisch denken, aber das wird dir definitiv helfen.
Hier erhalten Sie die Temperatur in Celsius. Bitte überprüfen Sie, wie es war. indem Sie hier einen Kommentar
1
Dies ist ein genaues Kopieren / Einfügen [ 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.
Mohnish