Repository navigation
DataTable
A table of dict rows: sortable, filterable, selectable, editable in place, with a right-click menu, resizable, reorderable and frozen columns, drop-to-upload, and CSV export.
from wapyt import ColumnConfig, DataTableConfig, TableAction
table = self.add_datatable("files", DataTableConfig(
columns=[
ColumnConfig(id="name", header="Name", icon_by="icon"),
ColumnConfig(id="size", header="Size", align="right", width=90, sort_by="size_bytes"),
],
selection="multi",
filterable=True,
group_dirs_first="is_dir",
context_actions=[TableAction("download", "Download", "mdi-download")],
))
table.set_rows(rows)
table.on_activate(lambda p: open_entry(p["row"]))They run in the browser on the rows already loaded, with no round trip.
Pre-format display values in Python (size as "1.2 MB") and point
sort_by at the raw field so the order stays right. For server-side sorting,
see how Monguana points every column's sort_by at a hidden rank key.
ColumnConfig(id, header, width, align, sortable, sort_by, type, ellipsis, icon_by, editable, editor, options, required, hidden, css, cell_class_by):
-
type="icon": the value is an MDI class, drawn as a glyph. -
icon_by="key": draw the MDI class inrow[key]before a text cell's value;row[key + "_title"]is its tooltip. -
ellipsis(default on): truncate, with a tooltip of the full text that shows only when the text is actually cut off.
Added in #37.
DataTableConfig(columns=[
ColumnConfig(id="name", header="Name", editable=True, required=True),
ColumnConfig(id="qty", header="Qty", align="right", editable=True, editor="number"),
ColumnConfig(id="status", header="Status", editable=True, editor="select",
options=["open", {"value": "done", "label": "Done"}]),
ColumnConfig(id="done", header="Done", editable=True, editor="checkbox"),
ColumnConfig(id="due", header="Due", editable=True, editor="date"),
])
def saved(e): # {id, column, value, old_value, row}
if not api.update(e["id"], e["column"], e["value"]):
table.set_cell(e["id"], e["column"], e["old_value"])
table.set_cell_error(e["id"], e["column"], "The server refused this change")
table.on_edit(saved)editor |
Edits with |
value in on_edit
|
|---|---|---|
text (default) |
a text box | string |
number |
a text box that must parse as a number | number, or None when emptied |
select |
a dropdown of options; the cell shows the option's label |
the option's value |
checkbox |
a checkbox drawn in the cell; click or Space toggles and saves | bool |
date |
the browser's date picker |
"YYYY-MM-DD", or None
|
-
Start editing by double-clicking an editable cell (other cells still
fire
on_activate), or on a focused cell press Enter or F2, or just start typing. Arrow keys move between editable cells, and the whole table is one Tab stop. - Finish with Enter (save), Tab / Shift+Tab (save and move on), or Escape (cancel; an enclosing ModalWindow stays open). Clicking away saves, or drops a value that is not valid.
-
Validation: a number that does not parse, or an emptied
requiredcell, keeps the editor open with the reason. -
Saving changes the row in place. Only that cell is redrawn, so the row
stays where it is until the next sort, filter or
set_rows. -
When the server says no,
set_cellputs a value back without firingon_edit, andset_cell_errormarks the cell red with the message as its tooltip (Noneclears it; a successful edit clears it too). -
edit_cell(id, column)opens an editor from code, for example on a row you just added;cancel_edit()closes one without saving.
TableAction(id, label, icon, danger, separator) entries in
context_actions. Right-clicking a selected row keeps a multi-selection;
right-clicking any other row selects just that row. The menu is a ContextMenu: ↑/↓, Home/End and Enter work,
Escape closes it, Shift+F10 or the Menu key opens it from the keyboard, and focus
returns to where it was.
DataTableConfig |
|
|---|---|
id_field |
Row key holding the stable id (default "id") |
selection |
"none", "single" (default) or "multi" (checkboxes, ctrl/shift clicks) |
sort_by, sort_dir
|
Initial sort |
group_dirs_first |
Row key whose truthy rows stay on top whatever the sort |
resizable_columns, reorderable_columns, min_column_width
|
Opt-in column layout editing |
frozen_columns |
Leading columns that stay put while the table scrolls sideways; see Frozen columns |
row_class_by |
Row key holding class names for that row; see Row and cell classes |
drop_upload |
Accept dropped files |
filter_placeholder, empty_text, loading_text
|
Copy |
Once any column is resized, every column gets a pixel width and the table can
scroll sideways. Persist layouts from on_columns; restore them with
set_columns (widths and hidden columns included).
Added in #42. Style rows and cells from their data, with your own CSS:
DataTableConfig(row_class_by="status_class", columns=[
ColumnConfig(id="task", header="Task"),
ColumnConfig(id="due", header="Due", css="num", cell_class_by="due_class"),
])
# rows: {"id": "1", "task": "Invoice", "due": "...", "status_class": "overdue", "due_class": "late"}| Option | Adds |
|---|---|
DataTableConfig(row_class_by="key") |
The class names in row[key] (a space-separated string or a list) to the row |
ColumnConfig(css="...") |
Fixed classes to the column's header and every cell |
ColumnConfig(cell_class_by="key") |
The class names in row[key] to that column's cell in each row |
Only values that look like class names are applied, so data cannot break the
markup. Classes stay through edits and set_cell. The older row["_class"]
still works.
Added in #42.
ColumnConfig(id="internal_id", header="ID", hidden=True)
table.hide_column("owner"); table.show_column("owner")- A hidden column keeps its place and width, so showing it puts it back where it was.
- It does not render, match the filter, freeze, edit, or export (unless named
in
to_csv(columns=[...])). -
on_columnsfires withreason="hide"or"show", andget_column_state()includeshidden, so a saved layout restores it. - Methods:
hide_column,show_column,set_column_hidden(id, hidden),is_column_hidden.
DataTableConfig(columns=[...], frozen_columns=2) # dhxpyt's leftSplit
table.set_frozen_columns(3) # later; 0 unfreezesAdded in #38. The first N columns stay in place while the
rest scroll sideways; with selection="multi" the checkbox column goes with
them.
-
Only matters once the table is wider than its panel, which happens when
the column widths add up to more than the panel (fixed
widths orresizable_columns). -
They stay right as things change: resizing a column or the panel moves
the frozen offsets, and sorts,
set_rowsand edits keep them. - They look solid: frozen cells carry the row's hover and selected tints, so scrolled cells never show through, and the top-left corner stays on top when you scroll both ways. The frozen edge has a line, and a shadow once the table is scrolled.
csv_text = table.to_csv() # the CSV as a string
table.export_csv("documents.csv") # a browser download; returns the row count
table.export_csv("picked.csv", selected_only=True, columns=["name", "size"])Added in #39. It exports what the table shows: the filtered, sorted rows, in the current column order, with a select column's labels.
| Option | |
|---|---|
selected_only |
Only the selected rows |
columns |
Column ids to include, in this order (icon columns are left out unless named) |
raw |
Stored values instead of what the cells show |
header |
Include the header row (default on) |
delimiter |
One character, default ,
|
safe |
Default on; see below |
bom |
export_csv only: a UTF-8 byte-order mark (default on), so Excel reads accented text |
-
Safe to open in a spreadsheet. With
safeon, text that Excel or Sheets would run as a formula (starting with=,+,-,@, a tab or a carriage return) gets a leading', so a malicious value from a database cannot run when someone opens the file. Numbers, including negative ones, are left alone. - Standard CSV quoting (commas, quotes, line breaks, leading or trailing
spaces) and CRLF line ends; booleans as
true/false, empty values empty. - The download is built in the browser, with no server round trip. For exports bigger than what is loaded (every document a query matches), build the file on the server and use File Transfer.
| Method | Payload |
|---|---|
on_select |
{ids, id, rows} |
on_activate |
{id, row} (double-click; on an editable cell, a double-click edits instead) |
on_edit |
{id, column, value, old_value, row} |
on_action |
{action, id, row, selected} |
on_sort |
{column, direction} |
on_filter |
{value} |
on_columns |
{reason: "resize" | "reorder" | "hide" | "show", column, columns: [{id, width, hidden}]} |
on_drop |
{files: [{name, size, type}]} (metadata only) |
Dropped files: read the handles with get_dropped_files() and pass them to
filetransfer.adopt(). Bytes never cross into Python.
set_rows, get_rows, get_row, set_columns, get_column_state,
move_column, get_selected_ids, get_selected_rows, select,
clear_selection, sort, set_filter, set_busy, set_empty_text,
set_cell, set_cell_error, edit_cell, cancel_edit, set_frozen_columns,
hide_column, show_column, set_column_hidden, is_column_hidden,
to_csv, export_csv, destroy.
Selecting rows updates them in place without redrawing the table (since #37), so focus and row positions hold steady while you click.
No footer rows or row spans; see the Roadmap.
wapyt is MIT-licensed · Repository · pyTincture · Issues
Getting started
Widgets
- Layout and MainWindow
- Tree
- DataTable
- Form
- ModalWindow
- Window
- TabWidget
- Toolbar
- Sidebar
- Chat
- Terminal
- CardPanel
- ResourceBoard
- File Transfer
- Message
- ContextMenu
- MenuBar
- Listbox
- Kanban
- ProgressBar
- Pagination
- Popup and tooltips
- Chart
- Scheduler
- MediaPlayer
Guides
Planning
Contributing