Как создать вращающийся курсор командной строки?
Есть ли способ напечатать вращающийся курсор в терминале, используя Python?
24 ответа
Примерно так, при условии, что ваш терминал обрабатывает \b
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')
Простой в использовании API (он запускает spinner в отдельном потоке):
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 start(self):
self.busy = True
threading.Thread(target=self.spinner_task).start()
def stop(self):
self.busy = False
time.sleep(self.delay)
В любом месте кода, где это необходимо:
spinner = Spinner()
spinner.start()
# ... some long-running operations
# time.sleep(3)
spinner.stop()
Хороший питонский способ - использовать itertools.cycle:
import itertools, sys
spinner = itertools.cycle(['-', '/', '|', '\\'])
while True:
sys.stdout.write(spinner.next()) # write the next character
sys.stdout.flush() # flush stdout buffer (actual character display)
sys.stdout.write('\b') # erase the last written char
Кроме того, вы можете использовать многопоточность для отображения счетчика во время длинного вызова функции, как в http://www.interclasse.com/scripts/spin.php
Для полноты картины хочу добавить отличный пакетный ореол. Он предлагает множество предустановленных спиннеров и параметры настройки более высокого уровня.
Выписка из их ридми
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()
В качестве альтернативы вы можете использовать halo с оператором Python with:
from halo import Halo
with Halo(text='Loading', spinner='dots'):
# Run time consuming work here
Наконец, вы можете использовать halo в качестве декоратора:
from halo import Halo
@Halo(text='Loading', spinner='dots')
def long_running_function():
# Run time consuming work here
pass
long_running_function()
Решение:
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)
Ключ должен использовать символ возврата \ 'b' и сбросить стандартный вывод.
Улучшенная версия от @Victor Moyseenko, поскольку в исходной версии было мало проблем
- покидал персонажей прядильщика после завершения вращения
- и иногда приводит к удалению первого символа следующего вывода
- позволяет избежать редкого состояния гонки, помещая threading.Lock() в вывод
- возвращается к более простому выводу, когда tty недоступен (без вращения)
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')
пример использования класса Spinner выше:
with Spinner("just waiting a bit.. "):
time.sleep(3)
загрузил код на https://github.com/Tagar/stuff/blob/master/spinner.py
Красиво, просто и чисто...
while True:
for i in '|\\-/':
print('\b' + i, end='')
Я нашел пакет py-spin на GitHub. У него много приятных стилей вращения. Вот несколько примеров того, как использовать,Spin1
это \-/
стиль:
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)
Также можно управлять отжимом вручную:
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)
Другие стили в гифке ниже.
Конечно, это возможно. Это просто вопрос печати символа возврата (\b
) между четырьмя символами, что бы "курсор" выглядел так, как будто он вращается (-
, \
, |
, /
).
Хватай удивительный progressbar
модуль - http://code.google.com/p/python-progressbar/ use RotatingMarker
,
Ты можешь написать
'\r\033[K'
чтобы очистить текущую строку. А ниже приведен пример, измененный на основе @nos.
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="")
Для всех, кто интересуется nodejs, я также пишу пример nodejs.
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');
}
})();
Я создал общий синглтон, общий для всего приложения
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()
модуль проклятий. Я бы взглянул на функции addstr() и addch(). Никогда не использовал это все же.
Для более сложных манипуляций с консолью в Unix вы можете использовать модуль Python curses, а в Windows вы можете использовать WConio, который обеспечивает эквивалентную функциональность библиотеки curses.
#!/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()
ПРЕДУПРЕЖДЕНИЯ. По моему опыту, это работает не во всех терминалах. Более надежный способ сделать это в Unix/Linux, будь он более сложным, - это использовать модуль curses, который не работает под Windows. Возможно, вы захотите немного замедлить его, как с реальной обработкой, которая происходит в фоновом режиме.
Предлагаю решение с помощью декораторов
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
Простое использование
@spinner("Downloading")
def f():
time.sleep(10)
Я только начал с python около недели назад и нашел эту публикацию. Я объединил кое-что из того, что нашел здесь, с тем, что узнал о потоках и очередях в других местах, чтобы обеспечить, на мой взгляд, гораздо лучшую реализацию. В моем решении запись на экран обрабатывается потоком, который проверяет очередь на предмет содержимого. Если в этой очереди есть содержимое, поток, вращающий курсор, знает, что нужно остановиться. С другой стороны, поток, вращающий курсор, использует очередь в качестве блокировки, чтобы поток печати знал, что не печатать, пока не будет завершен полный проход кода счетчика. Это предотвращает состояние гонки и много лишнего кода, который люди используют, чтобы поддерживать консоль в чистоте.
См. ниже:
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()
Сказав все, что я сказал, и написал все, что написал, я занимаюсь питоном всего неделю. Если есть более чистые способы сделать это или я пропустил некоторые передовые практики, я хотел бы изучить. Спасибо.
Вот и я - просто и понятно.
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)
Грубое, но простое решение:
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)
Есть очевидные ограничения, но опять же, грубые.
import sys
def DrowWaitCursor(self, 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')
Это может быть также другое решение, использующее функцию с параметром.
Что-то очень простое: если вы знаете окончательную метрику, то она также напечатает прогресс и время.
from datetime import datetime
import itertools
def progress(title: str, total):
moon = itertools.cycle(["", "", "", "", "", "", "", ""])
start_time = datetime.now()
def show(current):
curr_time = datetime.now()
time_taken = curr_time - start_time
print(" ", next(moon), title, current, "/", total,
" progress: ", round((100.0 * current) / total, 2), "%",
" eta: ", (time_taken * (total - current)) / current, end="\r ")
return show
если вы хотите текстовый счетчик на Python, вы можете посмотреть картинку
Простой:
print_spinner("Hayatta en hakiki mürşit ilimdir.")
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))
Здравствуйте, вот простейший счетчик загрузки для Python.
Время импорта
Spin=["загрузка ......", "|", "/","-", "\"]
Для i в Spin:Print("\b"+i, end= "")Time.sleep (0.2)
Выход: загрузка ...... |