Search code examples
pythonpython-3.xansi-escapestring.format

Is there a way to shorten an ANSI output or change it entirely for .format() spacing


I'm trying to add color to values in a formatted table, but the spacing is acting strange and spacing based off of the characters instead of the format spacing I input.
dictionary:

items = {
'staff'     : ['Staff',          20, 0,'Yes', 'None', 'None', 'None', ' ',  5,12],
'club'      : ['Club',           75, 0,'Yes', 'None', 'None', 'None', ' ', 15,22],
'mace'      : ['Mace',           200,0,'Yes', 'None', 'None', 'None', ' ', 30,371],
'warhammer' : ['Warhammer',      350,0,'Yes', 'None', 'None', 'None', ' ', 50,60]
}

argv = 'staff', 'club', 'mace', 'warhammer'

code in question:

def color(r, g, b, text):
    return str("\033[38;2;{};{};{}m{} \033[38;2;255;255;255m".format(r, g, b, text))
x = 0
i = 1
print("{:<0} {:<12} {:<10} {:<10} {:<6}".format(" ", "Item", "Cost", "Damage", "Owned"))
while x < i - 1:
    y = listing(x, argv, False, 0,1,8,9,3) #outputs a list from a dictionary
    if y[1] > player['coins']:
        y[1] = color(255,0,0,(y[1]))
    else:
        y[1] = color(0,255,0,(y[1]))
    z = options.get(x+1) #gets an arrow for selection, not important for this code
    w = {z:y}
    for k, v in w.items():
        item, cost, damagemin, damagemax, owned = v
        print("{:<0} {:<12} {:<6} {:>03d}-{:>03d}    {:<5}".format(k, item, cost, damagemin, damagemax, owned))
    x += 1

it outputs this without the coin color check.

  Item         Cost   Damage     Owned 
> Staff        20     005-012    Yes   
  Club         75     015-022    Yes   
  Mace         200    045-055    Yes   
  Warhammer    350    075-090    Yes   
  Back

But when I add the coin check to change the color, it outputs this:

  Item         Cost   Damage     Owned 
> Staff        20  005-012    Yes      
  Club         75  015-022    Yes      
  Mace         200  045-055    Yes     
  Warhammer    350  075-090    Yes     
  Back

I've checked what it changes the value of y[1] to and it returns '\x1b[38;2;0;255;0m20 \x1b[38;2;255;255;255m'
I'm at a complete loss for how to fix this.


Solution

  • The solution is

    while x < i - 1:
        y = listing(x, argv, False, 0,1,8,9,3)
        if y[1] > player['coins']:
            r,g,b = 255,0,0
        else:
            r,g,b = 0,255,0
        z = options.get(x+1)
        w = {z:y}
        for k, v in w.items():
            item, cost, damagemin, damagemax, owned = v
            print("{:<0} {:<12} \033[38;2;{};{};{}m{:<5} \033[38;2;255;255;255m {:>03d}-{:>03d}    {:<5}".format(k, item, r,g,b, cost, damagemin, damagemax, owned))
        x += 1
    

    After research on the .format() function of Python and the way Python reads it, there isn't a way (as far as I know, please correct me if I'm wrong) to fix this in a way for variable spacing amount in the "{}".format(). So due to that, I need to break apart \033[38;2;{};{};{}m{} \033[38;2;255;255;255m into its separate parts and manually insert them into the .format().
    The problem with the formatting is that m{} was effectively overriding the {:<6} in the "{}".format(). So in order to fix that, I broke \033[38;2;{};{};{}m{} \033[38;2;255;255;255m into \033[38;2;{};{};{}m{} and \033[38;2;255;255;255m. While this is less than optimal, it does fix the problem.

    In \033[38;2;{};{};{}m{}, the first 3 {} accept values 0-255 (in this specific case) while m{} accepted text. Since all of this is in the "".format(), all of the {} still accept formatting arguments. So after inserting the color code into the "".format().

    print("{:<0} {:<12} \033[38;2;{};{};{}m{:<5} \033[38;2;255;255;255m {:>03d}-{:>03d}    {:<5}".format(k, item, r,g,b, cost, damagemin, damagemax, owned))
    

    I added m{:<5} to format the text, fixing the problem entirely.

    Now to account for this, I changed

    if y[1] > player['coins']:
        y[1] = color(255,0,0,(y[1]))
    else:
        y[1] = color(0,255,0,(y[1]))
    

    Which ran the color function and returned an ANSI string that was not able to be formatted. Which then I changed to

    if y[1] > player['coins']:
        r,g,b = 255,0,0
    else:
        r,g,b = 0,255,0
    

    Which changes/adds new variables r,g,b that will be inserted directly into the .format() function.
    This effectively is just expanding the original color() function and inserting it directly into the .format().