2

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
Community
  • 1
  • 1
  • One quick observation is \e should be \033 like in your first BOLD definition. – Mark Tolonen Nov 27 '11 at 14:55
  • (a) If you go and implement your own, you're repeating work that others have done and made sure they've done properly. (b) Why do you even *want* to go doing the work again when you can use what they've implemented successfully? (sure, I've done it myself, too, but that's beside the point!) (c) If you care about Windows, don't even try writing your own; use colorama. – Chris Morgan Nov 27 '11 at 15:34
  • ah no, i dont care about windows at all. guess the main reason im asking is - will i be able to use some library and have that still work easily within something which is printing a character at a time. if i have to implement my own code to do that, then not much point using an outside source. plus it means installing extra code to run. and yeah :) its also for practice, just over a week doing python now. –  Nov 27 '11 at 15:37
  • possible duplicate of [Print in terminal with colors using python?](http://stackoverflow.com/questions/287871/print-in-terminal-with-colors-using-python) – Wooble Nov 27 '11 at 22:37
  • what do you just go marking duplicates without reading the posts ? :) im aware of that page actually, but thats not my problem, the problem stems from printing out characters one at a time. well, if nobody suggests a way im going to copy my clunky bash routines into python and then post back... maybe someone wants this as well. –  Nov 28 '11 at 01:39
  • update the script, if you're bored have a look :) –  Nov 29 '11 at 02:29
  • Ask another question rather than completely changing the title and content. The current answers are now completely out of context. – Mark Tolonen Nov 29 '11 at 06:59
  • yep, done and i tried to change it back a bit so if someone comes across it they wont be totally baffled :) –  Nov 29 '11 at 12:37

2 Answers2

1

Here are a few techniques to try:

  1. 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()]
    ]
    
  2. 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)
    
  3. 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()
    
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • oh you beauty. unfortunately cant thank you enough. beer and cup cakes for you. so far ive come across list comprehensions a few times, but they are totally new to me, bit confusing all these loops within loops. zero is false ! what ! i thought it was true ! im sure its true in bash, great, way to make it difficult to remember :) i dont really understand 2) ill have to hack it about and see whether it sinks in, but on 3) is this way faster than what i did ? i looked through randoms functions and kind of assumed a straight choice between two things would be faster than the rest. –  Nov 28 '11 at 04:24
  • 1
    In Python, zero empty strings, empty lists, empty dictionaries...all considered false for an `if`. Upvotes and green checks are thanks enough (*wink*). – Mark Tolonen Nov 28 '11 at 04:33
  • course. yeah thanks so much, ill have a great time playing with these concepts, and ill stick it back here in case anyone is as ocd as me. ah i was thinking bash exit codes. –  Nov 28 '11 at 04:35
  • You might check out http://www.diveintopython.net if you haven't found that site yet. – Mark Tolonen Nov 28 '11 at 04:37
  • dive into Python ? I hit me head on the bottom! –  Nov 28 '11 at 17:41
0

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.

Epcylon
  • 4,674
  • 2
  • 26
  • 35
  • firstly, thankyou for taking the time to answer. re: booleans, I didnt think of that. I think the main reason is because I havent got a clue what Im doing :) ah sorry no, i thought you meant the random bit. ah the reason im doing that is because i may have an arbitrary number of colour sequences. That first bit was just to test the colour printing which I didnt manage to get working, it just printed the escape codes to the terminal. yeah it has to recognise the escape sequences. I was trying to hack the recipe link for a bit but couldnt understand what it was doing. –  Nov 27 '11 at 20:23
  • so to clarify 0 would be off and anything other would be the number of the colour cycling sequence. –  Nov 27 '11 at 20:30