Skip to content

Commit

Permalink
Updated table example.
Browse files Browse the repository at this point in the history
Renamed Column settings:
    override_header_style -> style_header_text
    override_data_style   -> style_data_text
  • Loading branch information
kmvanbrunt committed Oct 26, 2021
1 parent fcf3410 commit 0d60017
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 79 deletions.
34 changes: 17 additions & 17 deletions cmd2/table_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ def __init__(
width: Optional[int] = None,
header_horiz_align: HorizontalAlignment = HorizontalAlignment.LEFT,
header_vert_align: VerticalAlignment = VerticalAlignment.BOTTOM,
override_header_style: bool = True,
style_header_text: bool = True,
data_horiz_align: HorizontalAlignment = HorizontalAlignment.LEFT,
data_vert_align: VerticalAlignment = VerticalAlignment.TOP,
override_data_style: bool = True,
style_data_text: bool = True,
max_data_lines: Union[int, float] = constants.INFINITY,
) -> None:
"""
Expand All @@ -79,16 +79,16 @@ def __init__(
this width using word-based wrapping (defaults to actual width of header or 1 if header is blank)
:param header_horiz_align: horizontal alignment of header cells (defaults to left)
:param header_vert_align: vertical alignment of header cells (defaults to bottom)
:param override_header_style: if True, then the table is allowed to apply text styles to the header, which may
conflict with any styles the header already has. If False, the header is printed as is.
Table classes which apply style to headers must account for the value of this flag.
(defaults to True)
:param style_header_text: if True, then the table is allowed to apply styles to the header text, which may
conflict with any styles the header already has. If False, the header is printed as is.
Table classes which apply style to headers must account for the value of this flag.
(defaults to True)
:param data_horiz_align: horizontal alignment of data cells (defaults to left)
:param data_vert_align: vertical alignment of data cells (defaults to top)
:param override_data_style: if True, then the table is allowed to apply text styles to the data, which may
conflict with any styles the data already has. If False, the data is printed as is.
Table classes which apply style to data must account for the value of this flag.
(defaults to True)
:param style_data_text: if True, then the table is allowed to apply styles to the data text, which may
conflict with any styles the data already has. If False, the data is printed as is.
Table classes which apply style to data must account for the value of this flag.
(defaults to True)
:param max_data_lines: maximum lines allowed in a data cell. If line count exceeds this, then the final
line displayed will be truncated with an ellipsis. (defaults to INFINITY)
:raises: ValueError if width is less than 1
Expand All @@ -103,11 +103,11 @@ def __init__(

self.header_horiz_align = header_horiz_align
self.header_vert_align = header_vert_align
self.override_header_style = override_header_style
self.style_header_text = style_header_text

self.data_horiz_align = data_horiz_align
self.data_vert_align = data_vert_align
self.override_data_style = override_data_style
self.style_data_text = style_data_text

if max_data_lines < 1:
raise ValueError("Max data lines cannot be less than 1")
Expand Down Expand Up @@ -655,7 +655,7 @@ def generate_header(self) -> str:
# Apply background color to header text in Columns which allow it
to_display: List[Any] = []
for index, col in enumerate(self.cols):
if col.override_header_style:
if col.style_header_text:
to_display.append(self.apply_header_bg(col.header))
else:
to_display.append(col.header)
Expand Down Expand Up @@ -691,7 +691,7 @@ def generate_data_row(self, row_data: Sequence[Any]) -> str:
# Apply background color to data text in Columns which allow it
to_display: List[Any] = []
for index, col in enumerate(self.cols):
if col.override_data_style:
if col.style_data_text:
to_display.append(self.apply_data_bg(row_data[index]))
else:
to_display.append(row_data[index])
Expand Down Expand Up @@ -940,7 +940,7 @@ def generate_header(self) -> str:
# Apply background color to header text in Columns which allow it
to_display: List[Any] = []
for index, col in enumerate(self.cols):
if col.override_header_style:
if col.style_header_text:
to_display.append(self.apply_header_bg(col.header))
else:
to_display.append(col.header)
Expand Down Expand Up @@ -981,7 +981,7 @@ def generate_data_row(self, row_data: Sequence[Any]) -> str:
# Apply background color to data text in Columns which allow it
to_display: List[Any] = []
for index, col in enumerate(self.cols):
if col.override_data_style:
if col.style_data_text:
to_display.append(self.apply_data_bg(row_data[index]))
else:
to_display.append(row_data[index])
Expand Down Expand Up @@ -1028,7 +1028,7 @@ class AlternatingTable(BorderedTable):
Implementation of BorderedTable which uses background colors to distinguish between rows instead of row border
lines. This class can be used to create the whole table at once or one row at a time.
To nest an AlternatingTable within another AlternatingTable, set override_data_style to False on the Column
To nest an AlternatingTable within another AlternatingTable, set style_data_text to False on the Column
which contains the nested table. That will prevent the current row's background color from affecting the colors
of the nested table.
"""
Expand Down
107 changes: 60 additions & 47 deletions examples/table_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,18 @@ def __str__(self) -> str:
class Book:
"""Class used for example data"""

def __init__(self, title: str, due_date: str) -> None:
def __init__(self, title: str, year_published: str) -> None:
self.title = title
self.due_date = due_date
self.year_published = year_published


class Person:
class Author:
"""Class used for example data"""

def __init__(self, name: str, birthday: str, department: str) -> None:
def __init__(self, name: str, birthday: str, place_of_birth: str) -> None:
self.name = name
self.birthday = birthday
self.department = department
self.place_of_birth = place_of_birth
self.books: List[Book] = []


Expand Down Expand Up @@ -108,48 +108,61 @@ def basic_tables():

def nested_tables():
"""
Demonstrates how to nest tables using the override_data_style keyword to handle tables with conflicting styles.
Demonstrates how to nest tables using the style_data_text keyword to handle tables with conflicting styles.
In these cases, the inner tables reset the background color applied by the outer AlternatingTable.
It also demonstrates coloring various aspects of tables.
"""

# Create data for this example
person_data: List[Person] = []
person_1 = Person("Bill Anderson", "01/22/1955", "Accounting")
person_1.books.append(Book("Great Expectations", "11/01/2025"))
person_1.books.append(Book("Strange Case of Dr Jekyll and Mr Hyde", "07/16/2026"))
person_1.books.append(Book("Dune", "01/24/2027"))

person_2 = Person("Arthur Smith", "06/11/1974", "Automotive")
person_2.books.append(Book("Nineteen Eighty-Four", "08/07/2025"))
person_2.books.append(Book("Pride and Prejudice", "04/13/2026"))
person_2.books.append(Book("Fahrenheit 451", "07/29/2026"))
person_2.books.append(Book("The Count of Monte Cristo", "10/15/2027"))

person_data.append(person_1)
person_data.append(person_2)

# Define table which presents Person data fields vertically with no header.
author_data: List[Author] = []
author_1 = Author("Frank Herbert", "10/08/1920", "Tacoma, Washington")
author_1.books.append(Book("Dune", "1965"))
author_1.books.append(Book("Dune Messiah", "1969"))
author_1.books.append(Book("Children of Dune", "1976"))
author_1.books.append(Book("God Emperor of Dune", "1981"))
author_1.books.append(Book("Heretics of Dune", "1984"))
author_1.books.append(Book("Chapterhouse: Dune", "1985"))

author_2 = Author("Jane Austen", "12/16/1775", "Steventon, Hampshire, England")
author_2.books.append(Book("Sense and Sensibility", "1811"))
author_2.books.append(Book("Pride and Prejudice", "1813"))
author_2.books.append(Book("Mansfield Park ", "1814"))
author_2.books.append(Book("Emma", "1815"))
author_2.books.append(Book("Northanger Abbey", "1818"))
author_2.books.append(Book("Persuasion", "1818"))
author_2.books.append(Book("Lady Susan", "1871"))

author_data.append(author_1)
author_data.append(author_2)

# Define table which presents Author data fields vertically with no header.
# This will be nested in the parent table.
person_columns: List[Column] = list()
person_columns.append(Column("", width=10))
person_columns.append(Column("", width=20))
author_columns: List[Column] = list()
author_columns.append(Column("", width=14))
author_columns.append(Column("", width=20))

# The text labels in this table will be bold text. They will also be aligned by the table code.
# When styled text is aligned, a TextStyle.RESET_ALL sequence is inserted between the aligned text
# and the fill characters. Therefore, the Person table will contain TextStyle.RESET_ALL sequences,
# and the fill characters. Therefore, the Author table will contain TextStyle.RESET_ALL sequences,
# which would interfere with the background color applied by the parent table. To account for this,
# we will color the Person tables to match the background colors of the parent AlternatingTable's rows
# and set override_data_style to False in the Person column. See below for that.
odd_person_tbl = SimpleTable(person_columns, data_bg=EightBitBg.GRAY_0)
even_person_tbl = SimpleTable(person_columns, data_bg=EightBitBg.GRAY_15)
# we will color the Author tables to match the background colors of the parent AlternatingTable's rows
# and set style_data_text to False in the Author column. See below for that.
odd_author_tbl = SimpleTable(author_columns, data_bg=EightBitBg.GRAY_0)
even_author_tbl = SimpleTable(author_columns, data_bg=EightBitBg.GRAY_15)

# Define AlternatingTable table for books checked out by people in the first table.
# This will also be nested in the parent table.
books_columns: List[Column] = list()
books_columns.append(Column("Title", width=28))
books_columns.append(Column("Due Date", width=10))
books_columns.append(Column("Title", width=25))
books_columns.append(
Column(
"Published",
width=9,
header_horiz_align=HorizontalAlignment.RIGHT,
data_horiz_align=HorizontalAlignment.RIGHT,
)
)

books_tbl = AlternatingTable(
books_columns,
Expand All @@ -160,13 +173,13 @@ def nested_tables():
even_bg=EightBitBg.GRAY_15,
)

# Define parent AlternatingTable which contains Person and Book tables
# Define parent AlternatingTable which contains Author and Book tables
parent_tbl_columns: List[Column] = list()

# Both the Person and Books tables already have background colors. Set override_data_style
# Both the Author and Books tables already have background colors. Set style_data_text
# to False so the parent AlternatingTable does not apply background color to them.
parent_tbl_columns.append(Column("Person", width=odd_person_tbl.total_width(), override_data_style=False))
parent_tbl_columns.append(Column("Books", width=books_tbl.total_width(), override_data_style=False))
parent_tbl_columns.append(Column("Author", width=odd_author_tbl.total_width(), style_data_text=False))
parent_tbl_columns.append(Column("Books", width=books_tbl.total_width(), style_data_text=False))

parent_tbl = AlternatingTable(
parent_tbl_columns,
Expand All @@ -178,26 +191,26 @@ def nested_tables():

# Construct the tables
parent_table_data: List[List[Any]] = []
for row, person in enumerate(person_data, start=1):
# First build the person table and color it based on row number
person_tbl = even_person_tbl if row % 2 == 0 else odd_person_tbl
for row, author in enumerate(author_data, start=1):
# First build the author table and color it based on row number
author_tbl = even_author_tbl if row % 2 == 0 else odd_author_tbl

# This table has three rows and two columns
table_data = [
[ansi.style("Name", bold=True), person.name],
[ansi.style("Birthday", bold=True), person.birthday],
[ansi.style("Department", bold=True), person.department],
[ansi.style("Name", bold=True), author.name],
[ansi.style("Birthday", bold=True), author.birthday],
[ansi.style("Place of Birth", bold=True), author.place_of_birth],
]

# Build the person table string
person_tbl_str = person_tbl.generate_table(table_data, include_header=False, row_spacing=0)
# Build the author table string
author_tbl_str = author_tbl.generate_table(table_data, include_header=False, row_spacing=0)

# Now build this person's book table
table_data = [[book.title, book.due_date] for book in person.books]
# Now build this author's book table
table_data = [[book.title, book.year_published] for book in author.books]
book_tbl_str = books_tbl.generate_table(table_data)

# Add these tables to the parent table's data
parent_table_data.append(['\n' + person_tbl_str, '\n' + book_tbl_str + '\n\n'])
parent_table_data.append(['\n' + author_tbl_str, '\n' + book_tbl_str + '\n\n'])

# Build the parent table
top_table_str = parent_tbl.generate_table(parent_table_data)
Expand Down
30 changes: 15 additions & 15 deletions tests/test_table_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,18 @@ def test_column_creation():
tc = TableCreator([c])
assert tc.cols[0].width == ansi.style_aware_wcswidth("line with tabs")

# Add basic tests for override_header_style and override_data_style to make sure these members don't get removed.
# Add basic tests for style_header_text and style_data_text to make sure these members don't get removed.
c = Column("Column 1")
assert c.override_header_style is True
assert c.override_data_style is True
assert c.style_header_text is True
assert c.style_data_text is True

c = Column("Column 1", override_header_style=False)
assert c.override_header_style is False
assert c.override_data_style is True
c = Column("Column 1", style_header_text=False)
assert c.style_header_text is False
assert c.style_data_text is True

c = Column("Column 1", override_data_style=False)
assert c.override_header_style is True
assert c.override_data_style is False
c = Column("Column 1", style_data_text=False)
assert c.style_header_text is True
assert c.style_data_text is False


def test_column_alignment():
Expand Down Expand Up @@ -465,9 +465,9 @@ def test_simple_table_creation():
'\x1b[0m\x1b[104mCol 1 Row 2\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m\x1b[104mCol 2 Row 2\x1b[49m\x1b[0m\x1b[104m \x1b[49m\x1b[0m'
)

# Make sure SimpleTable respects override_header_style override_data_style flags.
# Make sure SimpleTable respects style_header_text and style_data_text flags.
# Don't apply parent table's background colors to header or data text in second column.
st = SimpleTable([column_1, Column("Col 2", width=16, override_header_style=False, override_data_style=False)],
st = SimpleTable([column_1, Column("Col 2", width=16, style_header_text=False, style_data_text=False)],
divider_char=None, header_bg=Bg.GREEN, data_bg=Bg.LIGHT_BLUE)
table = st.generate_table(row_data)
assert table == (
Expand Down Expand Up @@ -576,9 +576,9 @@ def test_bordered_table_creation():
'\x1b[93m╚═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╧═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╝\x1b[39m'
)

# Make sure BorderedTable respects override_header_style override_data_style flags.
# Make sure BorderedTable respects style_header_text and style_data_text flags.
# Don't apply parent table's background colors to header or data text in second column.
bt = BorderedTable([column_1, Column("Col 2", width=15, override_header_style=False, override_data_style=False)],
bt = BorderedTable([column_1, Column("Col 2", width=15, style_header_text=False, style_data_text=False)],
header_bg=Bg.GREEN, data_bg=Bg.LIGHT_BLUE)
table = bt.generate_table(row_data)
assert table == (
Expand Down Expand Up @@ -702,9 +702,9 @@ def test_alternating_table_creation():
'\x1b[93m╚═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╧═\x1b[39m\x1b[0m\x1b[0m\x1b[93m═══════════════\x1b[39m\x1b[0m\x1b[93m═╝\x1b[39m'
)

# Make sure AlternatingTable respects override_header_style override_data_style flags.
# Make sure AlternatingTable respects style_header_text and style_data_text flags.
# Don't apply parent table's background colors to header or data text in second column.
at = AlternatingTable([column_1, Column("Col 2", width=15, override_header_style=False, override_data_style=False)],
at = AlternatingTable([column_1, Column("Col 2", width=15, style_header_text=False, style_data_text=False)],
header_bg=Bg.GREEN, odd_bg=Bg.LIGHT_BLUE, even_bg=Bg.LIGHT_RED)
table = at.generate_table(row_data)
assert table == (
Expand Down

0 comments on commit 0d60017

Please sign in to comment.