Table
A data table with a fixed header, optional sorting and row selection. Columns are ui.Column objects whose key is a dict key, an attribute name or a function of the row; render turns a value into any widget. The table sorts the rows itself by the column whose title is sort_by: you only store that title and the direction.
ui.table · Category: Data · Full reference →


rows = [ {"name": "render.rs", "lang": "Rust", "lines": 1840}, {"name": "app.py", "lang": "Python", "lines": 512}, {"name": "theme.py", "lang": "Python", "lines": 730}, {"name": "docs.yml", "lang": "YAML", "lines": 64},]pz.container(ui.table( [ui.Column("File", "name"), ui.Column("Language", "lang", width=120), ui.Column("Lines", "lines", width=80, align="end")], rows, sort_by="Lines", descending=True, on_sort=on_sort, selected=rows[1], striped=True,), width=520)When to use
Section titled “When to use”- For records with several comparable fields: files, users, processes, results.
- For a single column of items, a list of List items is lighter.
import pyzed as pzfrom pyzed import ui
class Files(pz.App): def __init__(self): self.rows = [{"name": "app.py", "lines": 512}, {"name": "theme.py", "lines": 730}] self.sort_by, self.descending, self.selected = "Lines", True, None
def sort(self, title): self.descending = not self.descending if self.sort_by == title else False self.sort_by = title
def view(self): return ui.table( [ui.Column("File", "name"), ui.Column("Lines", "lines", width=80, align="end")], self.rows, sort_by=self.sort_by, descending=self.descending, on_sort=self.sort, on_row_press=lambda row: setattr(self, "selected", row), selected=self.selected, )Variants
Section titled “Variants”Options
Section titled “Options”

Code
pz.container(ui.table([ui.Column("File", "name"), ui.Column("Lines", "lines", width=80, align="end")], [{"name": "app.py", "lines": 512}, {"name": "theme.py", "lines": 730}, {"name": "ui.py", "lines": 1290}]), width=320)

striped=TrueCode
pz.container(ui.table([ui.Column("File", "name"), ui.Column("Lines", "lines", width=80, align="end")], [{"name": "app.py", "lines": 512}, {"name": "theme.py", "lines": 730}, {"name": "ui.py", "lines": 1290}], striped=True), width=320)

selected=Code
rows = [{"name": "app.py", "lines": 512}, {"name": "theme.py", "lines": 730}, {"name": "ui.py", "lines": 1290}]pz.container(ui.table([ui.Column("File", "name"), ui.Column("Lines", "lines", width=80, align="end")], rows, selected=rows[1]), width=320)

sort_by=, descending=TrueCode
pz.container(ui.table([ui.Column("File", "name"), ui.Column("Lines", "lines", width=80, align="end")], [{"name": "app.py", "lines": 512}, {"name": "theme.py", "lines": 730}, {"name": "ui.py", "lines": 1290}], sort_by="Lines", descending=True, on_sort=on_sort), width=320)

row_height=24Code
pz.container(ui.table([ui.Column("File", "name"), ui.Column("Lines", "lines", width=80, align="end")], [{"name": "app.py", "lines": 512}, {"name": "theme.py", "lines": 730}, {"name": "ui.py", "lines": 1290}], row_height=24), width=320)

render=Code
rows = [{"job": "build", "state": "passed"}, {"job": "lint", "state": "failed"}]tint = {"passed": "success", "failed": "error"}pz.container(ui.table([ui.Column("Job", "job"), ui.Column("State", "state", width=110, render=lambda s: ui.badge(s, tint=tint[s]))], rows), width=320)

key=lambdaCode
people = [("Ana", 31), ("Luis", 27)]pz.container(ui.table([ui.Column("Name", lambda p: p[0]), ui.Column("Age", lambda p: p[1], width=70, align="end")], people), width=320)States and events
Section titled “States and events”on_row_double_press(row) opens a row and on_row_context(row) shows a context menu.


Code
rows = [{"name": "app.py", "size": 4}, {"name": "ui.py", "size": 12}, {"name": "README.md", "size": 2}]ui.table(["name", "size"], rows, selected=[rows[0], rows[2]], on_row_press=on_press, on_row_double_press=on_open, on_row_context=on_context, height=130)

empty=Code
ui.table(["name", "size"], [], empty="No files yet", height=100)Examples
Section titled “Examples”Custom cells
Section titled “Custom cells”render receives the value and returns a widget, here a badge.


rows = [{"job": "build", "state": "passed"}, {"job": "lint", "state": "failed"}, {"job": "docs", "state": "running"}]tint = {"passed": "success", "failed": "error", "running": "warning"}pz.container(ui.table( [ui.Column("Job", "job"), ui.Column("State", "state", width=120, render=lambda s: ui.badge(s, tint=tint[s]))], rows,), width=400)Events
Section titled “Events”Table has 4 events of its own, listed below. Like every widget it also has the 18 common events (click, double_click, right_click, mouse_enter, drag, key_down, resize…), attached with .on(...): see Events and Events of every component.
| Event | Handler gets | When |
|---|---|---|
on_sort |
(title) |
Callable that receives the clicked title. |
on_row_press |
(row) |
Callable that receives the clicked row. |
on_row_double_press |
(row) |
Called with the row on a double click (open, edit…). |
on_row_context |
(row) |
Called with the row on a right click (a context menu). |
Handler gets is what Pyzed passes to your callable: () means nothing. Any handler can also be a message (a value that is not callable), which goes to update().
Handling the events
Section titled “Handling the events”The same events written three ways. Imperative: methods of the app change self. Declarative: the handlers produce messages and one update() decides. Functional: a pure function draws an immutable state and asks for changes with set_state(...). See Imperative, declarative and functional.
class Files(pz.App): def __init__(self): self.rows = [{"name": "app.py", "lines": 512}, {"name": "theme.py", "lines": 730}] self.sort_by, self.descending = "Lines", True self.selected, self.opened = None, None
def sort(self, title): # on_sort: the clicked column title self.descending = not self.descending if self.sort_by == title else False self.sort_by = title
def view(self): key = "name" if self.sort_by == "File" else "lines" rows = sorted(self.rows, key=lambda r: r[key], reverse=self.descending) return ui.table( [ui.Column("File", "name"), ui.Column("Lines", "lines", width=80, align="end")], rows, sort_by=self.sort_by, descending=self.descending, on_sort=self.sort, selected=self.selected, on_row_press=lambda row: setattr(self, "selected", row), # click on_row_double_press=lambda row: setattr(self, "opened", row), # double click on_row_context=lambda row: print("menu for", row["name"]), # right click )from dataclasses import dataclass
@dataclassclass Sort: title: str
@dataclassclass Select: row: dict
class Files(pz.App): rows = [{"name": "app.py", "lines": 512}, {"name": "theme.py", "lines": 730}] sort_by, descending, selected = "Lines", True, None
def update(self, message): match message: case Sort(title): self.descending = not self.descending if title == self.sort_by else False self.sort_by = title case Select(row): self.selected = row
def view(self): return ui.table([ui.Column("File", "name"), ui.Column("Lines", "lines", width=80)], self.rows, sort_by=self.sort_by, descending=self.descending, selected=self.selected, on_sort=Sort, on_row_press=Select)from dataclasses import dataclass
ROWS = ({"name": "app.py", "lines": 512}, {"name": "theme.py", "lines": 730})
@dataclass(frozen=True)class State: sort_by: str = "Lines" descending: bool = True selected: str = ""
def view(state, set_state): key = "name" if state.sort_by == "File" else "lines" rows = sorted(ROWS, key=lambda r: r[key], reverse=state.descending) return ui.table( [ui.Column("File", "name"), ui.Column("Lines", "lines", width=80)], rows, sort_by=state.sort_by, descending=state.descending, selected=next((r for r in rows if r["name"] == state.selected), None), on_sort=lambda t: set_state(sort_by=t, descending=not state.descending if t == state.sort_by else False), on_row_press=lambda row: set_state(selected=row["name"]))Properties and API
Section titled “Properties and API”ui.table
Section titled “ui.table”table( columns, rows, *, sort_by=None, descending=False, on_sort=None, on_row_press=None, selected=None, striped=False, row_height=30, height=None, on_row_double_press=None, on_row_context=None, empty=None, theme=None,)A data table with a fixed header, optional sorting and row selection. Sorting happens here when sort_by names a column: clicking a header calls on_sort(title) and you update sort_by/descending in your state. Rows are virtualized: only the ones in view are built, so hundreds of thousands of rows scroll smoothly.
| Property | Default | Description |
|---|---|---|
columns |
required | ui.Column or plain keys. |
rows |
required | Dicts or objects. |
sort_by |
None |
Title of the column to sort by. |
descending |
False |
Descending order. |
selected |
None |
The row to highlight, or a list or set of rows (multiple selection). |
striped |
False |
Shaded alternating rows. |
row_height |
30 |
Height of each row. |
height |
None |
Height: pixels, "fill", "shrink" or "fill:N" (proportion). |
empty |
None |
Text or widget shown in place of the rows when there are none. |
theme |
None |
Theme to use instead of the current one (pyzed.theme.current()). |
Events: on_sort, on_row_press, on_row_double_press, on_row_context (events).
ui.Column
Section titled “ui.Column”@dataclassclass Column(title, key, width='fill', align='start', render=None, sortable=True)A column of ui.table. key is a dict key, an attribute name or a function of the row; render (optional) turns the value into a widget. width is "fill", a number or a portion such as "fill:2".
| Property | Default | Description |
|---|---|---|
title |
required | Header (and name used for sorting). |
key |
required | How to get the value from each row. |
width |
"fill" |
Width: pixels, "fill", "shrink" or "fill:N" (proportion). |
align |
"start" |
"start", "center" or "end". |
render |
None |
Function value → widget. |
sortable |
True |
Whether the header can be clicked to sort. |