Reputation: 474
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
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:
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:
Upvotes: 1
Reputation: 20434
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