如何创建旋转的命令行光标?

2025-04-17 09:02:00
admin
原创
13
摘要:问题描述:有没有办法使用 Python 在终端中打印旋转光标?解决方案 1:类似这样,假设你的终端处理 \bimport sys import time def spinning_cursor(): while True: for cursor in '|/-\\': ...

问题描述:

有没有办法使用 Python 在终端中打印旋转光标?


解决方案 1:

类似这样,假设你的终端处理 \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('')

解决方案 2:

易于使用的 API(这将在单独的线程中运行微调器):

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

现在with在代码中的任意位置的块中使用它:

with Spinner():
  # ... some long-running operations
  # time.sleep(3) 

解决方案 3:

一个不错的 Python 方式是使用 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('')            # erase the last written char

此外,您可能希望使用线程在长函数调用期间显示微调器,如http://www.interclasse.com/scripts/spin.php

解决方案 4:

例子

为了完整性,我想添加halo这个很棒的软件包。它提供了许多预设的旋转器和更高级别的自定义选项。

摘自他们的自述文件

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()

解决方案 5:

解决方案:

import sys
import time

print "processing...\\\",
syms = ['\\', '|', '/', '-']
bs = ''

for _ in range(10):
    for sym in syms:
        sys.stdout.write("%s" % sym)
        sys.stdout.flush()
        time.sleep(.5)

关键是使用退格字符“\b”并刷新标准输出。

解决方案 6:

@Victor Moyseenko 改进的版本,因为原始版本几乎没有问题

  1. 旋转完成后,旋转器的角色会离开

  2. 有时也会导致删除以下输出的第一个字符

  3. 通过在输出上放置 threading.Lock() 来避免罕见的竞争条件

  4. 当没有可用的 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('')
                self.spinner_visible = False
                if cleanup:
                    sys.stdout.write(' ')       # overwrite spinner with blank
                    sys.stdout.write('
')      # 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('
')

上面 Spinner 类的使用示例:


with Spinner("just waiting a bit.. "):

        time.sleep(3)

将代码上传至https://github.com/Tagar/stuff/blob/master/spinner.py

解决方案 7:

漂亮、简单、干净……

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

解决方案 8:

当然可以。只需在四个字符之间打印一个退格符 ( ),就能让“光标”看起来像在旋转(-, `, |, /`)。

解决方案 9:

我在 GitHub 上找到了py-spin包。它有很多很棒的旋转样式。以下是一些使用示例,样式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"
{0}".format(spin.next()), end="")
    sys.stdout.flush()
    time.sleep(0.1)

其他风格见下面的 gif。

py-spin 包中的旋转样式。

解决方案 10:

抓住这个很棒的progressbar模块 - http://code.google.com/p/python-progressbar/
使用RotatingMarker

解决方案 11:

我构建了一个通用的单例,供整个应用程序共享

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("
{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("
", end="")

        print("
", end="")

if __name__ == '__main__':
    import time
    # Testing
    spinner = Spinner()
    spinner.start("Downloading")
    # Make actions
    time.sleep(5) # Simulate a process
    #
    spinner.stop()

解决方案 12:

你可以用`'
'`下面的命令清除当前行。以下是@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'
{next(spinner)} Downloading...'
    print(content, end="")
    time.sleep(0.1)
    print('
', 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 = `
${spinner.next().value} Downloading...`;
    process.stdout.write(content);
    await sleep(0.1);
    process.stdout.write('
');
  }
})();

解决方案 13:

curses 模块。我想看看 addstr() 和 addch() 函数。不过我从未用过。

解决方案 14:

对于更高级的控制台操作,在 unix 上您可以使用curses python 模块,在 windows 上,您可以使用WConio,它提供与 curses 库等效的功能。

解决方案 15:

#!/usr/bin/env python

import sys

chars = '|/-\\'

for i in xrange(1,1000):
    for c in chars:
        sys.stdout.write(c)
        sys.stdout.write('')
        sys.stdout.flush()

注意事项:
根据我的经验,这并非在所有终端上都有效。在 Unix/Linux 下,更稳健的方法是使用curses模块,尽管这种方法更复杂,但在 Windows 下无法使用。你可能需要通过后台实际处理来降低速度。

解决方案 16:

就是这样——简单而清晰。

import sys
import time

idx = 0
cursor = ['|','/','-','\\'] #frames of an animated cursor

while True:
    sys.stdout.write(cursor[idx])
    sys.stdout.write('')
    idx = idx + 1

    if idx > 3:
        idx = 0

    time.sleep(.1)

解决方案 17:

粗鲁但简单的解决方案:

import sys
import time
cursor = ['|','/','-','\\']
for count in range(0,1000):
  sys.stdout.write('{}'.format(cursor[count%4]))
  sys.stdout.flush()
  # replace time.sleep() with some logic
  time.sleep(.1)

存在明显的局限性,但同样很粗糙。

解决方案 18:

我提出使用装饰器的解决方案

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("
{message} {symbol}".format(message=message, symbol=symbol), end="")
            time.sleep(0.3)

    def stop():
        global spinner_event
        spinner_event = False
        print("
", 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)

解决方案 19:

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

这也可以是使用带有参数的函数的另一种解决方案。

解决方案 20:

我大约一周前刚开始学习 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('')
            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()

说了这么多,也写了这么多,但我才用 Python 做了一个星期。如果有更简洁的方法,或者我错过了一些最佳实践,我很乐意学习。谢谢。

解决方案 21:

非常简单:如果您知道最终指标,那么这也会打印进度和预计到达时间。

from datetime import datetime
import itertools

def progress(title: str, total):
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用