Python Forum

Full Version: printing text tables with consistent width
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
is there a facility that exists to make it easy to print out a multi-column table which has data of different width in each row of a column in a way where every row in a given column has the same width? the difficult part will be that the first row has to be delayed until the lat row is output, buffering the entire table, unless the case is that the data can be produced twice.
I've done this a few times. Get the string output as a list of lists. For each index in the sub-lists, get maximum width. Construct a format string with those widths and the justifications you want. Apply the rows to that format string repeatedly, printing or joining with '\n'.
Here's a simple example I whipped up:

def table(rows):
	max_widths = []
	for column in zip(*rows):
		max_widths.append(max([len(text) for text in column]))
	template = '  '.join(['{{:<{}}}'.format(width) for width in max_widths])
	return '\n'.join([template.format(*row) for row in rows])

knights = [['Sir Lancelot', 'The Holy Grail', 'Blue'],
	['Sir Robin', 'The Holy Grail', "I don't know that."],
	['Sir Galahad', 'The Grail', 'Blue. No, yel...'],
	['King Arthur', 'The Holy Grail', 'What do you mean?']]

print(table(knights))
Output:
Sir Lancelot The Holy Grail Blue Sir Robin The Holy Grail I don't know that. Sir Galahad The Grail Blue. No, yel... King Arthur The Holy Grail What do you mean?
python-tabulate to take the more lazy way than @ichabod801 Wink
>>> from tabulate import tabulate

>>> knights = [['Sir Lancelot', 'The Holy Grail', 'Blue'],
    ['Sir Robin', 'The Holy Grail', "I don't know that."],
    ['Sir Galahad', 'The Grail', 'Blue. No, yel...'],
    ['King Arthur', 'The Holy Grail', 'What do you mean?']]

>>> print(tabulate(knights))
------------  --------------  ------------------
Sir Lancelot  The Holy Grail  Blue
Sir Robin     The Holy Grail  I don't know that.
Sir Galahad   The Grail       Blue. No, yel...
King Arthur   The Holy Grail  What do you mean?
------------  --------------  ------------------

>>> print(tabulate(knights, tablefmt="pipe"))
|:-------------|:---------------|:-------------------|
| Sir Lancelot | The Holy Grail | Blue               |
| Sir Robin    | The Holy Grail | I don't know that. |
| Sir Galahad  | The Grail      | Blue. No, yel...   |
| King Arthur  | The Holy Grail | What do you mean?  |

>>> print(tabulate(knights, tablefmt="fancy_grid"))
╒══════════════╤════════════════╤════════════════════╕
│ Sir Lancelot │ The Holy Grail │ Blue               │
├──────────────┼────────────────┼────────────────────┤
│ Sir Robin    │ The Holy Grail │ I don't know that. │
├──────────────┼────────────────┼────────────────────┤
│ Sir Galahad  │ The Grail      │ Blue. No, yel...   │
├──────────────┼────────────────┼────────────────────┤
│ King Arthur  │ The Holy Grail │ What do you mean?  │
╘══════════════╧════════════════╧════════════════════╛
(Jun-30-2018, 03:56 PM)ichabod801 Wrote: [ -> ]I've done this a few times. Get the string output as a list of lists. For each index in the sub-lists, get maximum width. Construct a format string with those widths and the justifications you want. Apply the rows to that format string repeatedly, printing or joining with '\n'.
BTDT2

just did one today which is why i was thinking or hoping something making this easy would be nice if it existed. you just print via a method of this class, giving each column as an argument. it would save the data and accumulate the max width of each column. once done a final method is called, then it actually prints the saved data, formatting each column to the max width or a specified width. it will also remember if a column is entirely numeric and right-justify if so, else left-justify, unless the justification is specified.

i didn't see any documentation for tabulate. where does that come from?
Note that most terminals use a fixed width font so this will work just fine. For anyone who reads this in the future and is using a proportional font, this won't work. A small tkinter program to show the difference.
try:
       import Tkinter as tk     ## Python 2.x
except ImportError:
       import tkinter as tk     ## Python 3.x

class DifferentFonts():
    def __init__(self):
        self.top=tk.Tk()
        lit_1="Display lots of i's and l's-iiilll"
        lit_2="This is the same length as 1-wwwqq"
        ctr = 0
        for each_font in (('Verdana', 12),
                          ('DejaVuSansMono', 12)):
            tk.Label(self.top, text=each_font[0]+"-"*30,
                    font=each_font).grid(row=ctr)
            ctr += 1
            tk.Label(self.top, text=lit_1, font=each_font
                    ).grid(row=ctr, sticky="w")
            ctr += 1
            tk.Label(self.top, text=lit_2, font=each_font
                    ).grid(row=ctr, sticky="w")
            ctr += 1
            self.top.rowconfigure(ctr, weight=1, minsize=50)
            ctr += 1

        tk.Button(self.top, text="Quit", bg="orange",
               command=self.top.quit).grid(row=20)

        self.top.mainloop()

DifferentFonts() 
(Jun-30-2018, 05:30 PM)Skaperen Wrote: [ -> ]i didn't see any documentation for tabulate. where does that come from?
It's in the in the link i posted,just scroll down.
Small project do not always make own document page,as this the one where documentation is on there Repo page or PyPi page.
i missed that it was an "outside project" (my term). while i might use some on some of my projects, there are others i cannot use any for. this one is one where i can (i will be the only user). it is listing out backup partitions with columns like start sector and number of sectors.