* DataTable sort by function (or other callable) The `DataTable` widget now takes the `by` argument instead of `columns`, allowing the table to also be sorted using a custom function (or other callable). This is a breaking change since it requires all calls to the `sort` method to include an iterable of key(s) (or a singular function/callable). Covers #2261 using [suggested function signature](https://github.com/Textualize/textual/pull/2512#issuecomment-1580277771) from @darrenburns on PR #2512. * argument change and functionaloty update Changed back to orinal `columns` argument and added a new `key` argument which takes a function (or other callable). This allows the PR to NOT BE a breaking change. * better example for docs - Updated the example file for the docs to better show the functionality of the change (especially when using `columns` and `key` together). - Added one new tests to cover a similar situation to the example changes * removed unecessary code from example - the sort by clicked column function was bloat in my opinion * requested changes * simplify method and terminology * combine key_wrapper and default sort * Removing some tests from DataTable.sort as duplicates. Ensure there is test coverage of the case where a key, but no columns, is passed to DataTable.sort. * Remove unused import * Fix merge issues in CHANGELOG, update DataTable sort-by-key changelog PR link --------- Co-authored-by: Darren Burns <darrenburns@users.noreply.github.com> Co-authored-by: Darren Burns <darrenb900@gmail.com>
92 lines
3.2 KiB
Python
92 lines
3.2 KiB
Python
from rich.text import Text
|
|
|
|
from textual.app import App, ComposeResult
|
|
from textual.widgets import DataTable, Footer
|
|
|
|
ROWS = [
|
|
("lane", "swimmer", "country", "time 1", "time 2"),
|
|
(4, "Joseph Schooling", Text("Singapore", style="italic"), 50.39, 51.84),
|
|
(2, "Michael Phelps", Text("United States", style="italic"), 50.39, 51.84),
|
|
(5, "Chad le Clos", Text("South Africa", style="italic"), 51.14, 51.73),
|
|
(6, "László Cseh", Text("Hungary", style="italic"), 51.14, 51.58),
|
|
(3, "Li Zhuhao", Text("China", style="italic"), 51.26, 51.26),
|
|
(8, "Mehdy Metella", Text("France", style="italic"), 51.58, 52.15),
|
|
(7, "Tom Shields", Text("United States", style="italic"), 51.73, 51.12),
|
|
(1, "Aleksandr Sadovnikov", Text("Russia", style="italic"), 51.84, 50.85),
|
|
(10, "Darren Burns", Text("Scotland", style="italic"), 51.84, 51.55),
|
|
]
|
|
|
|
|
|
class TableApp(App):
|
|
BINDINGS = [
|
|
("a", "sort_by_average_time", "Sort By Average Time"),
|
|
("n", "sort_by_last_name", "Sort By Last Name"),
|
|
("c", "sort_by_country", "Sort By Country"),
|
|
("d", "sort_by_columns", "Sort By Columns (Only)"),
|
|
]
|
|
|
|
current_sorts: set = set()
|
|
|
|
def compose(self) -> ComposeResult:
|
|
yield DataTable()
|
|
yield Footer()
|
|
|
|
def on_mount(self) -> None:
|
|
table = self.query_one(DataTable)
|
|
for col in ROWS[0]:
|
|
table.add_column(col, key=col)
|
|
table.add_rows(ROWS[1:])
|
|
|
|
def sort_reverse(self, sort_type: str):
|
|
"""Determine if `sort_type` is ascending or descending."""
|
|
reverse = sort_type in self.current_sorts
|
|
if reverse:
|
|
self.current_sorts.remove(sort_type)
|
|
else:
|
|
self.current_sorts.add(sort_type)
|
|
return reverse
|
|
|
|
def action_sort_by_average_time(self) -> None:
|
|
"""Sort DataTable by average of times (via a function) and
|
|
passing of column data through positional arguments."""
|
|
|
|
def sort_by_average_time_then_last_name(row_data):
|
|
name, *scores = row_data
|
|
return (sum(scores) / len(scores), name.split()[-1])
|
|
|
|
table = self.query_one(DataTable)
|
|
table.sort(
|
|
"swimmer",
|
|
"time 1",
|
|
"time 2",
|
|
key=sort_by_average_time_then_last_name,
|
|
reverse=self.sort_reverse("time"),
|
|
)
|
|
|
|
def action_sort_by_last_name(self) -> None:
|
|
"""Sort DataTable by last name of swimmer (via a lambda)."""
|
|
table = self.query_one(DataTable)
|
|
table.sort(
|
|
"swimmer",
|
|
key=lambda swimmer: swimmer.split()[-1],
|
|
reverse=self.sort_reverse("swimmer"),
|
|
)
|
|
|
|
def action_sort_by_country(self) -> None:
|
|
"""Sort DataTable by country which is a `Rich.Text` object."""
|
|
table = self.query_one(DataTable)
|
|
table.sort(
|
|
"country",
|
|
key=lambda country: country.plain,
|
|
reverse=self.sort_reverse("country"),
|
|
)
|
|
|
|
def action_sort_by_columns(self) -> None:
|
|
"""Sort DataTable without a key."""
|
|
table = self.query_one(DataTable)
|
|
table.sort("swimmer", "lane", reverse=self.sort_reverse("columns"))
|
|
|
|
|
|
app = TableApp()
|
|
if __name__ == "__main__":
|
|
app.run()
|