Skip to content

table

Displays a data set as tabular data.

Properties

Name Type Default Description
data(★) Any
dynamic
Required

The data to be represented in this table. This property can be indexed to define other data for comparison.

page_size int 100

For a paginated table, the number of visible rows.

allow_all_rows bool False

For a paginated table, adds an option to show all the rows.

show_all bool False

For a paginated table, show all the rows.

auto_loading bool False

If True, the data will be loaded on demand.

width[column_name] str

The width of the indicated column, in CSS units (% values are not supported).

selected Union[list[int],str]
dynamic

The list of the indices of the rows to be displayed as selected.

page_size_options Union[list[int],str] [50, 100, 500]

The list of available page sizes that users can choose from.

columns Union[str,list[str],dict[str,dict[str,Union[str,int]]]] All columns

The list of the column names to display.

  • str: semicolon (';')-separated list of column names.
  • list[str]: the list of column names.
  • dict: a dictionary with entries matching: {"<column_name>": {"format": "<format>", "index": 1}}.
    if index is specified, it represents the display order of this column. If index is not specified, the list order defines the index.
    If format is specified, it is used for numbers or dates.

If columns is omitted or set to None, all columns of data are represented.

date_format str "MM/dd/yyyy"

The date format used for all date columns when the format is not specifically defined.

number_format str

The number format used for all number columns when the format is not specifically defined.

group_by[column_name] bool False

Indicates, if True, that the given column can be aggregated.
See below for more details.

apply[column_name] str "first"

The name of the aggregation function to use.
This is used only if group_by[column_name] is set to True.
See below for more details.

row_class_name Union[str, Callable]

Allows for styling rows.
This property must be a function or the name of a function that return the name of a CSS class for table rows.
This function is invoked with the following parameters:

  • state (State): the state instance.
  • index (int): the index of the row.
  • row (Any): all the values for this row.

See below for more details.

cell_class_name[column_name] Union[str, Callable]

Allows for styling cells.
This property must be a function or the name of a function that return the name of a CSS class for table cells.
This function is invoked with the following parameters:

  • state (State): the state instance.
  • value (Any): the value of the cell.
  • index (int): the index of the row.
  • row (Any): all the values for this row.
  • column_name (str): the name of the column.

See below for more details.

tooltip Union[str, Callable]

Enables tooltips on cells.
This property must be a function or the name of a function that must return a tooltip text for a cell.
See below for more details.

tooltip[column_name] Union[str, Callable]

Enables tooltips on cells at a column level.
This property must be a function or the name of a the function that must return a tooltip text for a cell.
See below for more details.

format_fn[column_name] Union[str, Callable]

Defines custom formatting for table cells. This property must be a function or the name of a function that returns a formatted string for each cell.
The function is invoked when the cells in the specified column (column_name) are rendered. It should return a string that represents the cell value to provide the best user experience.
This function is invoked with the following parameters:

  • state (State): the state instance.
  • value (Any): the value of the cell.
  • index (int): the index of the row.
  • row (Any): the entire row. The type depends on the type of data.
  • column_name (str): the name of the column.
By default, no custom formatting is applied to the column.
For more details, see the section.

width str "100%"

The width of this table control, in CSS units.

height str "80vh"

The height of this table control, in CSS units.

filter bool False

Indicates, if True, that all columns can be filtered.

filter[column_name] bool False

Indicates, if True, that the indicated column can be filtered.

nan_value str ""

The replacement text for NaN (not-a-number) values.

nan_value[column_name] str ""

The replacement text for NaN (not-a-number) values for the indicated column.

editable bool
dynamic
False

Indicates, if True, that all cells can be edited.

editable[column_name] bool editable

Indicates, if False, that the indicated column cannot be edited, even if editable is True.
By default, all columns are editable or not, depending on the value of the editable property.

on_edit Union[bool, Callable] default implementation

A function or the name of a function triggered when an edited cell is validated.
This function is invoked with the following parameters:

  • state (State): the state instance.
  • var_name (str): the name of the tabular data variable.
  • payload (dict): a dictionary containing details about the callback invocation, with the following keys:
    • index (int): the row index.
    • col (str): the column name.
    • value (Any): the new cell value, cast to the column's data type.
    • user_value (str): the new cell value, as entered by the user.
    • tz (str): the timezone, if the column type is date.
If this property is set to False, the table does not provide the cell editing functionality.
If this property is not set, the table will use the default implementation for editing cells.

on_add Union[bool, Callable]

A function or the name of a function that is triggered when the user requests a row to be added to the table.
This function is invoked with the following parameters:

  • state (State): the state instance.
  • var_name (str): the name of the tabular data variable.
  • payload (dict): the details on this callback's invocation.
    This dictionary has the following key:
    • index (int): the row index.

If this property is not set, the table uses the default implementation for adding a new row
If this property is set to False, you cannot add new rows.

on_delete Union[bool, Callable] default implementation

A function or the name of a function triggered when a row is deleted.
This function is invoked with the following parameters:

  • state (State): the state instance.
  • var_name (str): the name of the tabular data variable.
  • payload (dict): the details on this callback's invocation.
    This dictionary has one key:
    • index (int): the row index.

If this property is not set, the table uses the default implementation for deleting rows.

on_action Union[str, Callable]

A function or the name of a function that is triggered when the user selects a row.
This function is invoked with the following parameters:

  • state (State): the state instance.
  • var_name (str): the name of the tabular data variable.
  • payload (dict): the details on this callback's invocation.
    This dictionary has the following keys:
    • action: the name of the action that triggered this callback.
    • index (int): the row index.
    • col (str): the column name.
    • reason (str): the origin of the action: "click", or "button" if the cell contains a Markdown link syntax.
    • value (str): the link value indicated in the cell when using a Markdown link syntax (that is, reason is set to "button").
.

size str "small"

The size of the rows.
Valid values are "small" and "medium".

rebuild bool
dynamic
False

If set to True, this allows to dynamically refresh the columns.

lov[column_name] Union[list[str],str]

The list of values of the indicated column.

downloadable bool False

If True, a clickable icon is shown so the user can download the data as CSV.

on_compare Union[str, Callable]

A function or the name of a function that compares data. This function should return a structure that identifies the differences between the different data passed as name. The default implementation compares the default data with the data[1] value.

use_checkbox bool False

If True, boolean values are rendered as a simple HTML checkbox.

sortable bool True

If False, the table provides no sorting capability. Individual columns can override this global setting, allowing specific columns to be marked as sortable or non-sortable regardless of value of sortable, by setting the sortable property to True or False accordingly, in the dictionary for that column in the columns property value.

active bool
dynamic
True

Indicates if this component is active.
An inactive component allows no user interaction.

id str

The identifier that is assigned to the rendered HTML component.

properties dict[str, Any]

Bound to a dictionary that contains additional properties for this element.

class_name str
dynamic

The list of CSS class names that are associated with the generated HTML Element.
These class names are added to the default taipy-table class name.

hover_text str
dynamic

The information that is displayed when the user hovers over this element.

(★)data is the default property for this visual element.

Details

Data types

All the data sets represented in the table control must be assigned to its data property.

The supported types for the data property are:

  • A list of values:
    When receiving a data that is just a series of values, the table is made of a single column holding the values at the corresponding index. The column name is then "0".
  • A Pandas DataFrame:
    Taipy tables then use the same column names as the DataFrame's.
  • A dictionary:
    The value is converted into a Pandas DataFrame where each key/value pair is converted to a column named key and the associated value. Note that this method only works when all the dictionary's values are series of identical lengths.
  • A list of lists of values:
    All the lists must be the same length. The table control creates one row for each list in the collection.
  • A NumPy series:
    Taipy internally builds a Pandas DataFrame with the provided data.

Polars data types support

Taipy Enterprise edition provides native support for the polars.LazyFrame, polars.DataFrame or polars.Series data types in the data property, including for edits.

Display modes

The table component supports three display modes:

  • paginated: you can choose the page size and page size options. The allow_all_rows property allows adding an option to show a page with all rows.
  • unpaginated: all rows and no page are shown. That is the setting when the show_all property is set to True.
  • auto_loading: the pages are loaded on demand depending on the visible area. That is the behavior when the auto_loading property is set to True.

Customizing columns

The table control has properties that let you customize which columns are displayed and in what order and apply a number format to the numerical and date values.

You can use the columns property to customize what columns are displayed and how they should be displayed.

To demonstrate the capabilities of columns, we will use the following dataset:

Creating the dataset
data = {
    "date" : [datetime.datetime(year=2000, month=3, day=d) for d in [12, 15, 18, 19, 31]],
    "volume" : [6533, 1578, 1881, 2373, 8646],
    "price" : [152.611, 168.582, 193.798, 101.025, 159.071],
}
This data represents the volume and price of exchanges of some imaginary shares on the stock market for a few dates in March 2000.

Default settings

Let's create a table control without specifying the columns property:

Definition

<|{data}|table|>
<taipy:table>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}")

With that definition, the representation of the control is similar to the following image:

Displaying all the columns

Selecting columns

Let's say you don't want to display the "volume" column. To indicate that, you can specify the list of column names to display in the columns property:

Definition

<|{data}|table|columns=date;price|>
<taipy:table columns="date;price">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", columns="date;price")

Here is how the table appears with that definition:

Displaying specific columns

Formatting numbers globally

The "price" column displays values with too many decimal places, which is unusual for displaying a price.
The number_format property can be set to a format string that applies to all numerical values in the table. The syntax for this string is specified by the Python string format() function.

The following control definition indicates that all numbers should be represented with two decimal places:

Definition

<|{data}|table|number_format=%.02f|>
<taipy:table number_format="%.02f">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", number_format="%.02f")

And the result looks like this:

Formatting the numerical values

Although we only have two decimal places for stock prices, which was the point of setting the number_format property, the format has impacted all numerical values, including the values of the "volume" column, which is not what we want: volumes are integer values, they should have no decimal places at all.

Formatting numbers in specific columns

You can provide a specific format for a particular column by setting the columns property to a dictionary that indicates what format should be used for what columns.

The keys of this dictionary must be a column name, and the value for a given key must be another dictionary with specific key/value pairs.
For our use case, we just want to use a format for the "value" column.

Here is how we can define the dictionary that can be used by the table control to apply the format where it is required:

Format for a column
columns = {
    "date" : {},
    "volume" : {},
    "price" : {"format": "$%.02f"},
}
There is one entry for every column we want to display, and the "price" column has a specific setting that indicates what format to use: a dollar sign followed by the value with two decimal places.

We can set that dictionary to the columns property:

Definition

<|{data}|table|columns={columns}|>
<taipy:table columns="{columns}">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", columns="{columns}")

Here is how the table now looks:

Formatting a specific column

You can see that the format only impacts the values of the "price" column.

Formatting dates

You can use the date_format property to format dates in the table. This property uses the syntax of the date-fns.format() function to convert a datetime object to a string.

With the same dataset, here is a table control definition that uses the date_format property:

Definition

<|{data}|table|date_format=MMM do|>
<taipy:table date_format="MMM do">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", date_format="MMM do")

The value "MMM do" indicates that the conversion of the date to a string will begin with the abbreviation of the month name, followed by the day number in the month with the index suffix.

See how the dates of the "date" column are now represented:

Formatting dates

Note that if you need to apply a date format to a specific column, you need to create a columns dictionary as demonstrated above and set the format property for the columns that contain the date values.

Custom formatting

You can specify a Python function as the value of the format_fn[column_name] property. This function is invoked for every cell of the column when the table is rendered. This function allows complete control over the formatting, enabling any type of string representation.

The formatting function receives the following parameters:

  • state (State): The current state instance, which can be used to access additional variables required for formatting.
  • value (Any): The cell value to be formatted.
  • index (int): The index of the row containing the cell.
  • row (Any): All the values for the row. You can use values from other columns in the same row to influence the formatting.
  • column_name (str): The name of the column being formatted.

These arguments allow you to create flexible and dynamic formatting logic based on the row, value, and state.

For a complete example, refer to this section.

Setting the column titles

By default, a column title reflects the name of the collection that it represents. You can change that by setting the title key of a column-specific dictionary.

Let's specify a column definition that customizes the column titles:

Column titles
columns = {
    "date" : {"title": "Date"},
    "volume" : {"title": "Volume"},
    "price" : {"title": "Price ($)", "format": "%.02f"},
}

Using the control definition that we have used in the example above, here is how the table looks like:

Custom column titles

Ordering columns

When looking at stock exchange data tables, the most common representation is to display the stock price before the exchanged volume. The default behavior of the table control is to show the columns in the order they appear in the object set to the data property.

To reorder the columns, you can define the control with the columns property set to a list of column names in the order you want them displayed:

Definition

<|{data}|table|columns=date;price;volume|>
<taipy:table columns="date;price;volume">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", columns="date;price;volume")

This definition produces the following table representation:

Columns reordered

An alternative way to order columns that could be more suitable for your situation is to provide an index value (starting at 0) to the index property of the dictionary set for the appropriate column in a dictionary set to the columns property.

You can define a dictionary as follows:

Column order
columns = {
    "date" : {"index": 0},
    "volume" : {"index": 2},
    "price" : {"index": 1},
}
The value of the index property for each column indicates the order in which you want to see the columns organized.
The definition of the control would use that dictionary:

Definition

<|{data}|table|columns={columns}|>
<taipy:table columns="{columns}">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", columns="{columns}")

And the resulting display would be the same.

Editing the table content

The table control provides interface elements that help modify the bound data.
These elements let users:

  • edit cell content;
  • add new rows;
  • delete existing rows.

The presence of these UI elements depends on the control configuration:

  • icon (Add):
    • Appears in the top-left corner of the table when editable is set to True and on_add is not set to False.
    • Clicking this icon can add new rows to the table, as described in this section.
  • icon (Delete):
    • Appears to the left of each row when editable is set to True and on_delete is not set to False.
    • Clicking this icon can remove rows, as explained in the this section.
  • icon (Edit):
    • Appears to the right of each cell if editable is set to True.
    • It also appears to the right of each cell in a specific column ("column_name") if editable[column_name] is set to True, even when editable is set to False.
    • It does not appear to the right of cells in a given column ("column_name") if editable is set to False, even when editable[column_name] is set to True.

Here are the different actions provided by the table control and explanations about how you can leverage them to fit the needs of your application.

Adding a row

When a table control is editable (i.e., either the editable property is set to True or the editable[] property is True for any of the table's columns), and the on_add property is not set to False, an icon (Add) appears in the top-left corner of the table:

'Add' icon in the top-left corner of the table

The user can click this icon to add a new row to the table. This action triggers the callback function defined by the on_add property. The callback function should handle the creation and insertion of the new row into the table.

Default implementation

If the on_add property is not explicitly defined, the default behavior will invoke the method Gui.table_on_add(). This method inserts a new row at the top of the dataset. By default, all values in the new row are initialized to 0 (adapted to the data type of the column) or an empty string for columns of type str.

Custom implementation

The implementation of the callback function depends heavily on the data structure used in the data property and the specific side effects required by your application.

The callback function receives the following parameters:

  • state: the client's state;
  • var_name: the name of the variable bound to the control's data property;
  • payload: a dictionary containing additional information, including:
    • "index": the index in the table where the new row should be inserted.

Here is an example of a possible callback function implementation.
In this scenario, the control's data property holds a Pandas DataFrame, and the on_add property is set to the insert_row() function:

def insert_row(state: State, var_name: str, payload: dict):
    df = state.data
    # Retrieve the insertion index from the payload
    index = payload["index"]
    # Define the new row data (Ensure the types match the DataFrame's columns)
    new_row = ["Julius Caesar", -100]
    if index > 0:
        # Create a new DataFrame for the new row
        new_df = pd.DataFrame(new_row, columns=["Name", "BirthYear"])

        # Split the existing DataFrame and insert the new row
        rows_before = df.loc[:index-1]
        rows_after = df.loc[index+1:]
        state.data = pd.concat([rows_before, new_df, rows_after], ignore_index=True)
    else:
        # Insert the new row at the beginning
        state.data.loc[-1] = new_row  # Insert new row
        state.data.index = state.data.index + 1  # Adjust the index
        state.data = state.data.sort_index()  # Sort the index to maintain order

Note that in this implementation, after the row is added, the page refreshes automatically because state.data is updated.

You can simplify the code significantly by calling Gui.table_on_add(), which accepts a series of values to initialize the new row. This avoids the need to manually handle DataFrame splitting or indexing.

An example below demonstrates this functionality in this section.

Deleting a row

When a table control is editable (i.e., either the editable property is set to True or the editable[] property is True for any of the table's columns), and the on_delete property is not set to False, a icon (Delete) appears to the left of each row:

'Delete' icon to the left of a row

The user can click this icon to initiate the row deletion process.
Once clicked, the icon changes to the following:

Validating row deletion

The user must then confirm the deletion by clicking the icon (Apply) or cancel the operation by clicking the icon (Cancel).

If the deletion is confirmed, Taipy GUI triggers the callback function specified in the on_delete property.

Default implementation

If the on_delete property is not explicitly defined, the default behavior is to call the Gui.table_on_delete() method. This method automatically removes the selected row from the dataset.

Custom implementation

For a custom implementation of the on_delete callback function, the logic will depend on the structure of the data stored in the data property and any specific side effects your application needs to manage.

The callback function receives the following parameters:

  • state: the client's state;
  • var_name: the name of the variable bound to the control's data property;
  • payload: a dictionary containing additional information, including:
    • "index": the index of the row to be removed.

Below is an example implementation of a callback function, assuming the control’s data property is bound to a Pandas DataFrame, and the on_delete property is set to the function remove_row():

def remove_row(state: State, var_name: str, payload):
    state.data = state.data.drop(payload["index"])
In this implementation, the Pandas API is used to remove the row from the DataFrame, and the updated DataFrame is saved back to the state object, triggering an immediate update in the display.

If you prefer not to handle the Pandas API or are unsure of the data structure in the data property, you can delegate the task to the Gui.table_on_delete() method, which manages the deletion process for you.

In the example below, there is a demonstration of this functionality in this section.

Editing cells

If the on_edit property is not set to False and the table columns are editable (i.e., either editable[column_name] is set to True regardless of the editable property value, or editable[column_name] is not set to False and editable is set to True), the icon (Edit) appears at the right end of each editable cell. Users can click this icon to modify the values of individual cells.
Here is how this icon looks:

'Edit' icon to the right of a cell

When the user clicks this icon, the cell becomes editable:

Editing the cell value

The user can enter a new value for the cell and either click the icon (Apply) or press the Enter key to confirm the change. This action triggers the callback function defined by the on_edit property (or the built-in Gui.table_on_edit() method if on_edit is not set) to process the updated value.
Alternatively, the user can cancel the operation by clicking the icon (Cancel) or pressing the ESC key.

Enumerated values

A table control can be configured so that cell values are constrained to predefined options. In such cases, the editing interaction may differ slightly, as explained in the section on enumerated values.

Default implementation

If the on_edit property is not explicitly defined, the default behavior is to call the Gui.table_on_edit() method. This method updates the value of the specified cell with the user-provided input.

Custom implementation

The implementation of the on_edit callback function will vary depending on the data structure used in the data property, the data types involved, and the application’s requirements for data validation or transformation.

The callback function receives the following parameters:

  • state: the client's state;
  • var_name: the name of the variable bound to the control's data property;
  • payload: a dictionary containing additional information, including:
    • "index": the row index of the cell to be modified.
    • "col": the column name of the cell to be modified.
    • "value": the new value entered by the user, converted to the appropriate data type.
    • "user_value": the raw string input entered by the user.
    • "tz": the timezone to apply, if working with date or time values.

Below is an example of a callback function where the data property is a Pandas DataFrame, and the on_edit property is set to the edit_value() function:

def edit_value(state: State, var_name: str, payload):
    state.data.at[payload["index"], payload["col"]] = payload["value"]
    state.refresh(var_name)
In this implementation, the Pandas API is used to update the specific cell in the DataFrame based on the information provided in the payload parameter.

Note that the State.refresh() method must be explicitly called to update the display, as the underlying state.data object remains unchanged, and Taipy GUI will not automatically detect the modification.

There is an example on such a custom implementation of the on_edit callback function in this example, in this section.

Enumerated values

You can designate certain table columns as enumerated columns, requiring their values to be selected from a predefined list. While Taipy GUI does not control the actual data stored in the table, it can restrict user input during editing to ensure only valid values are entered.
When a user edits a cell, only values from the predefined list will be selectable.

To enable this, use the lov indexed property property for the specific column. You must create a list of allowed values for a column (e.g., "MyColumn") and assign this list to the lov[MyColumn] property.

Value restrictions

In most cases the user can select a value name from the predefined list. Only values from this list are accepted in the cell.

However, in some cases, the predefined list should serve more as a recommendation rather than a strict set of allowed values.

A common example would be a column intended to store color names. The application might provide a list of commonly used colors (e.g., red, orange, yellow, green, blue, and violet), but users may also want to enter other valid color names such as "rose" or "maroon."

To allow flexibility while still providing suggestions, you can set the first element of the lov list for that column to None. For example:

predefined_colors = [None, "Red", "Orange", "Yellow", "Green", "Blue", "Violet"]
By assigning predefined_colors to the lov property for the column that holds color names, you can guide users without enforcing strict validation, allowing them to enter other values if needed.

Below is a complete example of this feature.

The rebuild property

When the application modifies the value of a dynamic property, the impact of the change is immediately reflected on the application page. However, changing the value of properties that are not dynamic requires that the user refreshes the page manually (or that the application explicitly calls navigate() with the force parameter set to True). This is due to the fact that the entire front-end component must be entirely re-generated to reflect its new settings based on the property values.
The table control provides the rebuild property that, when set to True, triggers the rebuilding of the table front-end component and refreshes the page.
Note that this mechanism may hurt the user experience because rebuilding the entire component can be a somewhat complex operation.

Here is a situation where you may need to use rebuild: your application displays a table, and you want to provide a way to interactively change the order of its columns.
Here is what the application code would look like:

table.py
from taipy.gui import Gui

# x: [1..5]
x_range = range(1, 6)
data = {
    "X": x_range,
    "Y": [x*x for x in x_range]
}

column_orders = [("X;Y", "Squared"), ("Y;X", "Square root")]
columns = column_orders[0]

page = """
<|{data}|table|columns={columns[0]}|show_all|>

<|{columns}|toggle|lov={column_orders}|>
"""

Gui(page=page).run()

The table displays two columns of data, one column holding the square value of the other. When you run this application, here is what the page looks like:

Initial display of the application

A toggle button lets the user choose whether to represent, in the second column, the square of the value in the first column or the other way around.
To implement this, the code is setting the value "X;Y" or "Y;X" to the property columns.
Here is what the application looks like just after the user has changed the value of the toggle button:

After the columns should be reordered

We can see that although the value for the toggle button was properly updated, the table has not rearranged its columns. That is because the columns property is not dynamic.

Setting the rebuild property to True allows for updating the table on the fly: let's change the table's Markdown definition to:

<|{data}|table|columns={columns[0]}|show_all|rebuild|>

If you run the application again, and select the alternative column order in the toggle button, here is what the page looks like:

After the columns are reordered

Now the table properly reflects the value of the columns property and no manual refresh was needed.

Make sure, when you are using the rebuild property, that no performance impact is so bad that it would ruin the user experience.

Usage

Show tabular data

Suppose you want to display the data set defined as follows:

# x_range = [-10, -6, -2, 2, 6, 10]
x_range = range(-10, 11, 4)

data = {
    "x": x_range,
    # y1 = x*x
    "y1": [x*x for x in x_range],
    # y2 = 100-x*x
    "y2": [100-x*x for x in x_range]
}

You can use the following control declaration to display all these numbers in a table:

Definition

<|{data}|table|>
<taipy:table>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}")

The resulting image looks like this:

A simple table

Large data

The example above had only six lines of data. If we change the x_range definition to create far more data lines, we come up with a table with much more data to display:

# large_x_range = [-10, -9.98, ..., 9.98, 10.0] - 1000 x values
large_x_range = [round(20*i/1000-10, 2) for i in range(0, 1001)]

data = {
    "x": large_x_range,
    # y1 = x*x
    "y1": [round(x*x, 5) for x in large_x_range],
    # y2 = 100-x*x
    "y2": [round(100-x*x, 5) for x in large_x_range]
}

We can use the same table control definition:

Definition

<|{data}|table|>
<taipy:table>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}")

To get a rendering looking like this:

Paginated table (partial)

Only the first 100 rows (as indicated in the 'Rows per page' selector) are visible.
The table scroll bar lets you navigate across the 100 first rows.
You can change how many rows are displayed simultaneously using the page_size and page_size_options properties.

If you want to display all the rows at the same time, you can change the control definition to set the show_all to True:

Definition

<|{data}|table|show_all|>
<taipy:table show_all>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", show_all=True)

Now the table displays all the data rows, and the scrollbar lets you navigate among all of them:

Showing all the rows (partial)

Setting the allow_all_rows property to True for a paginated table adds the 'All' option to the page size options, so the user can switch from one mode to the other.

Show specific columns

If you want to display a specific set of columns, you can use the columns property to indicate what columns should be displayed.

Here is how you would define the table control if you want to hide the column "y2" from the examples above:

Definition

<|{data}|table|columns=x;y1|>
<taipy:table columns="x;y1">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", columns="x;y1")

And the y2 column is not displayed any longer:

Specifying the visible columns

Formatting values

The table control has different means to let you specify what format to apply when displaying numerical or date values:

  • The number_format property specifies what format applies to all numerical values in the table data.
    The syntax for this property is defined by the format() method of the Python string type.
  • The date_format property specifies what format applies to all date values in the table data.
    The syntax for this property is defined by the JavaScript date-fns.format() function.

In some situations, you need each column to be represented with a dedicated format definition.
When this happens, you can use the dictionary version of the columns property to indicate the format to apply to each column, as described above.

Consider the following data set:

Dataset definition
stock = {
    "date": [
        datetime.datetime(year=2000, month=12, day=20),
        datetime.datetime(year=2000, month=12, day=21),
        datetime.datetime(year=2000, month=12, day=22),
        ...
    ]
    "price": [119.88, 112.657, 164.5, 105.42, 188.36, 103.9, 143.97, 160.11, 136.3, 174.06],
    "change": [7.814, -5.952, 0.01, 8.781, 7.335, 6.623, -6.635, -6.9, 0.327, -0.089],
    "volume": [773, 2622, 2751, 1108, 7400, 3772, 9398, 4444, 9264, 1108],
}
This dataset represents the price, value change, and volume of exchanged shares on the stock market for an imaginary tick on some consecutive days.

Standard formatting requirements could be:

  • The date values should be represented with only the month name and the day of the month.
    The format to apply is "MMM d".
  • The price values must have two significative decimal places and hold the currency symbol.
    The format to apply is "$%.02f": a dollar sign followed by the value with two decimal places.
  • The change values must have a single decimal place.
    The format to apply is "%.01f": the value with one decimal place.
  • The title of each column must be capitalized.
    We can force the column titles to: "Date", "Price", "% change" and "Volume"

The implementation of these requirements requires the creation of a columns dictionary defined as follows:

Column customization
columns = {
    "date": {"title": "Data", "format": "MMM d"},
    "price": {"title": "Price", "format": "$%.02f"},
    "change": {"title": "% change", "format": "%.01f"},
    "volume": {"title": "Volume"}
}

We can use this dictionary in the table definition:

Definition

<|{data}|table|columns={columns}|>
<taipy:table columns="{columns}">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", columns="{columns}")

The resulting display reflects what is requested:

Formatted columns

Custom formats

You can create a Python function to format the cell values according to the specific needs of your application. By assigning this function to the format_fn[column_name] property, the function will be used to format the cell values of that particular column when the table is displayed. This allows you to have entire control on how data are presented to the user.

Below is a simple example demonstrating this feature.
Suppose you are building an application that manages a table with user names and passwords. For security reasons, when displaying the table, the passwords will be hidden (e.g., replaced with asterisks).
However, you will allow the passwords to be visible during editing, where they will appear in plain text.

Here is the dataset that we will use, stored in the variable data:

data = {
    "User Name": ["johndoe", "janedoe", "admin", "sampleuser", "guestaccount" ],
    "Password": ["Password123!", "Test@2023", "Admin#789", "SamplePass#1", "Guest!2024"]
    }

Here is the definition of the table control:

Definition

<|{data}|table|format_fn[Password]=hide_text|editable[Password]|show_all|no on_add|no on_delete|>
<taipy:table format_fn[Password]="hide_text" editable[Password] show_all on_add="false" on_delete="false">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", format_fn__Password=hide_text, editable__Password=True, show_all=True, on_add=False, on_delete=False)

Here are the important property settings to mention:

  • The format_fn property, for the "Password" column, is set to the hide_text() function (described below) to perform the masking of the text.
  • The editable[Password] property is set to True, making only the "Password" column editable.
  • The on_add and on_delete properties are both set to False, preventing the user from adding or deleting rows in the table.

Here is the formatting function:

def hide_text():
    return "*" * 8
This formatting function for the password column does not rely on any of the provided arguments (such as the cell value or column name). Instead, it always returns a string of eight asterisks ("**"), effectively masking the password in the displayed table. The function ensures that passwords are consistently hidden regardless of their actual length.

Here is what the table looks like:

Custom format

If the user clicks the icon (Edit) next to the masked password, the text is revealed, showing the actual password in plain text. This allows the user to view or modify the password, but only when explicitly editing the field.
Here is what the user can see in this situation:

Editing the cell

Pick from a list

Here is an example demonstrating how to use enumerated values to guide user input in a table.

As mentioned earlier, you need to set the lov indexed property for columns that should only accept values from a predefined list.

To illustrate this feature with an example, consider the following table:

A table with an enumerated column

This table contains a list of city names along with their respective country, continent, and population (in millions).

While the user can edit any cell, it would simplify editing and validation if the "Continent" column were constrained to only accept valid continent names.
This is precisely the purpose of enumerated columns: To designate a column as enumerated, you must set its lov indexed property to a list of allowed values..
In this example, we need to create a list of valid continent names and assign it to the lov[Continent] property.

The Python code for defining the list of valid continents is:

all_continents = ["Africa", "America", "Antarctica", "Asia", "Europe", "Oceania"]

The table definition would then reference this all_continents variable:

Definition

<|{data}|table|lov[Continent]={all_continents}|editable|no on_add|no on_delete|show_all|>
<taipy:table lov[Continent]="{all_continents}" editable on_add="false" on_delete="false" show_all>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", lov__Continent="{all_continents}", editable=True, on_add=False, on_delete=False, show_all=True)

Note that all the cells in table can be edited because editable is set to True.
However we prevent users from creating or deleting new rows by setting on_add and on_delete to False.

When the user attempts to edit a cell in the "Continent" column, it will look like this:

Editing an enumerated value

A icon (Drop down) appears in the cell, which when clicked, displays a drop-down list of predefined values that the user can select from:

List of predefined values to pick from

Action on edit

While the table control includes a built-in mechanism to automatically update the bound data object (in the data property) when users edit the table (i.e., by adding or removing rows, or modifying cell values), you can also define custom callback functions to handle or restrict these changes.
This enables you to execute specific actions based on user modifications.

You can achieve this by setting the on_add, on_delete, or on_edit on_edit properties.
Below is an example that demonstrates this capability:

Suppose we are creating an application that displays how a user's assets are distributed across various categories. The table would list each category alongside the allocated amount. The purpose of the application is to show the percentage of each allocation relative to the user's total assets and allow the user to modify the amount allocated to each category.
Any changes in the allocation will trigger a recalculation of the distribution across all categories.

Below is the Python code that initializes the dataset with arbitrary initial values:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
categories = ["Real Estate", "Stocks", "Cash", "Retirement", "Other"]
amounts = [190000, 60000, 100000, 110000, 40000]

def compute_ratio(amounts: list[int]) -> list[int]:
    total = sum(amounts)
    return [int((amount / total) * 100) for amount in amounts]

ratio = compute_ratio(amounts)

data = pd.DataFrame({"Category": categories, "Amount": amounts, "%": ratio})
The data variable consists of three distinct series:

  • categories holds the different asset categories.
  • amounts holds the allocated amount for each category.
  • ratio holds the percentage of each amount relative to the total assets.
    The compute_ratio() function (defined on line 4) calculates these percentages and returns a list of integers representing the proportion of each amount.
    The initial ratios are computed on line 8 and stored in the ratio variable.
  • The complete dataset is assembled in the data variable on line 10.

Here is the table control definition:

Definition

<|{data}|table|editable[Amount]|on_edit=update|no on_add|no on_delete|show_all|>
<taipy:table editable[Amount] on_edit="update" on_add="false" on_delete="false" show_all>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", editable__Amount=True, on_edit=update, on_add=False, on_delete=False, show_all=True)

Setting editable[Amount] to True makes the column editable.
Due to the configuration of on_edit, when the user modifies a value in the 'Amount' column, the update() function is invoked.
To disable the ability to add or remove rows, the on_add and on_delete properties are set to False.

This is how the page looks like initially:

Initial page

Next, let's add functionality to update the "%" column when a user modifies the "Amount" column.
Here is the definition of the update() function:

1
2
3
4
5
def update(state: State, var_name: str, payload: dict):
    with state:
        state.get_gui().table_on_edit(state, var_name, payload)
        state.data["%"] = compute_ratio(state.data["Amount"])
        state.refresh(var_name)
In line 2, we create a context block (with state) to batch changes to the state. This is important because modifying both the "Amount" and "%" columns could otherwise trigger unnecessary page refreshes.
Line 3 invokes the default behavior for handling user edits, updating the "Amount" column based on the user’s input.
In line 4, the "%" column is updated by recalculating the ratio of all categories.
Finally, line 5 refreshes the var_name variable (set to "data") to update the display.

The requirements have now been implemented. Now, let's see how this works.
Assume the user enters a new value in a cell in the "Amount" column by pressing the icon (Edit) next to the cell:

Changing the value of a cell

Now, suppose the user changes the value from 190,000 to 80,000 and confirms the change by pressing the icon (Apply). The on_edit setting triggers the invocation of the update() function, which in turn calls compute_ratio() to recalculate all ratios. Once the new ratios are stored in the "%" column, the page is refreshed to display the updated results:

Ratios were recomputed

As you can see, all the ratio values in the "%" column have been updated to reflect the impact of the new value.

Guarding edits

The on_edit callback is triggered when the content of a cell is edited by the user. Your application may need to respond to this event, for instance, to perform computations or update related data.
While you could use the on_change callback to detect changes, doing so would not provide contextual information about the specific cell that was modified, which may be useful in certain scenarios.

In this example, we use the on_edit callback to address this limitation.
This property is set to a function that intercepts the cell edit operations.

We aim to build a small application that helps manage a team of employees. The requirements for the application are as follows:

  • Team Size: The team should consist of three to six employees.
  • Salary Adjustment: The salary for each employee should be rounded to the nearest multiple of $500.

The application interface is a table that displays the list of employees, with a column showing the proposed salary for each.

  • The user can remove a candidate by pressing the icon (Delete) in the corresponding row, but the list must contain at least three employees.
  • The user can add a new candidate to the list by pressing the icon (Add), but no more than six employees can be added.
  • The user can edit any employee's salary, and the final amount is rounded to the nearest multiple of $500.

We begin by initializing the variable that stores the list of candidates. It is initially empty:

candidates = {"Name": [], "Salary": []}

Next, we define a function that selects a new candidate. The exact implementation of this function is not critical - it could retrieve data from a database or other sources containing candidate names and their salary expectations. The only important detail is that this function returns a tuple where the first element is the candidate’s name, and the second element is their initial salary proposal:

def pick_user() -> tuple[str, int]:
    ...

We can now populate the list with an initial set of four candidates:

for candidate in [pick_user() for _ in range(4)]:
    candidates["Name"].append(candidate[0])
    candidates["Salary"].append(candidate[1])

Now, we define our table component:

Definition

<|{candidates}|table|editable[Salary]|on_delete=check_delete|on_add=check_add|on_edit=force_salary|show_all|>
<taipy:table editable[Salary] on_delete="check_delete" on_add="check_add" on_edit="force_salary" show_all>{candidates}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{candidates}", editable__Salary=True, on_delete=check_delete, on_add=check_add, on_edit=force_salary, show_all=True)

Note that:

  • The cells in the "Salary" column are editable.
  • The on_add and on_delete properties are set to invoke functions that check for the number of candidates in the list before performing the operation.
  • The on_edit property is set to invoke the force_salary() function.

Below is the initial state of the table when the application starts:

Initial table

Removing rows

To control the behavior of the application when a row deletion is requested, we must define a callback function and assign it to the on_delete property of the table.

The following implementation ensures that at least three candidates remain in the candidates list before performing any deletion:

1
2
3
4
5
6
def check_delete(state: State, var_name: str, payload: dict):
    n_candidates = len(state.candidates["Name"])
    if n_candidates <= 3:
        notify(state, "E", "Too few candidates")
    else:
        state.get_gui().table_on_delete(state, var_name, payload)

  • In line 2, we store the current number of candidates (the number of rows in any column of state.candidates) in n_candidates.
  • If there are three candidates or less (see line 3), a notification is triggered and the deletion is prevented.
  • If more than three candidates remain, the deletion is allowed and is performed by the call to the table_on_delete() method.
    The parameters passed to table_on_delete() are the same as those received by check_delete() since there is no need to modify them before forwarding the request.

Starting with the initial state (where four candidates are in the list), a row can be deleted by clicking the icon (Delete) on the corresponding row, and then confirming the action by pressing the icon (Apply).
Here is what the table look like after removing the third line:

One candidate was removed

There are now three candidates in the list. If you attempt to delete another row, the notification will appear, and the deletion will be blocked:

Row cannot be removed

Adding rows

The on_add property is assigned to a function that controls how the application handles row additions. Below is the implementation of the callback function in this example:

1
2
3
4
5
6
7
8
def check_add(state: State, var_name: str, payload: dict):
    n_candidates = len(state.candidates["Name"])
    if n_candidates >= 6:
        notify(state, "E", "Too many candidates")
    else:
        new_row = pick_user()
        payload["index"] = n_candidates
        state.get_gui().table_on_add(state, var_name, payload, new_row=list(new_row))

  • In line 2, we store the current number of candidates (the number of rows in a column of state.candidates) in n_candidates.
  • If there are six candidates or more (see line 3), a notification is triggered and the addition of a new row is prevented.
  • If there are less than six candidates, the creation of a new row is allowed.
    • On line 6, a tuple with the new candidate's data is created using the pick_user() function.
    • On line 7, payload["index"] is set to the current number of candidates to force the new row to be added at the end of the list (the default behavior is to add it at the top).
    • On line 8, we invoke the table_on_add() method with the modified payload value, and the values for the new row, converted to a list.

Assuming the application is in its initial state with four candidates, you can add a row by clicking the icon (Add) at the top of the table control.
The result is displayed like this:

A new row was created

If we add another row then try to add yet another one, we get the notification:

A new row cannot be added

Controlling cell value edits

The on_edit property allows you to control the values that users enter when modifying a cell's content. We can use this property to enforce the constraint that the salary must be rounded to the nearest multiple of 500.

Here is the implementation of the force_salary() function, which is assigned to the on_edit property of the table control in this example:

1
2
3
4
5
def force_salary(state: State, var_name: str, payload: dict):
    proposed_salary = payload["value"]
    proposed_salary = round(proposed_salary / 500) * 500
    payload["value"] = proposed_salary
    state.get_gui().table_on_edit(state, var_name, payload)

Let's explain what this function does line by line:

  • Line 2: Retrieves the value entered by the user from the payload dictionary, sent by the on_edit callback.
  • Line 3: Rounds the value to the nearest multiple of 500, satisfying the constraint, and assign the result to proposed_salary.
  • Line 4: Updates the "value" entry of the payload dictionary with the rounded salary to ensure the correct value is stored in the dataset.
  • Line 5: Delegate the update of the dataset and the control by calling table_on_edit().

Assume the user enters a random salary value for a candidate:

The user enters a new value

Obviously, the proposed value (963852) does not meet the constraint.

After the user validates the input (clicking the icon (Apply)), the force_salary() function is invoked to automatically adjusts the salary to the nearest multiple of 500, and the updated value is displayed:

Salary is adjusted

Aggregation

To get the aggregation functionality in your table, you must indicate which columns can be aggregated and how to perform the aggregation.

This is done using the indexed group_by and apply properties.

The group_by[column_name] property, when set to True indicates that the column column_name can be aggregated.

The function provided in the apply[column_name] property indicates how to perform this aggregation. The value of this property, which is a string, can be:

  • A built-in function. Available predefined functions are the following: count, sum, mean, median, min, max, std, first (the default value), and last.
  • The name of a user-defined function or a lambda function.
    This function receives a single parameter which is the series to aggregate, and it must return a scalar value that would result from the aggregation.

This control definition is all it takes to add aggregation functionality to the table:

Definition

<|{data}|table|group_by[Group column]|apply[Apply column]=count|>
<taipy:table group_by[Group column] apply[Apply column]="count">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", properties="{properties}")

Note

Note that we are using the properties property.
This is due to the fact that the column names cannot be used when expressing the indexed property name, which would not be a valid Python identifier. The workaround is to create a dictionary called properties that defines those property values:

properties = {
    "group_by[Group column]": True,
    "apply[Apply column]": "count"
}

Cell tooltips

You can specify a tooltip for specific table cells.

When Taipy creates the cells, it can add a specific tooltip that you would have set as the return value of the function set to the tooltip or tooltip[column_name] properties.

The signature of this function expects five optional parameters: - state: the current state. - value: the value of the cell. - index: the index of the row in this table. - row: all the values for this row. - column_name: the name of the column for this cell.

Based on these parameters, the function must return a string that defines a tooltip used as the cell's tooltip text.

Definition

<|{data}|table|tooltip={lambda state, val, idx: "A tooltip" if idx % 2 == 0 else "Another tooltip"}|>
<taipy:table tooltip="{lambda state, val, idx: 'A tooltip' if idx % 2 == 0 else 'Another tooltip'}">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", tooltip=lambda state, val, idx: "A tooltip" if idx % 2 == 0 else "Another tooltip")

Styling

All the table controls are generated with the "taipy-table" CSS class. You can use this class name to select the tables on your page and apply style.

Stylekit support

The Stylekit provides a CSS custom property:

  • --table-stripe-opacity
    This property contains the opacity applied to odd lines of tables.
    The default value is 0.5.

The Stylekit also provides specific CSS classes that you can use to style tables:

  • header-plain
    Adds a plain and contrasting background color to the table header.
  • rows-bordered
    Adds a bottom border to each row.
  • rows-similar
    Removes the even-odd striped background so all rows have the same background.

Dynamic styling

You can modify the style of entire rows or specific table cells based on any criteria, including the table data itself.

When Taipy creates the rows and the cells, it can add a specific CSS class to the generated elements. This class name is the string returned by the function set to the row_class_name property for entire rows, or cell_class_name[column_name] for specific cells.

The signature of this function depends on which property you use:

  • row_class_name: this applies to entire rows.
    The given function expects three optional parameters:
    • state: the current state
    • index: the index of the row in this table
    • row: all the values for this row
  • cell_class_name[column_name]: this applies to specific cells.
    The given function expects five optional parameters:
    • state: the current state
    • value: the value of the cell
    • index: the index of the row in this table
    • row: all the values for this row
    • column_name: the name of the column for this cell

Based on these parameters, the function must return a string that defines a CSS class name that will be added to the CSS classes for this table row or this specific cell.
The example below shows how this works.

Styling rows

To apply a specific CSS class to a table row, you will use the row_class_name property (note that you can find general instructions on how to apply style to visual elements in this page).
This property holds a function that is invoked when each row is rendered, and it must return the name of a class, defined in CSS.

Here is how a row styling function can be defined:

def even_odd_class(_1, row):
    if row % 2:
        return "blue-row"
    else:
        return "red-row"

We only use the second parameter since, in this straightforward case, we do not need the application state (first parameter) or the values in the row (third parameter).
Based on the row index (received in index), this function returns the name of the CSS class to apply to the row: "blue-row" if the index is odd, "red-row" if it is even.

We need to define what these class define. This is done in a CSS style sheet, where the following CSS content would appear:

.blue-row>td {
    color: white;
    background-color: blue;
}
.red-row>td {
    color: yellow;
    background-color: red;
}

Note that the CSS selectors use the child combinator selector ">" to target elements that hold a td element (the cells themselves).

To use this style, we can adjust the control definition used above so it looks like this:

Definition

<|{data}|table|row_class_name=even_odd_class|>
<taipy:table row_class_name="even_odd_class">{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", row_class_name=even_odd_class)

The resulting display will be what we expected:

Styling the rows

The "red-row" CSS class applies to every even row and similarly the "blue-row" CSS class for odd rows.

Note that the styling function is so simple that we could have made it a lambda, directly in the control definition:

Alternative definition

<|{data}|table|row_class_name={lambda _, row: "blue-row" if row % 2 else "red-row"}|show_all|>
<taipy:table data="{data}" row_class_name="{lambda _, row: 'blue-row' if row % 2 else 'red-row'}" show_all/>
import taipy.gui.builder as tgb
...
tgb.table("{data}", row_class_name=lambda _, row: "blue-row" if row % 2 else "red-row", show_all=True)

This definition of the control makes the even_odd_class() function useless.

Styling cells

To change the style of specific cells, you must use the cell_class_name[] indexed property. This property should be set to a function that returns a CSS class name, allowing dynamic styling based on the parameters passed to the function. These parameters are:

  • The value of the cell,
  • The index of the row,
  • The value of another column in the same row,
  • The name of the column.

Here is an example of how to use this property.
Consider a table with three columns:

  • Column "x" contains integers from 0 to 10.
    Requirement: Cells in the "x" column should have a background color alternating between two shades of green.
  • Column "y" contains the square of the corresponding values in column "x".
    Requirement: Cells in the "y" column should have a background color alternating between two shades of red.
  • Column "z" contains random values between 0 and 5.
    Requirement: Cells in the "z" column should have a background color that varies based on the value: the smaller the value, the lighter the shade of blue.

Here are the CSS classes that this application will use:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
.reddish {
    /* Set text color to white */
    color: white;
    /* Strong red background */
    background-color: #bf1313;
}
.light-reddish {
    /* Set text color to black */
    color: black;
    /* Light red background */
    background-color: #ff1919;
    /* Set bold typeface */
    font-weight: bold;
}
.greenish {
    /* Set text color to white */
    color: white;
    /* Green background */
    background-color: #75bf75;
}
.light-greenish {
    /* Set text color to black */
    color: black;
    /* Light green background */
    background-color: #9cff9c;
    /* Set bold typeface */
    font-weight: bold;
}
.col0 {
    /* Light gray background */
    background-color: #d0d0d0;
}
.col1 {
    /* Light purple background */
    background-color: #a4a0cf;
}
.col2 {
    /* Medium purple background */
    background-color: #7970cf;
}
.col3 {
    /* Darker purple background */
    background-color: #4e40cf;
    /* Set text color to white */
    color: white;
}
.col4 {
    /* Dark purple background */
    background-color: #2410cf;
    /* Set text color to white */
    color: white;
}
.col5 {
    /* Dark blue background */
    background-color: #1b02a8;
    /* Set text color to white */
    color: white;
}

  • The "reddish" and "light-reddish" classes (lines 1 to 13) will apply to the "x" column. The "reddish" class has a strong red background and uses white text, while the "light-reddish" class uses a lighter red background with black text and bold typeface.
  • The "greenish" and "light-greenish" classes (lines 15 to 28) will apply to the "y" column. The "greenish" class has a green background with white text, while the "light-greenish" class has a lighter green background with black text and bold typeface.
  • The "col0" to "col5" classes (lines 29 to 58) define the background color for the "z" column. These classes create a gradient from light gray ("col0") to dark blue ("col5"). For classes with darker backgrounds (from "col3" to "col5"), white text is used for better readability.

Below is how we configure the table control:

Definition

<|{data}|table|cell_class_name[x]=xy_class|cell_class_name[y]=xy_class|cell_class_name[z]=z_class|show_all|>
<taipy:table cell_class_name[x]="xy_class" cell_class_name[y]="xy_class" cell_class_name[z]="z_class" show_all>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", cell_class_name__x=xy_class, cell_class_name__y=xy_class, cell_class_name__z=z_class, show_all=True)

Both cell_class_name[x] and cell_class_name[y] are set to the xy_class() function defined below.
cell_class_name[z] is set to z_class(), which functions differently to meet specific requirements.

Here is the definition for xy_class() and z_class(), used in the control definition:

def xy_class(_1, _2, index, _3, column_name):
    return (
        ("greenish" if index % 2 else "light-greenish")
        if column_name == "x"
        else ("reddish" if index % 2 else "light-reddish")
    )

def z_class(_, value):
    return f"col{value}"

xy_class() uses the column name (passed as the column_name parameter) and the row index (passed as the index parameter) to determine the CSS class for each cell:

  • If the column is "x", it applies either the "greenish" or "light-greenish" class, alternating based on the row index (even or odd).
  • For the column "y" (else case), it applies either the "reddish" or "light-reddish" class, also alternating based on the row index.

z_class() dynamically builds a CSS class name based on the value of the cell, using the value parameter. For example, if the value is 2, it will return "col2".

The result is a table where each cell is styled with the appropriate CSS class based on its location or value, ensuring consistent and logical styling across the table:

Styling the cells

Note that the implementation of z_class() is simple enough that it could be replaced with a lambda function directly in the control definition:

Definition

<|{data}|table|cell_class_name[x]=xy_class|cell_class_name[y]=xy_class|cell_class_name[z]={lambda _, v: f'col{v}'}|show_all|>
<taipy:table cell_class_name[x]="xy_class" cell_class_name[y]="xy_class" cell_class_name[z]="{lambda _, v: f'col{v}'}" show_all>{data}</taipy:table>
import taipy.gui.builder as tgb
...
tgb.table("{data}", cell_class_name__x=xy_class, cell_class_name__y=xy_class, cell_class_name__z=lambda _, v: f'col{v}', show_all=True)

In this case, the z_class() function would no longer be needed.