mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Improving data table documentation (#2279)
* Improving data table documentation * More info on DataTable * Add note on retrieving cursor coordinate * Add note on DataTable supporting more than just strings * Add note on cell styling and justifying - common question * Slight rewording * Explaining what "row labels" are. * Update docs/widgets/data_table.md Co-authored-by: Will McGugan <willmcgugan@gmail.com> * Update docs/widgets/data_table.md Co-authored-by: Will McGugan <willmcgugan@gmail.com> * Update docs/widgets/data_table.md Co-authored-by: Will McGugan <willmcgugan@gmail.com> * Update docs/widgets/data_table.md Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com> * Update docs/widgets/data_table.md Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com> * Update docs/widgets/data_table.md Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com> * Update docs/widgets/data_table.md Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com> * Update docs/widgets/data_table.md Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com> * Improve data table fixed rows/columns docs * Update some examples --------- Co-authored-by: Will McGugan <willmcgugan@gmail.com> Co-authored-by: Rodrigo Girão Serrão <5621605+rodrigogiraoserrao@users.noreply.github.com>
This commit is contained in:
@@ -21,9 +21,8 @@ class TableApp(App):
|
|||||||
|
|
||||||
def on_mount(self) -> None:
|
def on_mount(self) -> None:
|
||||||
table = self.query_one(DataTable)
|
table = self.query_one(DataTable)
|
||||||
rows = iter(ROWS)
|
table.add_columns(*ROWS[0])
|
||||||
table.add_columns(*next(rows))
|
table.add_rows(ROWS[1:])
|
||||||
table.add_rows(rows)
|
|
||||||
|
|
||||||
|
|
||||||
app = TableApp()
|
app = TableApp()
|
||||||
|
|||||||
40
docs/examples/widgets/data_table_cursors.py
Normal file
40
docs/examples/widgets/data_table_cursors.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
from itertools import cycle
|
||||||
|
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.widgets import DataTable
|
||||||
|
|
||||||
|
ROWS = [
|
||||||
|
("lane", "swimmer", "country", "time"),
|
||||||
|
(4, "Joseph Schooling", "Singapore", 50.39),
|
||||||
|
(2, "Michael Phelps", "United States", 51.14),
|
||||||
|
(5, "Chad le Clos", "South Africa", 51.14),
|
||||||
|
(6, "László Cseh", "Hungary", 51.14),
|
||||||
|
(3, "Li Zhuhao", "China", 51.26),
|
||||||
|
(8, "Mehdy Metella", "France", 51.58),
|
||||||
|
(7, "Tom Shields", "United States", 51.73),
|
||||||
|
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
|
||||||
|
(10, "Darren Burns", "Scotland", 51.84),
|
||||||
|
]
|
||||||
|
|
||||||
|
cursors = cycle(["column", "row", "cell"])
|
||||||
|
|
||||||
|
|
||||||
|
class TableApp(App):
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield DataTable()
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
table = self.query_one(DataTable)
|
||||||
|
table.cursor_type = next(cursors)
|
||||||
|
table.zebra_stripes = True
|
||||||
|
table.add_columns(*ROWS[0])
|
||||||
|
table.add_rows(ROWS[1:])
|
||||||
|
|
||||||
|
def key_c(self):
|
||||||
|
table = self.query_one(DataTable)
|
||||||
|
table.cursor_type = next(cursors)
|
||||||
|
|
||||||
|
|
||||||
|
app = TableApp()
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run()
|
||||||
25
docs/examples/widgets/data_table_fixed.py
Normal file
25
docs/examples/widgets/data_table_fixed.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.widgets import DataTable
|
||||||
|
|
||||||
|
|
||||||
|
class TableApp(App):
|
||||||
|
CSS = "DataTable {height: 1fr}"
|
||||||
|
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield DataTable()
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
table = self.query_one(DataTable)
|
||||||
|
table.focus()
|
||||||
|
table.add_columns("A", "B", "C")
|
||||||
|
for number in range(1, 100):
|
||||||
|
table.add_row(str(number), str(number * 2), str(number * 3))
|
||||||
|
table.fixed_rows = 2
|
||||||
|
table.fixed_columns = 1
|
||||||
|
table.cursor_type = "row"
|
||||||
|
table.zebra_stripes = True
|
||||||
|
|
||||||
|
|
||||||
|
app = TableApp()
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run()
|
||||||
34
docs/examples/widgets/data_table_labels.py
Normal file
34
docs/examples/widgets/data_table_labels.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
from rich.text import Text
|
||||||
|
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.widgets import DataTable
|
||||||
|
|
||||||
|
ROWS = [
|
||||||
|
("lane", "swimmer", "country", "time"),
|
||||||
|
(4, "Joseph Schooling", "Singapore", 50.39),
|
||||||
|
(2, "Michael Phelps", "United States", 51.14),
|
||||||
|
(5, "Chad le Clos", "South Africa", 51.14),
|
||||||
|
(6, "László Cseh", "Hungary", 51.14),
|
||||||
|
(3, "Li Zhuhao", "China", 51.26),
|
||||||
|
(8, "Mehdy Metella", "France", 51.58),
|
||||||
|
(7, "Tom Shields", "United States", 51.73),
|
||||||
|
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
|
||||||
|
(10, "Darren Burns", "Scotland", 51.84),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TableApp(App):
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield DataTable()
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
table = self.query_one(DataTable)
|
||||||
|
table.add_columns(*ROWS[0])
|
||||||
|
for number, row in enumerate(ROWS[1:], start=1):
|
||||||
|
label = Text(str(number), style="#B0FC38 italic")
|
||||||
|
table.add_row(*row, label=label)
|
||||||
|
|
||||||
|
|
||||||
|
app = TableApp()
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run()
|
||||||
37
docs/examples/widgets/data_table_renderables.py
Normal file
37
docs/examples/widgets/data_table_renderables.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from rich.text import Text
|
||||||
|
|
||||||
|
from textual.app import App, ComposeResult
|
||||||
|
from textual.widgets import DataTable
|
||||||
|
|
||||||
|
ROWS = [
|
||||||
|
("lane", "swimmer", "country", "time"),
|
||||||
|
(4, "Joseph Schooling", "Singapore", 50.39),
|
||||||
|
(2, "Michael Phelps", "United States", 51.14),
|
||||||
|
(5, "Chad le Clos", "South Africa", 51.14),
|
||||||
|
(6, "László Cseh", "Hungary", 51.14),
|
||||||
|
(3, "Li Zhuhao", "China", 51.26),
|
||||||
|
(8, "Mehdy Metella", "France", 51.58),
|
||||||
|
(7, "Tom Shields", "United States", 51.73),
|
||||||
|
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
|
||||||
|
(10, "Darren Burns", "Scotland", 51.84),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TableApp(App):
|
||||||
|
def compose(self) -> ComposeResult:
|
||||||
|
yield DataTable()
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
table = self.query_one(DataTable)
|
||||||
|
table.add_columns(*ROWS[0])
|
||||||
|
for row in ROWS[1:]:
|
||||||
|
# Adding styled and justified `Text` objects instead of plain strings.
|
||||||
|
styled_row = [
|
||||||
|
Text(str(cell), style="italic #03AC13", justify="right") for cell in row
|
||||||
|
]
|
||||||
|
table.add_row(*styled_row)
|
||||||
|
|
||||||
|
|
||||||
|
app = TableApp()
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run()
|
||||||
@@ -5,9 +5,13 @@ A table widget optimized for displaying a lot of data.
|
|||||||
- [x] Focusable
|
- [x] Focusable
|
||||||
- [ ] Container
|
- [ ] Container
|
||||||
|
|
||||||
## Example
|
## Guide
|
||||||
|
|
||||||
The example below populates a table with CSV data.
|
### Adding data
|
||||||
|
|
||||||
|
The following example shows how to fill a table with data.
|
||||||
|
First, we use [add_columns][textual.widgets.DataTable.add_rows] to include the `lane`, `swimmer`, `country`, and `time` columns in the table.
|
||||||
|
After that, we use the [add_rows][textual.widgets.DataTable.add_rows] method to insert the rows into the table.
|
||||||
|
|
||||||
=== "Output"
|
=== "Output"
|
||||||
|
|
||||||
@@ -20,11 +24,141 @@ The example below populates a table with CSV data.
|
|||||||
--8<-- "docs/examples/widgets/data_table.py"
|
--8<-- "docs/examples/widgets/data_table.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To add a single row or column use [add_row][textual.widgets.DataTable.add_row] and [add_column][textual.widgets.DataTable.add_column], respectively.
|
||||||
|
|
||||||
|
#### Styling and justifying cells
|
||||||
|
|
||||||
|
Cells can contain more than just plain strings - [Rich](https://rich.readthedocs.io/en/stable/introduction.html) renderables such as [`Text`](https://rich.readthedocs.io/en/stable/text.html?highlight=Text#rich-text) are also supported.
|
||||||
|
`Text` objects provide an easy way to style and justify cell content:
|
||||||
|
|
||||||
|
=== "Output"
|
||||||
|
|
||||||
|
```{.textual path="docs/examples/widgets/data_table_renderables.py"}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "data_table_renderables.py"
|
||||||
|
|
||||||
|
```python
|
||||||
|
--8<-- "docs/examples/widgets/data_table_renderables.py"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Keys
|
||||||
|
|
||||||
|
When adding a row to the table, you can supply a _key_ to [add_row][textual.widgets.DataTable.add_row].
|
||||||
|
A key is a unique identifier for that row.
|
||||||
|
If you don't supply a key, Textual will generate one for you and return it from `add_row`.
|
||||||
|
This key can later be used to reference the row, regardless of its current position in the table.
|
||||||
|
|
||||||
|
When working with data from a database, for example, you may wish to set the row `key` to the primary key of the data to ensure uniqueness.
|
||||||
|
The method [add_column][textual.widgets.DataTable.add_column] also accepts a `key` argument and works similarly.
|
||||||
|
|
||||||
|
Keys are important because cells in a data table can change location due to factors like row deletion and sorting.
|
||||||
|
Thus, using keys instead of coordinates allows us to refer to data without worrying about its current location in the table.
|
||||||
|
|
||||||
|
If you want to change the table based solely on coordinates, you can use the [coordinate_to_cell_key][textual.widgets.DataTable.coordinate_to_cell_key] method to convert a coordinate to a _cell key_, which is a `(row_key, column_key)` pair.
|
||||||
|
|
||||||
|
### Cursors
|
||||||
|
|
||||||
|
The coordinate of the cursor is exposed via the `cursor_coordinate` reactive attribute.
|
||||||
|
Three types of cursors are supported: `cell`, `row`, and `column`.
|
||||||
|
Change the cursor type by assigning to the `cursor_type` reactive attribute.
|
||||||
|
|
||||||
|
=== "Column Cursor"
|
||||||
|
|
||||||
|
```{.textual path="docs/examples/widgets/data_table_cursors.py"}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Row Cursor"
|
||||||
|
|
||||||
|
```{.textual path="docs/examples/widgets/data_table_cursors.py" press="c"}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Cell Cursor"
|
||||||
|
|
||||||
|
```{.textual path="docs/examples/widgets/data_table_cursors.py" press="c,c"}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "data_table_cursors.py"
|
||||||
|
|
||||||
|
```python
|
||||||
|
--8<-- "docs/examples/widgets/data_table_cursors.py"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can change the position of the cursor using the arrow keys, ++page-up++, ++page-down++, ++home++ and ++end++,
|
||||||
|
or by assigning to the `cursor_coordinate` reactive attribute.
|
||||||
|
|
||||||
|
### Updating data
|
||||||
|
|
||||||
|
Cells can be updated in the `DataTable` by using the [update_cell][textual.widgets.DataTable.update_cell] and [update_cell_at][textual.widgets.DataTable.update_cell_at] methods.
|
||||||
|
|
||||||
|
### Removing data
|
||||||
|
|
||||||
|
To remove all data in the table, use the [clear][textual.widgets.DataTable.clear] method.
|
||||||
|
To remove individual rows, use [remove_row][textual.widgets.DataTable.remove_row].
|
||||||
|
The `remove_row` method accepts a `key` argument, which identifies the row to be removed.
|
||||||
|
|
||||||
|
If you wish to remove the row below the cursor in the `DataTable`, use `coordinate_to_cell_key` to get the row key of
|
||||||
|
the row under the current `cursor_coordinate`, then supply this key to `remove_row`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Get the keys for the row and column under the cursor.
|
||||||
|
row_key, _ = table.coordinate_to_cell_key(table.cursor_coordinate)
|
||||||
|
# Supply the row key to `remove_row` to delete the row.
|
||||||
|
table.remove_row(row_key)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fixed data
|
||||||
|
|
||||||
|
You can fix a number of rows and columns in place, keeping them pinned to the top and left of the table respectively.
|
||||||
|
To do this, assign an integer to the `fixed_rows` or `fixed_columns` reactive attributes of the `DataTable`.
|
||||||
|
|
||||||
|
=== "Fixed data"
|
||||||
|
|
||||||
|
```{.textual path="docs/examples/widgets/data_table_fixed.py" press="end"}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "data_table_fixed.py"
|
||||||
|
|
||||||
|
```python
|
||||||
|
--8<-- "docs/examples/widgets/data_table_fixed.py"
|
||||||
|
```
|
||||||
|
|
||||||
|
In the example above, we set `fixed_rows` to `2`, and `fixed_columns` to `1`,
|
||||||
|
meaning the first two rows and the leftmost column do not scroll - they always remain
|
||||||
|
visible as you scroll through the data table.
|
||||||
|
|
||||||
|
### Sorting
|
||||||
|
|
||||||
|
The `DataTable` can be sorted using the [sort][textual.widgets.DataTable.sort] method.
|
||||||
|
In order to sort your data by a column, you must have supplied a `key` to the `add_column` method
|
||||||
|
when you added it.
|
||||||
|
You can then pass this key to the `sort` method to sort by that column.
|
||||||
|
Additionally, you can sort by multiple columns by passing multiple keys to `sort`.
|
||||||
|
|
||||||
|
### Labelled rows
|
||||||
|
|
||||||
|
A "label" can be attached to a row using the [add_row][textual.widgets.DataTable.add_row] method.
|
||||||
|
This will add an extra column to the left of the table which the cursor cannot interact with.
|
||||||
|
This column is similar to the leftmost column in a spreadsheet containing the row numbers.
|
||||||
|
The example below shows how to attach simple numbered labels to rows.
|
||||||
|
|
||||||
|
=== "Labelled rows"
|
||||||
|
|
||||||
|
```{.textual path="docs/examples/widgets/data_table_labels.py"}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "data_table_labels.py"
|
||||||
|
|
||||||
|
```python
|
||||||
|
--8<-- "docs/examples/widgets/data_table_labels.py"
|
||||||
|
```
|
||||||
|
|
||||||
## Reactive Attributes
|
## Reactive Attributes
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
| ------------------- | ------------------------------------------- | ------------------ | ----------------------------------------------------- |
|
|---------------------|---------------------------------------------|--------------------|-------------------------------------------------------|
|
||||||
| `show_header` | `bool` | `True` | Show the table header |
|
| `show_header` | `bool` | `True` | Show the table header |
|
||||||
|
| `show_row_labels` | `bool` | `True` | Show the row labels (if applicable) |
|
||||||
| `fixed_rows` | `int` | `0` | Number of fixed rows (rows which do not scroll) |
|
| `fixed_rows` | `int` | `0` | Number of fixed rows (rows which do not scroll) |
|
||||||
| `fixed_columns` | `int` | `0` | Number of fixed columns (columns which do not scroll) |
|
| `fixed_columns` | `int` | `0` | Number of fixed columns (columns which do not scroll) |
|
||||||
| `zebra_stripes` | `bool` | `False` | Display alternating colors on rows |
|
| `zebra_stripes` | `bool` | `False` | Display alternating colors on rows |
|
||||||
@@ -43,6 +177,7 @@ The example below populates a table with CSV data.
|
|||||||
- [DataTable.ColumnHighlighted][textual.widgets.DataTable.ColumnHighlighted]
|
- [DataTable.ColumnHighlighted][textual.widgets.DataTable.ColumnHighlighted]
|
||||||
- [DataTable.ColumnSelected][textual.widgets.DataTable.ColumnSelected]
|
- [DataTable.ColumnSelected][textual.widgets.DataTable.ColumnSelected]
|
||||||
- [DataTable.HeaderSelected][textual.widgets.DataTable.HeaderSelected]
|
- [DataTable.HeaderSelected][textual.widgets.DataTable.HeaderSelected]
|
||||||
|
- [DataTable.RowLabelSelected][textual.widgets.DataTable.RowLabelSelected]
|
||||||
|
|
||||||
## Bindings
|
## Bindings
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user