Моргает ASCII анимация на python
Решил Написать небольшой mp3 плеер для командной строки. Пока занимался анимацией возникла проблема - анимация моргает когда обновляется кадр. Вот видео: https://youtu.be/in4VLPOfzHw. А также код:
import time, os, glob, eyed3, math, sys
from colorama import init
from mutagen.mp3 import MP3
mpts = glob.glob('*.mp3')
dark_grey = '\033[1;30;40m'
light_grey = '\033[0;37;40m'
white = '\033[1;37;40m'
lime = '\033[1;32;40m'
red = '\033[0;31;40m'
i = 0
song_list = []
for mpt in mpts:
song = MP3(mpt)
duration = math.ceil(song.info.length)
m_duration = duration // 60
s_duration = duration % 60
song = eyed3.load(mpt)
name = song.tag.title
song_list.append([name, [m_duration, s_duration]])
init()
# draw
while True:
# cassette
res = ''
i += 1
res += light_grey + ' ■̅̅̅̅̅̅̅̅̅̅̅̅■ \n'
res += dark_grey + ' |'
res += light_grey + '|############|'
res += dark_grey + '| \n'
res += dark_grey + ' |'
res += light_grey + '|'
if i % 4 == 0:
res += white + ' (/)====(/) '
elif i % 4 == 1:
res += white + ' (-)====(-) '
elif i % 4 == 2:
res += white + ' (\\)====(\\) '
elif i % 4 == 3:
res += white + ' (|)====(|) '
res += light_grey + '|'
res += dark_grey + '| \n'
res += dark_grey + ' |'
res += light_grey + '|############|'
res += dark_grey + '|\n'
res += light_grey + ' ■____________■ \n'
# green line
res += lime + ' ___________________________________\n\n'
# song list
res += red + ' # NAME TIME\n'
for i1 in range(len(song_list)):
res += dark_grey + ' ' + str(i1+1) + '.'
res += white + ' ' + song_list[i1][0] + ' '*(28 - len(song_list[i1][0])) + f'{song_list[i1][1][0]}:{song_list[i1][1][1]}\n'
os.system('cls')
sys.stdout.write(res)
sys.stdout.flush()
time.sleep(0.4)
Возможно ли это как-то исправить, или же лучше попробовать написать на чем-то другом?
Ответы (1 шт):
Вместо полной очистки консоли через cls просто перемещайте курсор в левый верхний угол консоли и рисуйте каждый новый кадр поверх старого, тогда моргания не будет:
while True:
# cassette
res = ''
i += 1
res += light_grey + ' ■̅̅̅̅̅̅̅̅̅̅̅̅■ \n'
res += dark_grey + ' |'
res += light_grey + '|############|'
res += dark_grey + '| \n'
res += dark_grey + ' |'
res += light_grey + '|'
if i % 4 == 0:
res += white + ' (/)====(/) '
elif i % 4 == 1:
res += white + ' (-)====(-) '
elif i % 4 == 2:
res += white + ' (\\)====(\\) '
elif i % 4 == 3:
res += white + ' (|)====(|) '
res += light_grey + '|'
res += dark_grey + '| \n'
res += dark_grey + ' |'
res += light_grey + '|############|'
res += dark_grey + '|\n'
res += light_grey + ' ■____________■ \n'
# green line
res += lime + ' ___________________________________\n\n'
# song list
res += red + ' # NAME TIME\n'
for i1 in range(len(song_list)):
res += dark_grey + ' ' + str(i1+1) + '.'
res += white + ' ' + song_list[i1][0] + ' '*(28 - len(song_list[i1][0])) + f'{song_list[i1][1][0]}:{song_list[i1][1][1]}\n'
# os.system('cls')
print('\033[H', end='') # <--- Переместить курсор в левый верхний угол консоли
sys.stdout.write(res)
sys.stdout.flush()
time.sleep(0.4)
Также есть смысл рамку кассеты рисовать один раз, а в цикле перерисовывать только меняющуюся часть. В этом случае нужно перемещаться не в верхний левый угол, а на конкретную позицию:
# cassette frame
res = ''
res += light_grey + ' ■̅̅̅̅̅̅̅̅̅̅̅̅■ \n'
res += dark_grey + ' |'
res += light_grey + '|############|'
res += dark_grey + '| \n'
res += dark_grey + ' |'
res += light_grey + '| |'
res += dark_grey + '| \n'
res += dark_grey + ' |'
res += light_grey + '|############|'
res += dark_grey + '|\n'
res += light_grey + ' ■____________■ \n'
# green line
res += lime + ' ___________________________________\n\n'
print(res)
# draw
while True:
i += 1
# cassette wheels
print('\033[3;5H', end='') # Переместить курсор на 3 строку, 5 столбец
if i % 4 == 0:
print(white + '(/)====(/)')
elif i % 4 == 1:
print(white + '(-)====(-)')
elif i % 4 == 2:
print(white + '(\\)====(\\)')
elif i % 4 == 3:
print(white + '(|)====(|)')
# song list
res = '\033[8;1H' # Переместить курсор на 8 строку (ниже кассеты)
res += red + ' # NAME TIME\n'
for i1 in range(len(song_list)):
res += dark_grey + ' ' + str(i1+1) + '.'
res += white + ' ' + song_list[i1][0] + ' '*(28 - len(song_list[i1][0])) + f'{song_list[i1][1][0]}:{song_list[i1][1][1]}\n'
sys.stdout.write(res)
sys.stdout.flush()
time.sleep(0.4)
Информация о перемещении курсора из этого ответа: Move console cursor up
Дальше рекомендации по улучшению кода.
Вот эту строку можно сильно упростить:
# Было
res += white + ' ' + song_list[i1][0] + ' '*(28 - len(song_list[i1][0])) + f'{song_list[i1][1][0]}:{song_list[i1][1][1]}\n'
# Стало
res += white + f' {song_list[i1][0]} {song_list[i1][1][0]:>28}:{song_list[i1][1][1]}\n'
При итерации по последовательности со счетчиком вместо range принято использовать enumerate, escape-последовательности можно подставлять прямо в f-строку, получается такой цикл:
for i1, song in enumerate(song_list, start=1):
res += f'{dark_grey} {i1}. {white} {song[0]} {song[1][0]:>28}:{song[1][1]}\n'
Ну и если список песен не меняется, то его отображение тоже можно вынести до цикла while.