- Fixed DataTable row selection and event handling - Added explicit column keys to prevent auto-generated keys - Implemented row-to-flow mapping for reliable selection tracking - Converted left metrics panel to horizontal top bar - Fixed all missing FlowStats/EnhancedAnalysisData attributes - Created comprehensive Textual API documentation in Documentation/textual/ - Added validation checklist to prevent future API mismatches - Preserved cursor position during data refreshes - Fixed RowKey type handling and event names The TUI now properly handles flow selection, displays metrics in a compact top bar, and correctly correlates selected rows with the details pane.
242 lines
5.7 KiB
Markdown
242 lines
5.7 KiB
Markdown
# DataTable Widget API Reference
|
|
|
|
## Overview
|
|
|
|
The DataTable widget displays tabular data with support for selection, scrolling, and styling.
|
|
|
|
## Import
|
|
|
|
```python
|
|
from textual.widgets import DataTable
|
|
```
|
|
|
|
## Class Hierarchy
|
|
|
|
```
|
|
Widget → ScrollView → DataTable
|
|
```
|
|
|
|
## Constructor
|
|
|
|
```python
|
|
DataTable(
|
|
*,
|
|
show_header: bool = True,
|
|
fixed_rows: int = 0,
|
|
fixed_columns: int = 0,
|
|
zebra_stripes: bool = False,
|
|
header_height: int = 1,
|
|
show_cursor: bool = True,
|
|
cursor_foreground_priority: Literal["renderable", "css"] = "renderable",
|
|
cursor_background_priority: Literal["renderable", "css"] = "renderable",
|
|
cursor_type: CursorType = "cell",
|
|
cell_padding: int = 1,
|
|
name: str | None = None,
|
|
id: str | None = None,
|
|
classes: str | None = None,
|
|
disabled: bool = False
|
|
)
|
|
```
|
|
|
|
## Key Properties
|
|
|
|
### Cursor Properties
|
|
- `cursor_row: int` - Current cursor row (0-based)
|
|
- `cursor_column: int` - Current cursor column (0-based)
|
|
- `cursor_coordinate: Coordinate` - (row, column) tuple
|
|
|
|
### Table Properties
|
|
- `rows: Mapping[RowKey, Row]` - Dictionary of rows
|
|
- `columns: list[Column]` - List of columns
|
|
- `row_count: int` - Number of rows
|
|
- `column_count: int` - Number of columns
|
|
|
|
## Methods
|
|
|
|
### Adding Data
|
|
|
|
```python
|
|
# Add columns
|
|
add_column(label: TextType, *, width: int | None = None, key: str | None = None, default: CellType | None = None) -> ColumnKey
|
|
|
|
# Add multiple columns
|
|
add_columns(*labels: TextType) -> list[ColumnKey]
|
|
|
|
# Add a single row
|
|
add_row(*cells: CellType, height: int | None = 1, key: str | None = None, label: TextType | None = None) -> RowKey
|
|
|
|
# Add multiple rows
|
|
add_rows(rows: Iterable[Iterable[CellType]]) -> list[RowKey]
|
|
```
|
|
|
|
### Modifying Data
|
|
|
|
```python
|
|
# Update a cell
|
|
update_cell(row_key: RowKey, column_key: ColumnKey, value: CellType, *, update_width: bool = False) -> None
|
|
|
|
# Update cell at coordinate
|
|
update_cell_at(coordinate: Coordinate, value: CellType, *, update_width: bool = False) -> None
|
|
|
|
# Remove row
|
|
remove_row(row_key: RowKey) -> None
|
|
|
|
# Remove column
|
|
remove_column(column_key: ColumnKey) -> None
|
|
|
|
# Clear all data
|
|
clear(columns: bool = False) -> Self
|
|
```
|
|
|
|
### Navigation
|
|
|
|
```python
|
|
# Move cursor
|
|
move_cursor(row: int | None = None, column: int | None = None, animate: bool = True, scroll: bool = True) -> None
|
|
|
|
# Scroll to coordinate
|
|
scroll_to(x: float | None = None, y: float | None = None, *, animate: bool = True, speed: float | None = None, duration: float | None = None, ...) -> None
|
|
```
|
|
|
|
### Sorting
|
|
|
|
```python
|
|
# Sort by column(s)
|
|
sort(*columns: ColumnKey | str, reverse: bool = False) -> Self
|
|
|
|
# Sort with custom key
|
|
sort(key: Callable[[Any], Any], *, reverse: bool = False) -> Self
|
|
```
|
|
|
|
## Events
|
|
|
|
### Selection Events
|
|
|
|
```python
|
|
class RowSelected(Message):
|
|
"""Posted when a row is selected (Enter key)."""
|
|
row_key: RowKey
|
|
row_index: int
|
|
|
|
class CellSelected(Message):
|
|
"""Posted when a cell is selected."""
|
|
coordinate: Coordinate
|
|
cell_key: CellKey
|
|
|
|
class RowHighlighted(Message):
|
|
"""Posted when cursor highlights a row."""
|
|
row_key: RowKey
|
|
row_index: int
|
|
|
|
class CellHighlighted(Message):
|
|
"""Posted when cursor highlights a cell."""
|
|
coordinate: Coordinate
|
|
cell_key: CellKey
|
|
|
|
class ColumnHighlighted(Message):
|
|
"""Posted when cursor highlights a column."""
|
|
column_key: ColumnKey
|
|
column_index: int
|
|
```
|
|
|
|
### Header Events
|
|
|
|
```python
|
|
class HeaderSelected(Message):
|
|
"""Posted when a column header is clicked."""
|
|
column_key: ColumnKey
|
|
column_index: int
|
|
label: Text
|
|
|
|
class RowLabelSelected(Message):
|
|
"""Posted when a row label is clicked."""
|
|
row_key: RowKey
|
|
row_index: int
|
|
label: Text
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Creating a DataTable
|
|
|
|
```python
|
|
class MyWidget(Widget):
|
|
def compose(self) -> ComposeResult:
|
|
yield DataTable(id="my-table")
|
|
|
|
def on_mount(self) -> None:
|
|
table = self.query_one("#my-table", DataTable)
|
|
table.add_columns("Name", "Value", "Status")
|
|
table.add_row("Item 1", "100", "Active", key="item_1")
|
|
```
|
|
|
|
### Handling Selection
|
|
|
|
```python
|
|
def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
|
|
# React to cursor movement
|
|
self.selected_row = event.row_key
|
|
|
|
def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
|
|
# React to Enter key press
|
|
self.process_selection(event.row_key)
|
|
```
|
|
|
|
### Preserving Cursor Position
|
|
|
|
```python
|
|
def refresh_table(self):
|
|
table = self.query_one(DataTable)
|
|
|
|
# Save position
|
|
cursor_row = table.cursor_row
|
|
selected_key = list(table.rows.keys())[cursor_row] if table.rows else None
|
|
|
|
# Update data
|
|
table.clear()
|
|
# ... add new data ...
|
|
|
|
# Restore position
|
|
if selected_key and selected_key in table.rows:
|
|
row_index = list(table.rows.keys()).index(selected_key)
|
|
table.move_cursor(row=row_index, animate=False)
|
|
```
|
|
|
|
## Styling
|
|
|
|
DataTable supports CSS styling but does NOT have:
|
|
- `set_row_style()` method
|
|
- `set_cell_style()` method
|
|
|
|
Use CSS classes and selectors instead:
|
|
|
|
```css
|
|
DataTable > .datatable--cursor {
|
|
background: $primary 30%;
|
|
}
|
|
|
|
DataTable > .datatable--header {
|
|
text-style: bold;
|
|
}
|
|
```
|
|
|
|
## Type Hints
|
|
|
|
```python
|
|
from textual.widgets.data_table import RowKey, ColumnKey, CellKey
|
|
from textual.coordinate import Coordinate
|
|
|
|
# RowKey and ColumnKey are not strings - convert with str() if needed
|
|
row_key_str = str(row_key)
|
|
```
|
|
|
|
## Common Errors and Solutions
|
|
|
|
1. **TypeError: 'RowKey' is not iterable**
|
|
- Solution: Convert to string first: `str(row_key)`
|
|
|
|
2. **AttributeError: 'DataTable' has no attribute 'set_row_style'**
|
|
- Solution: Use CSS styling instead
|
|
|
|
3. **Event not firing**
|
|
- Check handler name: `on_data_table_row_highlighted` not `on_data_table_cursor_moved` |