Criando indicador de progresso giratório no console (Python)
Overview
Bem-vindo ao fascinante mundo da automação de tarefas com Python! Neste post, você vai aprender a dizer adeus ao monótono efeito Pacman em seus scripts de automação e dar as boas-vindas a um estiloso cursor giratório. Acompanhe-me nesta jornada por códigos simples e eficazes, e prepare-se para elevar suas habilidades de codificação ao próximo nível.
Uma das grandes vantagens do Python é a facilidade para criar scripts de automatização de tarefas repetitivas. É comum estes scripts possuírem um indicador de progresso (exemplo: Processando registro n de 1000). O problema disso é que você pode cair no efeito Pacman e ficar colocando . (pontos) para indicar que algo está sendo feito. Isso fica visualmente feio, alem de atrapalhar a visualização das mensagens, caso o processamento seja longo.
Para resolver isso, vou mostrar como fazer um cursor que fica girando, indicando que algo está sendo processado.
Para exemplificar o efeito pacman, segue um gif
Este é o efeito pacman: Você vai acumulando os pontos e isso vai ficando cada vez mais confuso na hora de ler…
O objeto deste post é mostrar como criar um cursors que fique girando, exemplo:
Fica bem mais limpo, certo?
O exemplo é bem simples e vai ser uma função que você vai chamando em um loop e ela se encarrega de atualizar o cursor e a mensagem.
Pode até ser que já exista uma biblioteca/pacote que faça isso, mas desconheço. De toda forma, é bom aprender como este tipo de abordagem funciona. O desenvolvimento desta função utilizará o pacote sys, então não precisamos instalar nada de novo.
Vamos começar pelo “segredo” do processo. No exemplo:
print("Hi", end="")
print("\rThere", end="")
print("\rBacon")
O resultado no console será:
Bacon
Process finished with exit code 0
Onde foram parar as outras mensagens (“Hi” e “There”)? Bom, elas foram exibidas no console, mas foram imediatamente substituídas por causa do caractere especial \r, que é o return carriage, responsável por voltar o cursor para o início da linha.
No exemplo, utilizei a função print com o argumento end="", indicando que uma string vazia será adicionada ao final de cada mensagem, ao invés da quebra de linha (que é o padrão da função). Para explicar o código de exemplo:
- A mensagem “Hi” foi exibida no console sem quebra de linha;
- O cursor retornou para o inicio da linha (\r) e a mensagem “There” foi exibida (também sem quebra de linha);
- Novamente, o caractere \r foi utilizado e o cursor retornou para o inicio da linha.
- A mensagem “Bacon” foi exibida. (Como foi o último print, não coloquei o end="", pois não faria diferença)
Bem simples, certo?
Para fazer cursor que fica girando, vou assumir o mesmo padrão de mensagens que está no gif: [<cursor>] mensagem.
A primeira coisa que precisamos é da sequencia de caracteres que serão utilizados:
CURSOR_POSITIONS = ('\\', '|', '/', '-')
CURRENT_CURSOR_POS = 0
Agora, para facilitar a vida, precisamos de uma função que, sempre que acionada, vai retornar a próxima posição do cursor:
def _get_next_cursor_():
global CURRENT_CURSOR_POS
try:
CURRENT_CURSOR_POS += 1
return CURSOR_POSITIONS[CURRENT_CURSOR_POS]
except:
CURRENT_CURSOR_POS = 0
return CURSOR_POSITIONS[CURRENT_CURSOR_POS]
Esta função tenta retornar a próxima posição do tuple criado com os caracteres (CURSOR_POSITIONS). Um exception é indicação de que o cursor está na última posição, então a variável CURRENT_CURSOR_POS, que armazena a posição atual do cursor, é definida para zero, retornando a “animação” para o início.
O próximo (e último) passo é criar a função que mostra o cursor girando + mensagem:
def spinning_cursor_with_label(label_text):
sys.stdout.write('\r[{}]\t{}'.format(_get_next_cursor_(), label_text))
sys.stdout.flush()
Explicando o funcionamento dela:
- A mensagem que será exibida é formatada, colocando na primeira posição (primeiro {}) o caractere referente ao cursor e na segunda posição, a mensagem;
- É feita uma chamada para a função write (stdout.write), informando a mensagem que será exibida. Note que ela começa com um \r, ou seja, antes de exibir a mensagem, o cursor sempre vai retornar para o inicio da linha;
- A função flush (stdout.flush) é chamada para forçar o buffer que guarda as mensagens a exibi-las na tela;
Qual a diferença entre o print e o stdout.write? A grosso modo, o print é um encapsulamento da função stdout.write.
Pronto. Você tem um cursor que gira e não precisa mais cair no efeito pacman!
Para fazer o exemplo exibido no gif:
for i in range(25):
spinning_cursor_with_label(label_text="Processing thing {}...".format(i))
Este exemplo está no meu Github.
Espero ter ajudado!