Skip to content

Commit 0d2aeb1

Browse files
committed
Demonstation of setting per-row text color. Added new Row definition function to mirror Column definition function.
1 parent ca72aee commit 0d2aeb1

File tree

2 files changed

+133
-1
lines changed

2 files changed

+133
-1
lines changed

examples/rows.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env python
2+
# coding=utf-8
3+
"""
4+
Demonstration of per-row customizations (currently only text foreground color)
5+
Also demonstrates use of object formatter with both tuple-based and object-base row entries
6+
"""
7+
import tableformatter as tf
8+
9+
10+
class MyRowObject(object):
11+
"""Simple object to demonstrate using a list of objects with TableFormatter"""
12+
def __init__(self, field1: str, field2: str, field3: int, field4: int):
13+
self.field1 = field1
14+
self.field2 = field2
15+
self._field3 = field3
16+
self.field4 = field4
17+
18+
def get_field3(self):
19+
"""Demonstrates accessing object functions"""
20+
return self._field3
21+
22+
23+
def multiply(row_obj: MyRowObject):
24+
"""Demonstrates an object formatter function"""
25+
return str(row_obj.get_field3() * row_obj.field4)
26+
27+
28+
def multiply_tuple(row_obj):
29+
"""Demonstrates an object formatter function"""
30+
return str(row_obj[2] * row_obj[3])
31+
32+
33+
def int2word(num, separator="-"):
34+
"""Demonstrates a field formatter function
35+
From: https://codereview.stackexchange.com/questions/156590/create-the-english-word-for-a-number
36+
"""
37+
ones_and_teens = {0: "Zero", 1: 'One', 2: 'Two', 3: 'Three',
38+
4: 'Four', 5: 'Five', 6: 'Six', 7: 'Seven',
39+
8: 'Eight', 9: 'Nine', 10: 'Ten', 11: 'Eleven',
40+
12: 'Twelve', 13: 'Thirteen', 14: 'Fourteen',
41+
15: 'Fifteen', 16: 'Sixteen', 17: 'Seventeen',
42+
18: 'Eighteen', 19: 'Nineteen'}
43+
twenty2ninety = {2: 'Twenty', 3: 'Thirty', 4: 'Forty', 5: 'Fifty',
44+
6: 'Sixty', 7: 'Seventy', 8: 'Eighty', 9: 'Ninety', 0: ""}
45+
46+
if 0 <= num < 19:
47+
return ones_and_teens[num]
48+
elif 20 <= num <= 99:
49+
tens, below_ten = divmod(num, 10)
50+
if below_ten > 0:
51+
words = twenty2ninety[tens] + separator + \
52+
ones_and_teens[below_ten].lower()
53+
else:
54+
words = twenty2ninety[tens]
55+
return words
56+
57+
elif 100 <= num <= 999:
58+
hundreds, below_hundred = divmod(num, 100)
59+
tens, below_ten = divmod(below_hundred, 10)
60+
if below_hundred == 0:
61+
words = ones_and_teens[hundreds] + separator + "hundred"
62+
elif below_ten == 0:
63+
words = ones_and_teens[hundreds] + separator + \
64+
"hundred" + separator + twenty2ninety[tens].lower()
65+
else:
66+
if tens > 0:
67+
words = ones_and_teens[hundreds] + separator + "hundred" + separator + twenty2ninety[
68+
tens].lower() + separator + ones_and_teens[below_ten].lower()
69+
else:
70+
words = ones_and_teens[
71+
hundreds] + separator + "hundred" + separator + ones_and_teens[below_ten].lower()
72+
return words
73+
74+
else:
75+
print("num out of range")
76+
77+
78+
rows = [tf.Row(MyRowObject('Longer text that will trigger the column wrapping', 'A2', 5, 56),
79+
text_color=tf.TableColors.TEXT_COLOR_RED),
80+
tf.Row(MyRowObject('B1', 'B2\nB2\nB2', 23, 8),
81+
text_color=tf.TableColors.TEXT_COLOR_GREEN),
82+
MyRowObject('C1', 'C2', 4, 9),
83+
MyRowObject('D1', 'D2', 7, 5)]
84+
85+
86+
columns = (tf.Column('First', width=20, attrib='field1'),
87+
tf.Column('Second', attrib='field2'),
88+
tf.Column('Num 1', width=3, attrib='get_field3'),
89+
tf.Column('Num 2', attrib='field4'),
90+
tf.Column('Multiplied', obj_formatter=multiply))
91+
print("Per-row customizations with object-based row entries")
92+
print(tf.generate_table(rows, columns))
93+
94+
95+
rows = [tf.Row('Longer text that will trigger the column wrapping', 'A2', 5, 56, None,
96+
text_color=tf.TableColors.TEXT_COLOR_RED),
97+
tf.Row('B1', 'B2\nB2\nB2', 23, 8, None,
98+
text_color=tf.TableColors.TEXT_COLOR_GREEN),
99+
('C1', 'C2', 4, 9, None),
100+
('D1', 'D2', 7, 5, None)]
101+
102+
103+
columns = (tf.Column('First', width=20, wrap_mode=tf.WrapMode.TRUNCATE_HARD),
104+
tf.Column('Second'),
105+
tf.Column('Num 1'),
106+
tf.Column('Num 2', formatter=int2word),
107+
tf.Column('Multiplied', obj_formatter=multiply_tuple))
108+
109+
110+
print("Per-row customizations with tuple-based row entries")
111+
print(tf.generate_table(rows, columns))

tableformatter.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,24 @@ def Column(col_name: str,
683683
return col_name, opts
684684

685685

686+
def Row(*args, text_color: TableColors=None):
687+
"""
688+
Processes row options and generates a tuple in the format the TableFormatter expects
689+
:param args: Can be either 1 object or a list of values
690+
:param text_color: text color to use when displaying this row
691+
:return: Tuple formatted for the TableFormatter to consume
692+
"""
693+
opts = dict()
694+
695+
if text_color is not None:
696+
opts[TableFormatter.ROW_OPT_TEXT_COLOR] = text_color
697+
698+
row = list(args)
699+
if opts:
700+
row.append(opts)
701+
return tuple(row)
702+
703+
686704
class TableFormatter(object):
687705
"""
688706
Simple implementation of an ascii table formatter.
@@ -926,7 +944,10 @@ def generate_table(self, entries: List[Iterable], force_transpose=False):
926944
# skip extra values beyond the columns configured
927945
if column_index < len(self._columns):
928946
formatter = self._get_column_option(column_index, TableFormatter.COL_OPT_FIELD_FORMATTER)
929-
if formatter is not None and callable(formatter):
947+
obj_formatter = self._get_column_option(column_index, TableFormatter.COL_OPT_OBJECT_FORMATTER)
948+
if obj_formatter is not None and callable(obj_formatter):
949+
field_string = obj_formatter(entry)
950+
elif formatter is not None and callable(formatter):
930951
field_string = formatter(field, )
931952
elif isinstance(field, str):
932953
field_string = field

0 commit comments

Comments
 (0)