Skip to content

DataTable

A table widget optimized for displaying a lot of data.

  • Focusable
  • Container

Guide

Adding data

The following example shows how to fill a table with data. First, we use add_columns to include the lane, swimmer, country, and time columns in the table. After that, we use the add_rows method to insert the rows into the table.

TableApp  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 

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])
        table.add_rows(ROWS[1:])


app = TableApp()
if __name__ == "__main__":
    app.run()

To add a single row or column use add_row and add_column, respectively.

Styling and justifying cells

Cells can contain more than just plain strings - Rich renderables such as Text are also supported. Text objects provide an easy way to style and justify cell content:

TableApp  lane  swimmer               country        time      4    Joseph Schooling    Singapore50.39    2      Michael PhelpsUnited States51.14    5        Chad le Clos South Africa51.14    6         László Cseh      Hungary51.14    3           Li Zhuhao        China51.26    8       Mehdy Metella       France51.58    7         Tom ShieldsUnited States51.73    1Aleksandr Sadovnikov       Russia51.84   10        Darren Burns     Scotland51.84

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()

Keys

When adding a row to the table, you can supply a key to 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 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 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.

TableApp  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 

TableApp  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 

TableApp  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 

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()

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 and update_cell_at methods.

Removing data

To remove all data in the table, use the clear method. To remove individual rows, use 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:

# 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)

Removing columns

To remove individual columns, use remove_column. The remove_column method accepts a key argument, which identifies the column to be removed.

You can remove the column below the cursor using the same coordinate_to_cell_key method described above:

# Get the keys for the row and column under the cursor.
_, column_key = table.coordinate_to_cell_key(table.cursor_coordinate)
# Supply the column key to `column_row` to delete the column.
table.remove_column(column_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.

TableApp  A   B    C     1   2    3     2   4    6     79  158  237   80  160  240   81  162  243   82  164  246   83  166  249   84  168  252   85  170  255   86  172  258   87  174  261   88  176  264   89  178  267   90  180  270   91  182  273   92  184  276   93  186  279   94  188  282 ▇▇  95  190  285   96  192  288   97  194  291   98  196  294   99  198  297 

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()

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 method. In order to sort your data by a column, you can provide the key you supplied to the add_column method or a ColumnKey. You can then pass one more column keys to the sort method to sort by one or more columns.

Additionally, you can sort your DataTable with a custom function (or other callable) via the key argument. Similar to the key parameter of the built-in sorted() function, your function (or other callable) should take a single argument (row) and return a key to use for sorting purposes.

Providing both columns and key will limit the row information sent to your key function (or other callable) to only the columns specified.

TableApp  lane  swimmer               country        time 1  time 2   4     Joseph Schooling     Singapore 50.39   51.84    2     Michael Phelps       United States 50.39   51.84    5     Chad le Clos         South Africa 51.14   51.73    6     László Cseh          Hungary 51.14   51.58    3     Li Zhuhao            China 51.26   51.26    8     Mehdy Metella        France 51.58   52.15    7     Tom Shields          United States 51.73   51.12    1     Aleksandr Sadovnikov Russia 51.84   50.85    10    Darren Burns         Scotland 51.84   51.55    A  Sort By Average Time  N  Sort By Last Name  C  Sort By Country  D  Sort By …

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()

Labelled rows

A "label" can be attached to a row using the 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.

TableApp  lane  swimmer               country        time   1 4     Joseph Schooling      Singapore      50.39  2 2     Michael Phelps        United States  51.14  3 5     Chad le Clos          South Africa   51.14  4 6     László Cseh           Hungary        51.14  5 3     Li Zhuhao             China          51.26  6 8     Mehdy Metella         France         51.58  7 7     Tom Shields           United States  51.73  8 1     Aleksandr Sadovnikov  Russia         51.84  9 10    Darren Burns          Scotland       51.84 

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()

Reactive Attributes

Name Type Default Description
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_columns int 0 Number of fixed columns (columns which do not scroll)
zebra_stripes bool False Display alternating colors on rows
header_height int 1 Height of header row
show_cursor bool True Show the cursor
cursor_type str "cell" One of "cell", "row", "column", or "none"
cursor_coordinate Coordinate Coordinate(0, 0) The current coordinate of the cursor
hover_coordinate Coordinate Coordinate(0, 0) The coordinate the mouse cursor is above

Messages

Bindings

The data table widget defines the following bindings:

Key(s) Description
enter Select cells under the cursor.
up Move the cursor up.
down Move the cursor down.
right Move the cursor right.
left Move the cursor left.

Component Classes

The data table widget provides the following component classes:

Class Description
datatable--cursor Target the cursor.
datatable--hover Target the cells under the hover cursor.
datatable--fixed Target fixed columns and fixed rows.
datatable--fixed-cursor Target highlighted and fixed columns or header.
datatable--header Target the header of the data table.
datatable--header-cursor Target cells highlighted by the cursor.
datatable--header-hover Target hovered header or row label cells.
datatable--even-row Target even rows (row indices start at 0).
datatable--odd-row Target odd rows (row indices start at 0).

textual.widgets.DataTable class

def __init__(
    self,
    *,
    show_header=True,
    show_row_labels=True,
    fixed_rows=0,
    fixed_columns=0,
    zebra_stripes=False,
    header_height=1,
    show_cursor=True,
    cursor_foreground_priority="css",
    cursor_background_priority="renderable",
    cursor_type="cell",
    cell_padding=_DEFAULT_CELL_X_PADDING,
    name=None,
    id=None,
    classes=None,
    disabled=False
):

Bases: ScrollView, Generic[CellType]

A tabular widget that contains data.

Parameters
Parameter Default Description
show_header
bool
True

Whether the table header should be visible or not.

show_row_labels
bool
True

Whether the row labels should be shown or not.

fixed_rows
int
0

The number of rows, counting from the top, that should be fixed and still visible when the user scrolls down.

fixed_columns
int
0

The number of columns, counting from the left, that should be fixed and still visible when the user scrolls right.

zebra_stripes
bool
False

Enables or disables a zebra effect applied to the background color of the rows of the table, where alternate colors are styled differently to improve the readability of the table.

header_height
int
1

The height, in number of cells, of the data table header.

show_cursor
bool
True

Whether the cursor should be visible when navigating the data table or not.

cursor_foreground_priority
Literal['renderable', 'css']
'css'

If the data associated with a cell is an arbitrary renderable with a set foreground color, this determines whether that color is prioritized over the cursor component class or not.

cursor_background_priority
Literal['renderable', 'css']
'renderable'

If the data associated with a cell is an arbitrary renderable with a set background color, this determines whether that color is prioritized over the cursor component class or not.

cursor_type
CursorType
'cell'

The type of cursor to be used when navigating the data table with the keyboard.

cell_padding
int
_DEFAULT_CELL_X_PADDING

The number of cells added on each side of each column. Setting this value to zero will likely make your table very heard to read.

name
str | None
None

The name of the widget.

id
str | None
None

The ID of the widget in the DOM.

classes
str | None
None

The CSS classes for the widget.

disabled
bool
False

Whether the widget is disabled or not.

BINDINGS class-attribute

BINDINGS: list[BindingType] = [
    Binding("enter", "select_cursor", "Select", show=False),
    Binding("up", "cursor_up", "Cursor Up", show=False),
    Binding(
        "down", "cursor_down", "Cursor Down", show=False
    ),
    Binding(
        "right", "cursor_right", "Cursor Right", show=False
    ),
    Binding(
        "left", "cursor_left", "Cursor Left", show=False
    ),
    Binding("pageup", "page_up", "Page Up", show=False),
    Binding(
        "pagedown", "page_down", "Page Down", show=False
    ),
]
Key(s) Description
enter Select cells under the cursor.
up Move the cursor up.
down Move the cursor down.
right Move the cursor right.
left Move the cursor left.

COMPONENT_CLASSES class-attribute

COMPONENT_CLASSES: set[str] = {
    "datatable--cursor",
    "datatable--hover",
    "datatable--fixed",
    "datatable--fixed-cursor",
    "datatable--header",
    "datatable--header-cursor",
    "datatable--header-hover",
    "datatable--odd-row",
    "datatable--even-row",
}
Class Description
datatable--cursor Target the cursor.
datatable--hover Target the cells under the hover cursor.
datatable--fixed Target fixed columns and fixed rows.
datatable--fixed-cursor Target highlighted and fixed columns or header.
datatable--header Target the header of the data table.
datatable--header-cursor Target cells highlighted by the cursor.
datatable--header-hover Target hovered header or row label cells.
datatable--even-row Target even rows (row indices start at 0).
datatable--odd-row Target odd rows (row indices start at 0).

cell_padding instance-attribute class-attribute

cell_padding = cell_padding

Horizontal padding between cells, applied on each side of each cell.

columns instance-attribute

columns: dict[ColumnKey, Column] = {}

Metadata about the columns of the table, indexed by their key.

cursor_background_priority instance-attribute

cursor_background_priority = cursor_background_priority

Should we prioritize the cursor component class CSS background or the renderable background in the event where a cell contains a renderable with a background color.

cursor_column property

cursor_column: int

The index of the column that the DataTable cursor is currently on.

cursor_coordinate instance-attribute class-attribute

cursor_coordinate: Reactive[Coordinate] = Reactive(
    Coordinate(0, 0), repaint=False, always_update=True
)

Current cursor Coordinate.

This can be set programmatically or changed via the method move_cursor.

cursor_foreground_priority instance-attribute

cursor_foreground_priority = cursor_foreground_priority

Should we prioritize the cursor component class CSS foreground or the renderable foreground in the event where a cell contains a renderable with a foreground color.

cursor_row property

cursor_row: int

The index of the row that the DataTable cursor is currently on.

cursor_type instance-attribute class-attribute

cursor_type: Reactive[CursorType] = cursor_type

The type of cursor of the DataTable.

fixed_columns instance-attribute class-attribute

fixed_columns = fixed_columns

The number of columns to fix (prevented from scrolling).

fixed_rows instance-attribute class-attribute

fixed_rows = fixed_rows

The number of rows to fix (prevented from scrolling).

header_height instance-attribute class-attribute

header_height = header_height

The height of the header row (the row of column labels).

hover_column property

hover_column: int

The index of the column that the mouse cursor is currently hovering above.

hover_coordinate instance-attribute class-attribute

hover_coordinate: Reactive[Coordinate] = Reactive(
    Coordinate(0, 0), repaint=False, always_update=True
)

The coordinate of the DataTable that is being hovered.

hover_row property

hover_row: int

The index of the row that the mouse cursor is currently hovering above.

ordered_columns property

ordered_columns: list[Column]

The list of Columns in the DataTable, ordered as they appear on screen.

ordered_rows property

ordered_rows: list[Row]

The list of Rows in the DataTable, ordered as they appear on screen.

row_count property

row_count: int

The number of rows currently present in the DataTable.

rows instance-attribute

rows: dict[RowKey, Row] = {}

Metadata about the rows of the table, indexed by their key.

show_cursor instance-attribute class-attribute

show_cursor = show_cursor

Show/hide both the keyboard and hover cursor.

show_header instance-attribute class-attribute

show_header = show_header

Show/hide the header row (the row of column labels).

show_row_labels instance-attribute class-attribute

show_row_labels = show_row_labels

Show/hide the column containing the labels of rows.

zebra_stripes instance-attribute class-attribute

zebra_stripes = zebra_stripes

Apply zebra effect on row backgrounds (light, dark, light, dark, ...).

CellHighlighted class

def __init__(self, data_table, value, coordinate, cell_key):

Bases: Message

Posted when the cursor moves to highlight a new cell.

This is only relevant when the cursor_type is "cell". It's also posted when the cell cursor is re-enabled (by setting show_cursor=True), and when the cursor type is changed to "cell". Can be handled using on_data_table_cell_highlighted in a subclass of DataTable or in a parent widget in the DOM.

cell_key instance-attribute

cell_key: CellKey = cell_key

The key for the highlighted cell.

control property

control: DataTable

Alias for the data table.

coordinate instance-attribute

coordinate: Coordinate = coordinate

The coordinate of the highlighted cell.

data_table instance-attribute

data_table = data_table

The data table.

value instance-attribute

value: CellType = value

The value in the highlighted cell.

CellSelected class

def __init__(self, data_table, value, coordinate, cell_key):

Bases: Message

Posted by the DataTable widget when a cell is selected.

This is only relevant when the cursor_type is "cell". Can be handled using on_data_table_cell_selected in a subclass of DataTable or in a parent widget in the DOM.

cell_key instance-attribute

cell_key: CellKey = cell_key

The key for the selected cell.

control property

control: DataTable

Alias for the data table.

coordinate instance-attribute

coordinate: Coordinate = coordinate

The coordinate of the cell that was selected.

data_table instance-attribute

data_table = data_table

The data table.

value instance-attribute

value: CellType = value

The value in the cell that was selected.

ColumnHighlighted class

def __init__(self, data_table, cursor_column, column_key):

Bases: Message

Posted when a column is highlighted.

This message is only posted when the cursor_type is set to "column". Can be handled using on_data_table_column_highlighted in a subclass of DataTable or in a parent widget in the DOM.

column_key instance-attribute

column_key = column_key

The key of the column that was highlighted.

control property

control: DataTable

Alias for the data table.

cursor_column instance-attribute

cursor_column: int = cursor_column

The x-coordinate of the column that was highlighted.

data_table instance-attribute

data_table = data_table

The data table.

ColumnSelected class

def __init__(self, data_table, cursor_column, column_key):

Bases: Message

Posted when a column is selected.

This message is only posted when the cursor_type is set to "column". Can be handled using on_data_table_column_selected in a subclass of DataTable or in a parent widget in the DOM.

column_key instance-attribute

column_key = column_key

The key of the column that was selected.

control property

control: DataTable

Alias for the data table.

cursor_column instance-attribute

cursor_column: int = cursor_column

The x-coordinate of the column that was selected.

data_table instance-attribute

data_table = data_table

The data table.

HeaderSelected class

def __init__(self, data_table, column_key, column_index, label):

Bases: Message

Posted when a column header/label is clicked.

column_index instance-attribute

column_index = column_index

The index for the column.

column_key instance-attribute

column_key = column_key

The key for the column.

control property

control: DataTable

Alias for the data table.

data_table instance-attribute

data_table = data_table

The data table.

label instance-attribute

label = label

The text of the label.

RowHighlighted class

def __init__(self, data_table, cursor_row, row_key):

Bases: Message

Posted when a row is highlighted.

This message is only posted when the cursor_type is set to "row". Can be handled using on_data_table_row_highlighted in a subclass of DataTable or in a parent widget in the DOM.

control property

control: DataTable

Alias for the data table.

cursor_row instance-attribute

cursor_row: int = cursor_row

The y-coordinate of the cursor that highlighted the row.

data_table instance-attribute

data_table = data_table

The data table.

row_key instance-attribute

row_key: RowKey = row_key

The key of the row that was highlighted.

RowLabelSelected class

def __init__(self, data_table, row_key, row_index, label):

Bases: Message

Posted when a row label is clicked.

control property

control: DataTable

Alias for the data table.

data_table instance-attribute

data_table = data_table

The data table.

label instance-attribute

label = label

The text of the label.

row_index instance-attribute

row_index = row_index

The index for the column.

row_key instance-attribute

row_key = row_key

The key for the column.

RowSelected class

def __init__(self, data_table, cursor_row, row_key):

Bases: Message

Posted when a row is selected.

This message is only posted when the cursor_type is set to "row". Can be handled using on_data_table_row_selected in a subclass of DataTable or in a parent widget in the DOM.

control property

control: DataTable

Alias for the data table.

cursor_row instance-attribute

cursor_row: int = cursor_row

The y-coordinate of the cursor that made the selection.

data_table instance-attribute

data_table = data_table

The data table.

row_key instance-attribute

row_key: RowKey = row_key

The key of the row that was selected.

action_page_down method

def action_page_down(self):

Move the cursor one page down.

action_page_up method

def action_page_up(self):

Move the cursor one page up.

action_scroll_end method

def action_scroll_end(self):

Scroll to the bottom of the data table.

action_scroll_home method

def action_scroll_home(self):

Scroll to the top of the data table.

add_column method

def add_column(
    self, label, *, width=None, key=None, default=None
):

Add a column to the table.

Parameters
Parameter Default Description
label
TextType
required

A str or Text object containing the label (shown top of column).

width
int | None
None

Width of the column in cells or None to fit content.

key
str | None
None

A key which uniquely identifies this column. If None, it will be generated for you.

default
CellType | None
None

The value to insert into pre-existing rows.

Returns
Type Description
ColumnKey

Uniquely identifies this column. Can be used to retrieve this column regardless of its current location in the DataTable (it could have moved after being added due to sorting/insertion/deletion of other columns).

add_columns method

def add_columns(self, *labels):

Add a number of columns.

Parameters
Parameter Default Description
*labels
TextType
()

Column headers.

Returns
Type Description
list[ColumnKey]

A list of the keys for the columns that were added. See the add_column method docstring for more information on how these keys are used.

add_row method

def add_row(self, *cells, height=1, key=None, label=None):

Add a row at the bottom of the DataTable.

Parameters
Parameter Default Description
*cells
CellType
()

Positional arguments should contain cell data.

height
int | None
1

The height of a row (in lines). Use None to auto-detect the optimal height.

key
str | None
None

A key which uniquely identifies this row. If None, it will be generated for you and returned.

label
TextType | None
None

The label for the row. Will be displayed to the left if supplied.

Returns
Type Description
RowKey

Unique identifier for this row. Can be used to retrieve this row regardless of its current location in the DataTable (it could have moved after being added due to sorting or insertion/deletion of other rows).

add_rows method

def add_rows(self, rows):

Add a number of rows at the bottom of the DataTable.

Parameters
Parameter Default Description
rows
Iterable[Iterable[CellType]]
required

Iterable of rows. A row is an iterable of cells.

Returns
Type Description
list[RowKey]

A list of the keys for the rows that were added. See the add_row method docstring for more information on how these keys are used.

clear method

def clear(self, columns=False):

Clear the table.

Parameters
Parameter Default Description
columns
bool
False

Also clear the columns.

Returns
Type Description
Self

The DataTable instance.

coordinate_to_cell_key method

def coordinate_to_cell_key(self, coordinate):

Return the key for the cell currently occupying this coordinate.

Parameters
Parameter Default Description
coordinate
Coordinate
required

The coordinate to exam the current cell key of.

Returns
Type Description
CellKey

The key of the cell currently occupying this coordinate.

Raises
Type Description
CellDoesNotExist

If the coordinate is not valid.

get_cell method

def get_cell(self, row_key, column_key):

Given a row key and column key, return the value of the corresponding cell.

Parameters
Parameter Default Description
row_key
RowKey | str
required

The row key of the cell.

column_key
ColumnKey | str
required

The column key of the cell.

Returns
Type Description
CellType

The value of the cell identified by the row and column keys.

get_cell_at method

def get_cell_at(self, coordinate):

Get the value from the cell occupying the given coordinate.

Parameters
Parameter Default Description
coordinate
Coordinate
required

The coordinate to retrieve the value from.

Returns
Type Description
CellType

The value of the cell at the coordinate.

Raises
Type Description
CellDoesNotExist

If there is no cell with the given coordinate.

get_cell_coordinate method

def get_cell_coordinate(self, row_key, column_key):

Given a row key and column key, return the corresponding cell coordinate.

Parameters
Parameter Default Description
row_key
RowKey | str
required

The row key of the cell.

column_key
ColumnKey | str
required

The column key of the cell.

Returns
Type Description
Coordinate

The current coordinate of the cell identified by the row and column keys.

Raises
Type Description
CellDoesNotExist

If the specified cell does not exist.

get_column method

def get_column(self, column_key):

Get the values from the column identified by the given column key.

Parameters
Parameter Default Description
column_key
ColumnKey | str
required

The key of the column.

Returns
Type Description
Iterable[CellType]

A generator which yields the cells in the column.

Raises
Type Description
ColumnDoesNotExist

If there is no column corresponding to the key.

get_column_at method

def get_column_at(self, column_index):

Get the values from the column at a given index.

Parameters
Parameter Default Description
column_index
int
required

The index of the column.

Returns
Type Description
Iterable[CellType]

A generator which yields the cells in the column.

Raises
Type Description
ColumnDoesNotExist

If there is no column with the given index.

get_column_index method

def get_column_index(self, column_key):

Return the current index for the column identified by column_key.

Parameters
Parameter Default Description
column_key
ColumnKey | str
required

The column key to find the current index of.

Returns
Type Description
int

The current index of the specified column key.

Raises
Type Description
ColumnDoesNotExist

If the column key does not exist.

get_row method

def get_row(self, row_key):

Get the values from the row identified by the given row key.

Parameters
Parameter Default Description
row_key
RowKey | str
required

The key of the row.

Returns
Type Description
list[CellType]

A list of the values contained within the row.

Raises
Type Description
RowDoesNotExist

When there is no row corresponding to the key.

get_row_at method

def get_row_at(self, row_index):

Get the values from the cells in a row at a given index. This will return the values from a row based on the rows current position in the table.

Parameters
Parameter Default Description
row_index
int
required

The index of the row.

Returns
Type Description
list[CellType]

A list of the values contained in the row.

Raises
Type Description
RowDoesNotExist

If there is no row with the given index.

get_row_height method

def get_row_height(self, row_key):

Given a row key, return the height of that row in terminal cells.

Parameters
Parameter Default Description
row_key
RowKey
required

The key of the row.

Returns
Type Description
int

The height of the row, measured in terminal character cells.

get_row_index method

def get_row_index(self, row_key):

Return the current index for the row identified by row_key.

Parameters
Parameter Default Description
row_key
RowKey | str
required

The row key to find the current index of.

Returns
Type Description
int

The current index of the specified row key.

Raises
Type Description
RowDoesNotExist

If the row key does not exist.

is_valid_column_index method

def is_valid_column_index(self, column_index):

Return a boolean indicating whether the column_index is within table bounds.

Parameters
Parameter Default Description
column_index
int
required

The column index to check.

Returns
Type Description
bool

True if the column index is within the bounds of the table.

is_valid_coordinate method

def is_valid_coordinate(self, coordinate):

Return a boolean indicating whether the given coordinate is valid.

Parameters
Parameter Default Description
coordinate
Coordinate
required

The coordinate to validate.

Returns
Type Description
bool

True if the coordinate is within the bounds of the table.

is_valid_row_index method

def is_valid_row_index(self, row_index):

Return a boolean indicating whether the row_index is within table bounds.

Parameters
Parameter Default Description
row_index
int
required

The row index to check.

Returns
Type Description
bool

True if the row index is within the bounds of the table.

move_cursor method

def move_cursor(self, *, row=None, column=None, animate=False):

Move the cursor to the given position.

Example
datatable = app.query_one(DataTable)
datatable.move_cursor(row=4, column=6)
# datatable.cursor_coordinate == Coordinate(4, 6)
datatable.move_cursor(row=3)
# datatable.cursor_coordinate == Coordinate(3, 6)
Parameters
Parameter Default Description
row
int | None
None

The new row to move the cursor to.

column
int | None
None

The new column to move the cursor to.

animate
bool
False

Whether to animate the change of coordinates.

refresh_column method

def refresh_column(self, column_index):

Refresh the column at the given index.

Parameters
Parameter Default Description
column_index
int
required

The index of the column to refresh.

Returns
Type Description
Self

The DataTable instance.

refresh_coordinate method

def refresh_coordinate(self, coordinate):

Refresh the cell at a coordinate.

Parameters
Parameter Default Description
coordinate
Coordinate
required

The coordinate to refresh.

Returns
Type Description
Self

The DataTable instance.

refresh_row method

def refresh_row(self, row_index):

Refresh the row at the given index.

Parameters
Parameter Default Description
row_index
int
required

The index of the row to refresh.

Returns
Type Description
Self

The DataTable instance.

remove_column method

def remove_column(self, column_key):

Remove a column (identified by a key) from the DataTable.

Parameters
Parameter Default Description
column_key
ColumnKey | str
required

The key identifying the column to remove.

Raises
Type Description
ColumnDoesNotExist

If the column key does not exist.

remove_row method

def remove_row(self, row_key):

Remove a row (identified by a key) from the DataTable.

Parameters
Parameter Default Description
row_key
RowKey | str
required

The key identifying the row to remove.

Raises
Type Description
RowDoesNotExist

If the row key does not exist.

sort method

def sort(self, *columns, key=None, reverse=False):

Sort the rows in the DataTable by one or more column keys or a key function (or other callable). If both columns and a key function are specified, only data from those columns will sent to the key function.

Parameters
Parameter Default Description
columns
ColumnKey | str
()

One or more columns to sort by the values in.

key
Callable[[Any], Any] | None
None

A function (or other callable) that returns a key to use for sorting purposes.

reverse
bool
False

If True, the sort order will be reversed.

Returns
Type Description
Self

The DataTable instance.

update_cell method

def update_cell(
    self, row_key, column_key, value, *, update_width=False
):

Update the cell identified by the specified row key and column key.

Parameters
Parameter Default Description
row_key
RowKey | str
required

The key identifying the row.

column_key
ColumnKey | str
required

The key identifying the column.

value
CellType
required

The new value to put inside the cell.

update_width
bool
False

Whether to resize the column width to accommodate for the new cell content.

Raises
Type Description
CellDoesNotExist

When the supplied row_key and column_key cannot be found in the table.

update_cell_at method

def update_cell_at(
    self, coordinate, value, *, update_width=False
):

Update the content inside the cell currently occupying the given coordinate.

Parameters
Parameter Default Description
coordinate
Coordinate
required

The coordinate to update the cell at.

value
CellType
required

The new value to place inside the cell.

update_width
bool
False

Whether to resize the column width to accommodate for the new cell content.

textual.widgets.data_table

CellType module-attribute

CellType = TypeVar('CellType')

Type used for cells in the DataTable.

CursorType module-attribute

CursorType = Literal['cell', 'row', 'column', 'none']

The valid types of cursors for DataTable.cursor_type.

CellDoesNotExist class

Bases: Exception

The cell key/index was invalid.

Raised when the coordinates or cell key provided does not exist in the DataTable (e.g. out of bounds index, invalid key)

CellKey class

Bases: NamedTuple

A unique identifier for a cell in the DataTable.

A cell key is a (row_key, column_key) tuple.

Even if the cell changes visual location (i.e. moves to a different coordinate in the table), this key can still be used to retrieve it, regardless of where it currently is.

column_key instance-attribute

column_key: ColumnKey

The key of this cell's column.

row_key instance-attribute

row_key: RowKey

The key of this cell's row.

Column class

Metadata for a column in the DataTable.

get_render_width method

def get_render_width(self, data_table):

Width, in cells, required to render the column with padding included.

Parameters
Parameter Default Description
data_table
DataTable[Any]
required

The data table where the column will be rendered.

Returns
Type Description
int

The width, in cells, required to render the column with padding included.

ColumnDoesNotExist class

Bases: Exception

Raised when the column index or column key provided does not exist in the DataTable (e.g. out of bounds index, invalid key)

ColumnKey class

Bases: StringKey

Uniquely identifies a column in the DataTable.

Even if the visual location of the column changes due to sorting or other modifications, a key will always refer to the same column.

DuplicateKey class

Bases: Exception

The key supplied already exists.

Raised when the RowKey or ColumnKey provided already refers to an existing row or column in the DataTable. Keys must be unique.

Row class

Metadata for a row in the DataTable.

RowDoesNotExist class

Bases: Exception

Raised when the row index or row key provided does not exist in the DataTable (e.g. out of bounds index, invalid key)

RowKey class

Bases: StringKey

Uniquely identifies a row in the DataTable.

Even if the visual location of the row changes due to sorting or other modifications, a key will always refer to the same row.