from colorama import Fore, Back, Style # see http://xahlee.info/comp/unicode_drawing_shapes.html def merge(color_string): """ Given a string of colorama ANSI, merge them if you can. """ return color_string.replace("m\x1b[", ";") class Boxes(object): """ Boxes makes nice ASCII graphic menus Example: c1 = merge(Style.BRIGHT + Fore.CYAN) c2 = merge(Style.NORMAL + Fore.CYAN) # Set the size of the box to 40 (Important!) box = Boxes(40, color=c1) self.queue_game.put(box.top()) # format string "{0:^40}".format("Text Centered") # This centers the text into the size of the box. self.queue_game.put(box.row(c1 + "{0:^40}".format("Scripts"))) # this draws the middle line. (The line that goes across, but is # connected on both sides to the one above it). self.queue_game.put(box.middle()) def menu_item(ch, desc): # Again we want the final string to be 40! # c1 and c2 (below) take 0 space. # We have " " + ch + " " + "-" + " " .. which equals 5 # So desc needs {:35} to pad out string out to 40 print chars. row = " {0}{1} {2}-{3} {4:35}".format(c1, ch, c2, c1, desc) # self.queue_game.put( # " " + c1 + ch + c2 + " - " + c1 + desc + self.nl # ) self.queue_game.put(box.row(row)) menu_item("1", "Ports (Trades between two sectors)") menu_item("2", "Explore (Strange new sectors)") menu_item("3", "Space... the final frontier...") menu_item("X", "eXit") self.queue_game.put(box.bottom()) """ box_styles = ( # ┌──┐ # │ │ # ├──┤ # └──┘ { "tl": "\xda", "tr": "\xbf", "top": "\xc4", "side": "\xb3", "bl": "\xc0", "br": "\xd9", "ml": "\xc3", "mr": "\xb4", }, # ╔══╗ # ║ ║ # ╠══╣ # ╚══╝ { "tl": "\xc9", "tr": "\xbb", "top": "\xcd", "side": "\xba", "bl": "\xc8", "br": "\xbc", "ml": "\xcc", "mr": "\xb9", }, # ╒══╕ # │ │ # ╞══╡ # ╘══╛ { "tl": "\xd5", "tr": "\xb8", "top": "\xcd", "side": "\xb3", "bl": "\xd4", "br": "\xbe", "ml": "\xc6", "mr": "\xb5", }, # ╓──╖ # ║ ║ # ╟──╢ # ╙──╜ { "tl": "\xd6", "tr": "\xb7", "top": "\xc4", "side": "\xba", "bl": "\xd3", "br": "\xbd", "ml": "\xc7", "mr": "\xb6", }, ) def __init__(self, size: int, color="", style=1, start_nl=True): """ Construct box size is width of text inside the box. (so actual width is size + 2). color = Style of color to use. If color is blank, we don't use the RESET_ALL. style=0 Is double lines. start_nl Should we start the top with a newline? """ self.size = size self.style = style self.color = color self.start_nl = start_nl # default colors # self.c = merge(Style.BRIGHT + Fore.WHITE + Back.BLUE) # self.cp = merge(Style.BRIGHT + Fore.YELLOW + Back.BLUE) # useful consts self.r = Style.RESET_ALL self.nl = "\n\r" # self.bsb = "\b \b" def top(self): """ Output the TOP line. """ if self.color == "": c = "" r = "" else: c = self.color r = self.r s = self.box_styles[self.style] if self.start_nl: n = self.nl else: n = "" return n + c + s["tl"] + s["top"] * self.size + s["tr"] + r + self.nl def middle(self): """ Output MIDDLE line. This solid line goes completely across. The edges connect to the top and bottom. """ if self.color == "": c = "" r = "" else: c = self.color r = self.r s = self.box_styles[self.style] return c + s["ml"] + s["top"] * self.size + s["mr"] + r + self.nl def row(self, line): """ Output content line. NOTE: the line length must match the box size. Use "{0:30}".format(content) to match size of 30. """ if self.color == "": c = "" r = "" else: c = self.color r = self.r s = self.box_styles[self.style] return c + "{0}{1}{2}".format(s["side"], r + line + c, s["side"]) + r + self.nl def bottom(self): """ Output BOTTOM line. """ if self.color == "": c = "" r = "" else: c = self.color r = self.r s = self.box_styles[self.style] return c + s["bl"] + s["top"] * self.size + s["br"] + r + self.nl @staticmethod def alert(message, length=0, pad=2, width=78, base="blue", style=1): """ Display alert message Example: self.queue_game.put(Boxes.alert("Proxy done.", base="green")) length: the printable character length. If the string contains color codes, you'll need to use this so it will be centered properly. If not given, len(message) is used. pad: The spaces at the front and back of the message. width: Defaults to 78 chars wide. base: The background color to use for the alert. """ if base == "red": color = merge(Style.BRIGHT + Fore.YELLOW + Back.RED) elif base == "blue": color = merge(Style.BRIGHT + Fore.YELLOW + Back.BLUE) elif base == "green": color = merge(Style.BRIGHT + Fore.YELLOW + Back.GREEN) elif base == "cyan": color = merge(Style.BRIGHT + Fore.YELLOW + Back.CYAN) elif base == "magenta": color = merge(Style.BRIGHT + Fore.YELLOW + Back.MAGENTA) else: # I don't know. color = Style.RESET_ALL if length == 0: length = len(message) box_size = length + pad * 2 boxpad = " " * pad box = Boxes(box_size, color=color, style=style, start_nl=False) msg = "{0}{1}{2}{3}".format(color, boxpad, message, boxpad) # How much pad to add to the left side? left_pad = (width - box_size + 2) // 2 pad = " " * left_pad return "{0}{1}{2}{3}{4}{5}".format( pad, box.top(), pad, box.row(msg), pad, box.bottom() )