Reputation:
I'm trying to implement colour cycling on my text in Python... This question has been edited and resubmitted as another question because of a massive change in context. please see here instead.
this question was more about what library I should use - termcolor, colorama, curses and an ansi colour recipe,
code so far:
#!/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
Upvotes: 2
Views: 1013
Reputation: 177800
Here are a few techniques to try:
This block of code creates lists of the actual escape strings. It uses a list comprehension to iterate over the list of color names and look up the escape codes in your colour
dictionary. The .split()
is just a lazy way to create a list of strings without typing lots of quote-comma-quote sequences.
color_cycle = [
[colour[name] for name in 'bldylw bldred bldgrn bldblu txtwht'.split()],
[colour[name] for name in 'txtblu txtcyn'.split()]
]
Later, your function can use these lists by creating an iterator. This particular iterator uses a standard library function itertools.cycle, which repeats a sequence indefinitely. I'm making an assumption here that you want to write each character of the string in a different color.
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)
Here's another way to select random case. In Python zero is considered false:
if rndcase:
if random.randint(0,1):
a = a.upper()
else:
a = a.lower()
Upvotes: 1
Reputation: 4727
There are a couple problems here. First of all, why are you using 0 and 1 instead of True
and False
on the colourc variable? It's much easier to tell what's happening if you use proper booleans.
In the first if-block, if colourc
is not 0, you write the entire string to stdout. I am surprised this doesn't actually print with colors, as it does when I run the code.
When it comes to printing one character at a time, this is where your code comes into problems. The ANSI escape sequences are not single characters, and can't be treated as such. For one thing, your random case code could clobber any ANSI-sequences it happens to hit, by randomly upper or lowercasing the m or K characters in the sequence.
When I run your code, but with rndcase=False
, even the single character at a time code works fine.
You should re-think how you set the colors in your input, so that when a new color should take effect, you can print the entire ANSI-sequence, followed by the next character in the output.
Upvotes: 0