Reputation: 658
I'm sorry, I feel like there is a better title for my question, but I can't think of it.
The situation, essentially, is this: I am creating a fixed-width information table. I have a list of (k,v) for k in a list of keys.
When done, the " : " should be centered in the line, and there should be a "|" on the far left and right sides.
My problem is, I have a few lists that are too long to fix into a single line. I either need to be able to have it so that once the list is x characters in, it will start a new line, and have the text indented to the same level, or I need to be able to have a number of values encoded such that they align with the same left padding (or what I'll have be the "tabbed" version of the content.
An example of what I have so far:
def make_information_file(t):
pairs=[("SERiES","Game of Thrones"),("SiZE","47,196,930,048 bytes"),(AUDiO TRACKS,['English: DTS-HD Master Audio 5.1', 'French: DTS 5.1', 'Spanish: DTS 5.1', 'Polish: DTS 2.0', 'Spanish: DTS 2.0'])]
general_keys=["COLLECTiON NAME","UPC","RETAiL RELEASE DATE","REGiON","STUDiO"]
video_keys=["ViDEO","SOURCE","RESOLUTiON","ASPECT RATiO"]
audio_keys=["AUDiO FORMATS"]
subtitle_keys=["SUBTiTLES"]
all_keys=general_keys+video_keys+audio_keys+subtitle_keys
longest_key=(sorted(all_keys,key=len) or [""])[-1]
longest_key_length=len(longest_key)
left_padding=longest_key_length+5
right_padding=106-left_padding
empty_left_padding=left_padding+3
empty_right_padding=106-left_padding-3
line_formatter=lambda p: "|{field:>{left_padding}} : {value:<{right_padding}}|".format(field=p[0],value=p[-1],left_padding=left_padding,right_padding=right_padding)
now, notice that depending on how long the longest key is, everything with be aligned such that the ":" is at a fixed point, with one space to either side of it and right text to the left right-aligned, and the stuff to the left left-aligned.
however, the "AUDiO TRACKS" list is too long to fit into one line. I can either have it automatically split if the word is going to push it over it's limit (my preference, I believe, at which point the second line (and any lines thereafter) will have to indent the text to keep in inline with the first line's text. The other option is to have it so that I have it so that every value is centered with empty_left_padding to the left, followed by the string value, followed by enough blank spaces such that the line's final length is the standard 111 characters long, with a "|" as the first and last characters
desired_output=""""
| SERiES : Game of Thrones |
| SiZE : 47,196,930,048 bytes |
| AUDiO FORMATS : English: DTS-HD Master Audio 5.1, French: DTS 5.1, Spanish: DTS 5.1, |
| Polish: DTS 2.0, Spanish: DTS 2.0 |
| UPC : 883929191505 |
| RETAiL RELEASE DATE : 03-06-2012 |
| REGiON : A, B, C |
| STUDiO : HBO Home Video |
| ViDEO : 1080p 1.78:1 |
| SOURCE : BD50 |
| RESOLUTiON : 1080p |
| ASPECT RATiO : 16:9 |"""
So, I can't figure out how to deal with the "AUDiO FORMATS" case above (I have the same issue with the list of subtitles available).
Upvotes: 0
Views: 2040
Reputation: 12150
First: store your informations in an ordered dictionary, to preserve its order, and also get the data organized into key-value pairs:
import collections
d = collections.OrderedDict([
('SERiES', 'Game of Thrones'),
('SiZE', '47,196,930,048 bytes'),
('AUDiO FORMATS', 'English: DTS-HD Master Audio 5.1, French: DTS 5.1, Spanish: DTS 5.1, Polish: DTS 2.0, Spanish: DTS 2.0'),
('UPC', '883929191505'),
('RETAiL RELEASE DATE', '03-06-2012'),
('REGiON', 'A, B, C'),
('STUDiO', 'HBO Home Video'),
('ViDEO', '1080p 1.78:1'),
('SOURCE', 'BD50'),
('RESOLUTiON', '1080p'),
('ASPECT RATiO', '16:9')
])
Second: you will need a word-wrapper, to wrap only whole words at a given column length:
def word_wrap(string, width=80, indent=4, tab=True):
output = list()
for ln in string.replace('\t', ' ' * indent).split('\n'):
line = list()
for wd in ln.split(' '):
if len(' '.join(line) + ' ' + wd) > width:
output.append(' '.join(line))
line = list()
line.append(wd)
output.append(' '.join(line))
return [l.replace(' ' * indent, '\t') for l in output] if tab else output
Third: you need a dictionary formatter, where you can set up the separator, the width of the whole text, the padding space before the texts and the spaces before and after the separator:
def format_dict(dictionary, sep=':', width=80, pad=2, pre=1, post=1):
max_len = max(len(k) for k in dictionary)
para_pad = '%s%s' % ('\n', (pad + max_len + pre + len(sep) + post)*' ')
separator = '%s%s%s' % (pre*' ', sep, post*' ')
output = list()
for key, value in dictionary.iteritems():
output.append(
'%s%s%s' % (
'%s%s' % ((max_len + pad - len(key))*' ', key),
separator,
para_pad.join(word_wrap(str(value), width - len(para_pad)))
)
)
return [''.join(l) for l in output]
Fourth: decorate the left and the right side of the paragraph:
def decorate_para(string, deco='|', width=80):
output = list()
for line in string.split('\n'):
output.append('%s%s%s%s' % (deco, line, (width - len(line))*' ', deco))
return '\n'.join(output)
Fifth: test the output by printing it:
w = 79
print decorate_para('\n'.join(format_dict(d, width=w - 2)), width=w)
And voilá! the output is:
| SERiES : Game of Thrones |
| SiZE : 47,196,930,048 bytes |
| AUDiO FORMATS : English: DTS-HD Master Audio 5.1, French: DTS 5.1, |
| Spanish: DTS 5.1, Polish: DTS 2.0, Spanish: DTS 2.0 |
| UPC : 883929191505 |
| RETAiL RELEASE DATE : 03-06-2012 |
| REGiON : A, B, C |
| STUDiO : HBO Home Video |
| ViDEO : 1080p 1.78:1 |
| SOURCE : BD50 |
| RESOLUTiON : 1080p |
| ASPECT RATiO : 16:9 |
Upvotes: 3
Reputation: 6814
Here's some hint that might help you:
Store your pairs as a dict. This is more intuitive than using a list and will come handy later on
pairs = dict([("SERiES","Game of Thrones"),("SiZE","47,196,930,048 bytes"),
('AUDiO TRACKS',['English: DTS-HD Master Audio 5.1', 'French: DTS 5.1',
'Spanish: DTS 5.1', 'Polish: DTS 2.0', 'Spanish: DTS 2.0'])])
To get the max length for the keys use the max built in method:
longest_key = max(pairs.keys(), key=len)
longest_key_length=len(longest_key)
To align a string to the right use the string function str.rjust
left_padding = longest_key_length + 5
for k in pairs.keys():
print '|' + (k + ' : ').rjust(left_padding)
to fill a string with spaces to a fixed with use the string function str.ljust
max_l = 105
max_right = max_l - left_padding
for k, v in pairs.items():
left = '|' + (k + ' : ').rjust(left_padding)
right = str(v) if len(str(v)) < max_right else ''
right = right.ljust(max_right) + '|'
print left + right
Finally you can use the textwrap library to wrap paragraphs or use a more advanced lib for this kind of stuff
You can also use other string methods like center, capitalize, title...
With this, you should be able to get everything going.
Upvotes: 2