elijahfhopp
elijahfhopp

Reputation: 474

How to format triple quoted text into a triple quoted print statement

So currently I am working on a text based project the includes a lotto. The trouble is that I want to use triple quoted text numbers inside a print statement that already has triple quotes. basically I want to do something like this:

num1  = """
 ______   
/ ___  \  
\/   \  \ 
   ___) / 
  (___ (  
      ) \ 
/\___/  / 
\______/   

"""
print("""
 ______________
|             |
|             |
|             |
|     {a}     |
|             |
|             |
|             |
""".format(a=num1))

This is just a concept, I know this does not actually work. If I try to do anything like this It just pushes the non-formated text to the next line. Should I print it line by line? Do I need to use %s rather than .format()? Anyway I hope I communicated my question clearly.

-Zeus

Upvotes: 4

Views: 525

Answers (2)

BPL
BPL

Reputation: 9863

I don't think there isn't any python builtin which can help you out with that. I'd recommend you just look for a proper ascii text library to build effortlesly that type of ascii-text scripts, figlet is one of the hundreds available out there.

If you want to code something by yourself, i'd just install pillow and i'd use it to fill a custom buffer with the shapes i wanted, look below a 5-min example which "renders" text and rectangles to such a buffer. Be aware it's not doing out-of-bounds checks and probably is quite slow... but i guess you can start working on top of that:

from PIL import Image, ImageFont, ImageDraw


class AsciiBuffer():

    def __init__(self, width, height):
        self.font_size = 15
        self.font = ImageFont.truetype('arialbd.ttf', self.font_size)
        self.width = width
        self.height = height
        self.clear()

    def clear(self):
        self.buffer = [['*' for x in range(self.width)]
                       for y in range(self.height)]

    def draw_text(self, x0, y0, text):
        size = self.font.getsize(text)
        image = Image.new('1', size, 1)
        draw = ImageDraw.Draw(image)
        draw.text((0, 0), text, font=self.font)

        for y in range(size[1]):
            line = []
            for x in range(size[0]):
                if image.getpixel((x, y)):
                    self.buffer[y0 + y][x0 + x] = ' '
                else:
                    self.buffer[y0 + y][x0 + x] = '#'

    def draw_rectangle(self, x0, y0, w, h, fill=' '):
        for y in range(h):
            for x in range(w):
                self.buffer[y0 + y][x0 + x] = fill

    def render(self):
        for y in range(self.height):
            print(''.join(self.buffer[y]))

if __name__ == "__main__":
    k = 20
    ab = AsciiBuffer(k * 3 + 4, k * 3 + 4)
    n = 1
    for i in range(3):
        for j in range(3):
            x = 1 + (k + 1) * i
            y = 1 + (k + 1) * j
            ab.draw_rectangle(x, y, k, k)
            ab.draw_text(x + int(k / 4), y, str(n))
            n += 1
    ab.render()

Output:

enter image description here

Refactoring:

In any case, the above code has multiple problems, being one of them the fact the class AsciiBuffer is coupled with PIL, when this class should be as dummy as possible, for instance, just drawing "text sprites" should be fine, so here's a little refactoring where i'm showing you how to generate "text sprites" either from multiline strings (like the one you posted in your question) or from certain system fonts rendered by PIL:

import sys
from PIL import Image, ImageFont, ImageDraw


class SpriteGenerator():

    def __init__(self):
        pass

    def from_multiline_string(self, text, length):
        buf = []

        for l in text.splitlines():
            buf.append(list(l) + [' '] * (length - len(l)))

        return buf

    def from_pil(self, text, font_name='arialbd.ttf', font_size=15):
        font = ImageFont.truetype(font_name, font_size)
        size = font.getsize(text)
        image = Image.new('1', size, 1)
        draw = ImageDraw.Draw(image)
        draw.text((0, 0), text, font=font)
        buf = []

        for y in range(size[1]):
            line = []
            for x in range(size[0]):
                if image.getpixel((x, y)):
                    line.append(' ')
                else:
                    line.append('#')
            buf.append(line)

        return buf


class AsciiBuffer():

    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.clear()

    def clear(self):
        self.buffer = [['*' for x in range(self.width)]
                       for y in range(self.height)]

    def draw_sprite(self, x0, y0, sprite):
        for y, row in enumerate((sprite)):
            for x, pixel in enumerate(row):
                self.buffer[y0 + y][x0 + x] = pixel

    def draw_rectangle(self, x0, y0, w, h, fill=' '):
        for y in range(h):
            for x in range(w):
                self.buffer[y0 + y][x0 + x] = fill

    def render(self):
        for y in range(self.height):
            print(''.join(self.buffer[y]))

if __name__ == "__main__":
    num  = """


 ______
/ ___  \\
\\/   \\  \\
   ___) /
  (___ (
      ) \\
/\\___/  /
\\______/
"""

    k = 15
    ab = AsciiBuffer(k * 3 + 4, k * 3 + 4)
    sg = SpriteGenerator()
    n = 1

    for i in range(3):
        for j in range(3):
            x = 1 + (k + 1) * i
            y = 1 + (k + 1) * j
            ab.draw_rectangle(x, y, k, k)
            if n == 3:
                ab.draw_sprite(
                    x + int(k / 4), y, sg.from_multiline_string(num, 11))
            else:
                ab.draw_sprite(
                    x + int(k / 4), y, sg.from_pil(str(n), font_size=k))
            n += 1

    ab.render()

Output:

enter image description here

Upvotes: 1

Joe Iddon
Joe Iddon

Reputation: 20434

A universal solution:

The function below should do what you want. It takes in a variable number of arguments with *arg and prints them out formatted, side-by-side, enclosed in boxes. The way it works is by zipping the numbers passed into it together and then for each one, printing lines between them. It also prints lines above and below the digits to complete the boxes.

def printNums(*arg):
   nums = [n.split("\n")[1:-1] for n in arg]
   print("_" * (sum([len(n[0]) for n in nums]) + len(nums) + 1))
   rows = zip(*nums)
   for r in rows:
      for n in r:
         print("|", end='')
         print(n, end='')
      print("|")
   print("_" * (sum([len(n[0]) for n in nums]) + len(nums) + 1))

with your number:

num1  = """
 ______   
/ ___  \  
\/   \  \ 
   ___) / 
  (___ (  
      ) \ 
/\___/  / 
\______/  
"""

we can call the function with 4 of them, for instance:

printNums(num1, num1, num1, num1)

which outputs:

_____________________________________________
| ______   | ______   | ______   | ______   |
|/ ___  \  |/ ___  \  |/ ___  \  |/ ___  \  |
|\/   \  \ |\/   \  \ |\/   \  \ |\/   \  \ |
|   ___) / |   ___) / |   ___) / |   ___) / |
|  (___ (  |  (___ (  |  (___ (  |  (___ (  |
|      ) \ |      ) \ |      ) \ |      ) \ |
|/\___/  / |/\___/  / |/\___/  / |/\___/  / |
|\______/  |\______/  |\______/  |\______/  |
_____________________________________________

which I hope is what you wanted! Of course, here we are using the repeated ASCII art for a 3 digit, you could make the other digits and call them appropriately if that is what you want for your application. :)

Hope this helps!

Upvotes: 1

Related Questions