summaryrefslogtreecommitdiffstats
path: root/terminaltables/base_table.py
diff options
context:
space:
mode:
Diffstat (limited to 'terminaltables/base_table.py')
-rw-r--r--terminaltables/base_table.py217
1 files changed, 217 insertions, 0 deletions
diff --git a/terminaltables/base_table.py b/terminaltables/base_table.py
new file mode 100644
index 0000000..281d5a3
--- /dev/null
+++ b/terminaltables/base_table.py
@@ -0,0 +1,217 @@
+"""Base table class. Define just the bare minimum to build tables."""
+
+from terminaltables.build import build_border, build_row, flatten
+from terminaltables.width_and_alignment import align_and_pad_cell, max_dimensions
+
+
+class BaseTable(object):
+ """Base table class.
+
+ :ivar iter table_data: List (empty or list of lists of strings) representing the table.
+ :ivar str title: Optional title to show within the top border of the table.
+ :ivar bool inner_column_border: Separates columns.
+ :ivar bool inner_footing_row_border: Show a border before the last row.
+ :ivar bool inner_heading_row_border: Show a border after the first row.
+ :ivar bool inner_row_border: Show a border in between every row.
+ :ivar bool outer_border: Show the top, left, right, and bottom border.
+ :ivar dict justify_columns: Horizontal justification. Keys are column indexes (int). Values are right/left/center.
+ :ivar int padding_left: Number of spaces to pad on the left side of every cell.
+ :ivar int padding_right: Number of spaces to pad on the right side of every cell.
+ """
+
+ CHAR_F_INNER_HORIZONTAL = '-'
+ CHAR_F_INNER_INTERSECT = '+'
+ CHAR_F_INNER_VERTICAL = '|'
+ CHAR_F_OUTER_LEFT_INTERSECT = '+'
+ CHAR_F_OUTER_LEFT_VERTICAL = '|'
+ CHAR_F_OUTER_RIGHT_INTERSECT = '+'
+ CHAR_F_OUTER_RIGHT_VERTICAL = '|'
+ CHAR_H_INNER_HORIZONTAL = '-'
+ CHAR_H_INNER_INTERSECT = '+'
+ CHAR_H_INNER_VERTICAL = '|'
+ CHAR_H_OUTER_LEFT_INTERSECT = '+'
+ CHAR_H_OUTER_LEFT_VERTICAL = '|'
+ CHAR_H_OUTER_RIGHT_INTERSECT = '+'
+ CHAR_H_OUTER_RIGHT_VERTICAL = '|'
+ CHAR_INNER_HORIZONTAL = '-'
+ CHAR_INNER_INTERSECT = '+'
+ CHAR_INNER_VERTICAL = '|'
+ CHAR_OUTER_BOTTOM_HORIZONTAL = '-'
+ CHAR_OUTER_BOTTOM_INTERSECT = '+'
+ CHAR_OUTER_BOTTOM_LEFT = '+'
+ CHAR_OUTER_BOTTOM_RIGHT = '+'
+ CHAR_OUTER_LEFT_INTERSECT = '+'
+ CHAR_OUTER_LEFT_VERTICAL = '|'
+ CHAR_OUTER_RIGHT_INTERSECT = '+'
+ CHAR_OUTER_RIGHT_VERTICAL = '|'
+ CHAR_OUTER_TOP_HORIZONTAL = '-'
+ CHAR_OUTER_TOP_INTERSECT = '+'
+ CHAR_OUTER_TOP_LEFT = '+'
+ CHAR_OUTER_TOP_RIGHT = '+'
+
+ def __init__(self, table_data, title=None):
+ """Constructor.
+
+ :param iter table_data: List (empty or list of lists of strings) representing the table.
+ :param title: Optional title to show within the top border of the table.
+ """
+ self.table_data = table_data
+ self.title = title
+
+ self.inner_column_border = True
+ self.inner_footing_row_border = False
+ self.inner_heading_row_border = True
+ self.inner_row_border = False
+ self.outer_border = True
+
+ self.justify_columns = dict() # {0: 'right', 1: 'left', 2: 'center'}
+ self.padding_left = 1
+ self.padding_right = 1
+
+ def horizontal_border(self, style, outer_widths):
+ """Build any kind of horizontal border for the table.
+
+ :param str style: Type of border to return.
+ :param iter outer_widths: List of widths (with padding) for each column.
+
+ :return: Prepared border as a tuple of strings.
+ :rtype: tuple
+ """
+ if style == 'top':
+ horizontal = self.CHAR_OUTER_TOP_HORIZONTAL
+ left = self.CHAR_OUTER_TOP_LEFT
+ intersect = self.CHAR_OUTER_TOP_INTERSECT if self.inner_column_border else ''
+ right = self.CHAR_OUTER_TOP_RIGHT
+ title = self.title
+ elif style == 'bottom':
+ horizontal = self.CHAR_OUTER_BOTTOM_HORIZONTAL
+ left = self.CHAR_OUTER_BOTTOM_LEFT
+ intersect = self.CHAR_OUTER_BOTTOM_INTERSECT if self.inner_column_border else ''
+ right = self.CHAR_OUTER_BOTTOM_RIGHT
+ title = None
+ elif style == 'heading':
+ horizontal = self.CHAR_H_INNER_HORIZONTAL
+ left = self.CHAR_H_OUTER_LEFT_INTERSECT if self.outer_border else ''
+ intersect = self.CHAR_H_INNER_INTERSECT if self.inner_column_border else ''
+ right = self.CHAR_H_OUTER_RIGHT_INTERSECT if self.outer_border else ''
+ title = None
+ elif style == 'footing':
+ horizontal = self.CHAR_F_INNER_HORIZONTAL
+ left = self.CHAR_F_OUTER_LEFT_INTERSECT if self.outer_border else ''
+ intersect = self.CHAR_F_INNER_INTERSECT if self.inner_column_border else ''
+ right = self.CHAR_F_OUTER_RIGHT_INTERSECT if self.outer_border else ''
+ title = None
+ else:
+ horizontal = self.CHAR_INNER_HORIZONTAL
+ left = self.CHAR_OUTER_LEFT_INTERSECT if self.outer_border else ''
+ intersect = self.CHAR_INNER_INTERSECT if self.inner_column_border else ''
+ right = self.CHAR_OUTER_RIGHT_INTERSECT if self.outer_border else ''
+ title = None
+ return build_border(outer_widths, horizontal, left, intersect, right, title)
+
+ def gen_row_lines(self, row, style, inner_widths, height):
+ r"""Combine cells in row and group them into lines with vertical borders.
+
+ Caller is expected to pass yielded lines to ''.join() to combine them into a printable line. Caller must append
+ newline character to the end of joined line.
+
+ In:
+ ['Row One Column One', 'Two', 'Three']
+ Out:
+ [
+ ('|', ' Row One Column One ', '|', ' Two ', '|', ' Three ', '|'),
+ ]
+
+ In:
+ ['Row One\nColumn One', 'Two', 'Three'],
+ Out:
+ [
+ ('|', ' Row One ', '|', ' Two ', '|', ' Three ', '|'),
+ ('|', ' Column One ', '|', ' ', '|', ' ', '|'),
+ ]
+
+ :param iter row: One row in the table. List of cells.
+ :param str style: Type of border characters to use.
+ :param iter inner_widths: List of widths (no padding) for each column.
+ :param int height: Inner height (no padding) (number of lines) to expand row to.
+
+ :return: Yields lines split into components in a list. Caller must ''.join() line.
+ """
+ cells_in_row = list()
+
+ # Resize row if it doesn't have enough cells.
+ if len(row) != len(inner_widths):
+ row = row + [''] * (len(inner_widths) - len(row))
+
+ # Pad and align each cell. Split each cell into lines to support multi-line cells.
+ for i, cell in enumerate(row):
+ align = (self.justify_columns.get(i),)
+ inner_dimensions = (inner_widths[i], height)
+ padding = (self.padding_left, self.padding_right, 0, 0)
+ cells_in_row.append(align_and_pad_cell(cell, align, inner_dimensions, padding))
+
+ # Determine border characters.
+ if style == 'heading':
+ left = self.CHAR_H_OUTER_LEFT_VERTICAL if self.outer_border else ''
+ center = self.CHAR_H_INNER_VERTICAL if self.inner_column_border else ''
+ right = self.CHAR_H_OUTER_RIGHT_VERTICAL if self.outer_border else ''
+ elif style == 'footing':
+ left = self.CHAR_F_OUTER_LEFT_VERTICAL if self.outer_border else ''
+ center = self.CHAR_F_INNER_VERTICAL if self.inner_column_border else ''
+ right = self.CHAR_F_OUTER_RIGHT_VERTICAL if self.outer_border else ''
+ else:
+ left = self.CHAR_OUTER_LEFT_VERTICAL if self.outer_border else ''
+ center = self.CHAR_INNER_VERTICAL if self.inner_column_border else ''
+ right = self.CHAR_OUTER_RIGHT_VERTICAL if self.outer_border else ''
+
+ # Yield each line.
+ for line in build_row(cells_in_row, left, center, right):
+ yield line
+
+ def gen_table(self, inner_widths, inner_heights, outer_widths):
+ """Combine everything and yield every line of the entire table with borders.
+
+ :param iter inner_widths: List of widths (no padding) for each column.
+ :param iter inner_heights: List of heights (no padding) for each row.
+ :param iter outer_widths: List of widths (with padding) for each column.
+ :return:
+ """
+ # Yield top border.
+ if self.outer_border:
+ yield self.horizontal_border('top', outer_widths)
+
+ # Yield table body.
+ row_count = len(self.table_data)
+ last_row_index, before_last_row_index = row_count - 1, row_count - 2
+ for i, row in enumerate(self.table_data):
+ # Yield the row line by line (e.g. multi-line rows).
+ if self.inner_heading_row_border and i == 0:
+ style = 'heading'
+ elif self.inner_footing_row_border and i == last_row_index:
+ style = 'footing'
+ else:
+ style = 'row'
+ for line in self.gen_row_lines(row, style, inner_widths, inner_heights[i]):
+ yield line
+ # If this is the last row then break. No separator needed.
+ if i == last_row_index:
+ break
+ # Yield heading separator.
+ if self.inner_heading_row_border and i == 0:
+ yield self.horizontal_border('heading', outer_widths)
+ # Yield footing separator.
+ elif self.inner_footing_row_border and i == before_last_row_index:
+ yield self.horizontal_border('footing', outer_widths)
+ # Yield row separator.
+ elif self.inner_row_border:
+ yield self.horizontal_border('row', outer_widths)
+
+ # Yield bottom border.
+ if self.outer_border:
+ yield self.horizontal_border('bottom', outer_widths)
+
+ @property
+ def table(self):
+ """Return a large string of the entire table ready to be printed to the terminal."""
+ dimensions = max_dimensions(self.table_data, self.padding_left, self.padding_right)[:3]
+ return flatten(self.gen_table(*dimensions))