彩色终端库
我正在尝试在Python中实现文本的颜色循环效果……这个问题因为背景发生了很大变化,所以我重新编辑并提交了一个新问题。请查看这里。
这个问题主要是关于我应该使用哪个库 - 比如termcolor、colorama、curses,还有一个ansi颜色的示例。
到目前为止的代码:
#!/usr/bin/env python
'''
"arg" is a string or None
if "arg" is None : the terminal is reset to his default values.
if "arg" is a string it must contain "sep" separated values.
if args are found in globals "attrs" or "colors", or start with "@" \
they are interpreted as ANSI commands else they are output as text.
@* commands:
@x;y : go to xy
@ : go to 1;1
@@ : clear screen and go to 1;1
@[colour] : set foreground colour
^[colour] : set background colour
examples:
echo('@red') : set red as the foreground color
echo('@red ^blue') : red on blue
echo('@red @blink') : blinking red
echo() : restore terminal default values
echo('@reverse') : swap default colors
echo('^cyan @blue reverse') : blue on cyan <=> echo('blue cyan)
echo('@red @reverse') : a way to set up the background only
echo('@red @reverse @blink') : you can specify any combinaison of \
attributes in any order with or without colors
echo('@blink Python') : output a blinking 'Python'
echo('@@ hello') : clear the screen and print 'hello' at 1;1
colours:
{'blue': 4, 'grey': 0, 'yellow': 3, 'green': 2, 'cyan': 6, 'magenta': 5, 'white': 7, 'red': 1}
'''
'''
Set ANSI Terminal Color and Attributes.
'''
from sys import stdout
import random
import sys
import time
esc = '%s['%chr(27)
reset = '%s0m'%esc
format = '1;%dm'
fgoffset, bgoffset = 30, 40
for k, v in dict(
attrs = 'none bold faint italic underline blink fast reverse concealed',
colours = 'grey red green yellow blue magenta cyan white'
).items(): globals()[k]=dict((s,i) for i,s in enumerate(v.split()))
def echo(arg=None, sep=' ', end='\n', rndcase=True, txtspeed=0.03):
cmd, txt = [reset], []
if arg:
# split the line up into 'sep' seperated values - arglist
arglist=arg.split(sep)
# cycle through arglist - word seperated list
for word in arglist:
if word.startswith('@'):
### First check for a colour command next if deals with position ###
# go through each fg and bg colour
tmpword = word[1:]
if tmpword in colours:
cmd.append(format % (colours[tmpword]+fgoffset))
c=format % attrs[tmpword] if tmpword in attrs else None
if c and c not in cmd:
cmd.append(c)
stdout.write(esc.join(cmd))
continue
# positioning (starts with @)
word=word[1:]
if word=='@':
cmd.append('2J')
cmd.append('H')
stdout.write(esc.join(cmd))
continue
else:
cmd.append('%sH'%word)
stdout.write(esc.join(cmd))
continue
if word.startswith('^'):
### First check for a colour command next if deals with position ###
# go through each fg and bg colour
tmpword = word[1:]
if tmpword in colours:
cmd.append(format % (colours[tmpword]+bgoffset))
c=format % attrs[tmpword] if tmpword in attrs else None
if c and c not in cmd:
cmd.append(c)
stdout.write(esc.join(cmd))
continue
else:
for x in word:
if rndcase:
# thankyou mark!
if random.randint(0,1):
x = x.upper()
else:
x = x.lower()
stdout.write(x)
stdout.flush()
time.sleep(txtspeed)
stdout.write(' ')
time.sleep(txtspeed)
if txt and end: txt[-1]+=end
stdout.write(esc.join(cmd)+sep.join(txt))
if __name__ == '__main__':
echo('@@') # clear screen
#echo('@reverse') # attrs are ahem not working
print 'default colors at 1;1 on a cleared screen'
echo('@red hello this is red')
echo('@blue this is blue @red i can ^blue change @yellow blah @cyan the colours in ^default the text string')
print
echo()
echo('default')
echo('@cyan ^blue cyan blue')
# echo('@cyan ^blue @reverse cyan blue reverse')
# echo('@blue ^cyan blue cyan')
#echo('@red @reverse red reverse')
# echo('yellow red yellow on red 1')
# echo('yellow,red,yellow on red 2', sep=',')
# print 'yellow on red 3'
# for bg in colours:
# echo(bg.title().center(8), sep='.', end='')
# for fg in colours:
# att=[fg, bg]
# if fg==bg: att.append('blink')
# att.append(fg.center(8))
# echo(','.join(att), sep=',', end='')
#for att in attrs:
# echo('%s,%s' % (att, att.title().center(10)), sep=',', end='')
# print
from time import sleep, strftime, gmtime
colist='@grey @blue @cyan @white @cyan @blue'.split()
while True:
try:
for c in colist:
sleep(.1)
echo('%s @28;33 hit ctrl-c to quit' % c,txtspeed=0)
#echo('@yellow @6;66 %s' % strftime('%H:%M:%S', gmtime()))
except KeyboardInterrupt:
break
except:
raise
echo('@10;1')
print
2 个回答
这里有几个问题。首先,你为什么在colourc
变量中使用0和1,而不是True
和False
呢?如果用正确的布尔值,理解起来会简单得多。
在第一个if语句块中,如果colourc
不是0,你就把整个字符串写到标准输出(stdout)上。我很惊讶这并没有像我运行代码时那样打印出颜色。
当你尝试一次打印一个字符时,代码就出现问题了。ANSI转义序列不是单个字符,不能把它们当作单个字符来处理。比如说,你的随机大小写代码可能会破坏任何它碰到的ANSI序列,因为它会随机把序列中的m或K字符改成大写或小写。
我运行你的代码时,如果把rndcase=False
,即使是一次打印一个字符的代码也能正常工作。
你应该重新考虑如何在输入中设置颜色,这样当需要应用新颜色时,你可以先打印整个ANSI序列,然后再打印输出中的下一个字符。
这里有几个可以尝试的方法:
这一段代码创建了实际的转义字符串列表。它使用了一种叫做列表推导式的方式,来遍历颜色名称的列表,并在你的
colour
字典中查找对应的转义代码。.split()
只是一个简单的方法,可以快速生成字符串列表,而不需要输入很多引号和逗号。color_cycle = [ [colour[name] for name in 'bldylw bldred bldgrn bldblu txtwht'.split()], [colour[name] for name in 'txtblu txtcyn'.split()] ]
之后,你的函数可以通过创建一个迭代器来使用这些列表。这个特定的迭代器使用了一个标准库函数itertools.cycle,它可以无限重复一个序列。我在这里假设你想把字符串的每个字符都用不同的颜色来写。
import itertools # Create an iterator for the selected color sequence. if colourc: icolor = itertools.cycle(color_cycle[colourc - 1]) for a in stringy: # Write out the escape code for next color if colourc: color = next(icolor) sys.stdout.write(color)
这里还有另一种选择随机大小写的方法。在Python中,零被认为是假的:
if rndcase: if random.randint(0,1): a = a.upper() else: a = a.lower()