From fa1837f71ff6ac31a9ea7d9d575d183fd3e96a70 Mon Sep 17 00:00:00 2001 From: joy Date: Sun, 26 Dec 2021 00:15:25 -0800 Subject: [PATCH] add color to botany Add example ANSI-colored ASCII art to "art" directory Add ansi_render() to menu_screen.py to render ANSI-colored ASCII art Fall back on non-colored art if no colored art exists Only single escape codes for setting foreground color are supported. Escape sequences with multiple codes (e.g. "ESC[1;33m") are unsupported. Unsupported escape codes reset colors to normal. --- art/ficus1.ansi | 10 ++++++ art/ficus2.ansi | 10 ++++++ art/ficus3.ansi | 10 ++++++ art/jackolantern.ansi | 10 ++++++ art/rip.ansi | 10 ++++++ art/seed.ansi | 10 ++++++ art/seedling.ansi | 10 ++++++ menu_screen.py | 73 ++++++++++++++++++++++++++++++++++++------- 8 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 art/ficus1.ansi create mode 100644 art/ficus2.ansi create mode 100644 art/ficus3.ansi create mode 100644 art/jackolantern.ansi create mode 100644 art/rip.ansi create mode 100644 art/seed.ansi create mode 100644 art/seedling.ansi diff --git a/art/ficus1.ansi b/art/ficus1.ansi new file mode 100644 index 0000000..7b129c1 --- /dev/null +++ b/art/ficus1.ansi @@ -0,0 +1,10 @@ + & \ & & + &\|,/ |/& && + &|/& / & & + \ { |___/_& + { {/ / & + `, \{______/_& + } }{ \_& + }{{ +. , , -=-~{ .-^- _ _ . + ^ ' diff --git a/art/ficus2.ansi b/art/ficus2.ansi new file mode 100644 index 0000000..497ea56 --- /dev/null +++ b/art/ficus2.ansi @@ -0,0 +1,10 @@ + &&&\/& &&& + &\|,/ |/& && + &&/ / /_&& && + \ { |_____/_& + { / / & & &&& + `, \{___________/_&& + } }{ &\____& + }{{ `&\&& + {}{ && +. , , -=-~{ .-^- _ _ . diff --git a/art/ficus3.ansi b/art/ficus3.ansi new file mode 100644 index 0000000..eb12ec7 --- /dev/null +++ b/art/ficus3.ansi @@ -0,0 +1,10 @@ + &*&\/& *&& + &\|,/ |/& *& + *&/ / /_&& && + \ { |_____/_* + {* / / & & *&& + `, \{___________/_*& + } }{ *\____& + }{{ `&\*& + {}{ && +. , , -=-~{ .-^- _ _ . diff --git a/art/jackolantern.ansi b/art/jackolantern.ansi new file mode 100644 index 0000000..1d395d0 --- /dev/null +++ b/art/jackolantern.ansi @@ -0,0 +1,10 @@ + /)) HAPPY + __(((__ HALLOWEEN + .' _`""`_`'. + / /\\ /\\ \ + | /)_\\/)_\\ | + | _ _()_ _ | + | \\/\\/\\// | + \ \/\/\/\/ / + . , .'.___..___.' _ ., _ . + ^ ' ` ' diff --git a/art/rip.ansi b/art/rip.ansi new file mode 100644 index 0000000..9a08f84 --- /dev/null +++ b/art/rip.ansi @@ -0,0 +1,10 @@ + ______________ + / \ + | | + | | + | R.I.P. | + | | + | | + | | +. |, _\/ .. \. \ /,|_ . + ^ ' ` ' diff --git a/art/seed.ansi b/art/seed.ansi new file mode 100644 index 0000000..d63d978 --- /dev/null +++ b/art/seed.ansi @@ -0,0 +1,10 @@ + + + + + + + + +. , _ . . _ , _ ., _ . + ^ ' o ` ' diff --git a/art/seedling.ansi b/art/seedling.ansi new file mode 100644 index 0000000..9d7fac2 --- /dev/null +++ b/art/seedling.ansi @@ -0,0 +1,10 @@ + + + + + + + . ; + \| +. , _ . ., l, _ ., _ . + ^ ' ` ' diff --git a/menu_screen.py b/menu_screen.py index 441593b..d4bad9a 100644 --- a/menu_screen.py +++ b/menu_screen.py @@ -57,7 +57,6 @@ class CursedMenu(object): self.show(["water","look","garden","visit", "instructions"], title=' botany ', subtitle='options') def define_colors(self): - # TODO: implement colors # set curses color pairs manually curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE) curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLACK) @@ -68,6 +67,21 @@ class CursedMenu(object): curses.init_pair(7, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(8, curses.COLOR_CYAN, curses.COLOR_BLACK) + ESC_CODE_TO_PAIR = { + '' : 0, # normal + '0' : 0, # normal + '30' : 1, # black + '31' : 7, # red + '32' : 3, # green + '33' : 6, # yellow + '34' : 4, # blue + '35' : 5, # magenta + '36' : 8, # cyan + '37' : 2 # white + } + def esc_code_to_color(self, esc_code): + return curses.color_pair(self.ESC_CODE_TO_PAIR.get(esc_code, 0)) + def show(self, options, title, subtitle): # Draws a menu with parameters self.set_options(options) @@ -135,7 +149,7 @@ class CursedMenu(object): def ascii_render(self, filename, ypos, xpos): # Prints ASCII art from file at given coordinates this_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)),"art") - this_filename = os.path.join(this_dir,filename) + this_filename = os.path.join(this_dir,filename + '.txt') this_file = open(this_filename,"r") this_string = this_file.readlines() this_file.close() @@ -145,6 +159,41 @@ class CursedMenu(object): # self.screen.refresh() self.screen_lock.release() + def ansi_render(self, filename, ypos, xpos): + # Prints ANSI art from file at given coordinates + # Falls back on ASCII if no ANSI version exists + # Assumes curses.has_colors() + this_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)),"art") + this_filename = os.path.join(this_dir,filename + '.ansi') + if not os.path.exists(this_filename): + self.ascii_render(filename, ypos, xpos) + return + this_file = open(this_filename,"r") + this_string = this_file.readlines() + this_file.close() + self.screen_lock.acquire() + color = curses.A_NORMAL + for y, line in enumerate(this_string, 2): + code_text_pairs = [tuple(token.split('m', 1)) if 'm' in token else (None, token) + for token in line.rstrip('\r\n').split('\x1b[') ] + color_text_pairs = [(color, text) if code == None else (self.esc_code_to_color(code), text) + for (code, text) in code_text_pairs] + x = 0 + for (color, text) in color_text_pairs: + # Handle overflowing art gracefully + text = text[:max(0, self.maxx-(xpos+x))] + if not text: + continue + self.screen.addstr(ypos+y, xpos+x, text, color) + x += len(text) + self.screen_lock.release() + + def art_render(self, filename, ypos, xpos): + if curses.has_colors(): + self.ansi_render(filename, ypos, xpos) + else: + self.ascii_render(filename, ypos, xpos) + def draw_plant_ascii(self, this_plant): ypos = 0 xpos = int((self.maxx-37)/2 + 25) @@ -173,22 +222,22 @@ class CursedMenu(object): 'pachypodium', ] if this_plant.dead == True: - self.ascii_render('rip.txt', ypos, xpos) + self.art_render('rip', ypos, xpos) elif datetime.date.today().month == 10 and datetime.date.today().day == 31: - self.ascii_render('jackolantern.txt', ypos, xpos) + self.art_render('jackolantern', ypos, xpos) elif this_plant.stage == 0: - self.ascii_render('seed.txt', ypos, xpos) + self.art_render('seed', ypos, xpos) elif this_plant.stage == 1: - self.ascii_render('seedling.txt', ypos, xpos) + self.art_render('seedling', ypos, xpos) elif this_plant.stage == 2: - this_filename = plant_art_list[this_plant.species]+'1.txt' - self.ascii_render(this_filename, ypos, xpos) + this_filename = plant_art_list[this_plant.species]+'1' + self.art_render(this_filename, ypos, xpos) elif this_plant.stage == 3 or this_plant.stage == 5: - this_filename = plant_art_list[this_plant.species]+'2.txt' - self.ascii_render(this_filename, ypos, xpos) + this_filename = plant_art_list[this_plant.species]+'2' + self.art_render(this_filename, ypos, xpos) elif this_plant.stage == 4: - this_filename = plant_art_list[this_plant.species]+'3.txt' - self.ascii_render(this_filename, ypos, xpos) + this_filename = plant_art_list[this_plant.species]+'3' + self.art_render(this_filename, ypos, xpos) def draw_default(self): # draws default menu