1
0
Fork 0

Adding upstream version 0.12.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-24 10:57:24 +01:00
parent d887bee5ca
commit 148efc9122
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
69 changed files with 12923 additions and 0 deletions

View file

@ -0,0 +1,185 @@
from textual.app import App
from textual.containers import Container, Horizontal, ScrollableContainer, Vertical
from textual.screen import Screen
from textual.widgets import Header, Label
from textual_fastdatatable import ArrowBackend, DataTable
class LabeledBox(Container):
DEFAULT_CSS = """
LabeledBox {
layers: base_ top_;
width: 100%;
height: 100%;
}
LabeledBox > Container {
layer: base_;
border: round $primary;
width: 100%;
height: 100%;
layout: vertical;
}
LabeledBox > Label {
layer: top_;
offset-x: 2;
}
"""
def __init__(self, title, *args, **kwargs):
self.__label = Label(title)
super().__init__(self.__label, Container(*args, **kwargs))
@property
def label(self):
return self.__label
class StatusTable(DataTable):
def __init__(self) -> None:
backend = ArrowBackend.from_pydict(
{
"Foo": ["ABCDEFGH"] * 50,
"Bar": ["0123456789"] * 50,
"Baz": ["IJKLMNOPQRSTUVWXYZ"] * 50,
}
)
super().__init__(backend=backend)
self.cursor_type = "row"
self.show_cursor = False
class Status(LabeledBox):
DEFAULT_CSS = """
Status {
width: auto;
}
Status Container {
width: auto;
}
Status StatusTable {
width: auto;
height: 100%;
margin-top: 1;
scrollbar-gutter: stable;
overflow-x: hidden;
}
"""
def __init__(self, name: str):
self.__name = name
self.__table = StatusTable()
super().__init__(f" {self.__name} ", self.__table)
@property
def name(self) -> str:
return self.__name
@property
def table(self) -> StatusTable:
return self.__table
class Rendering(LabeledBox):
DEFAULT_CSS = """
#issue-info {
height: auto;
border-bottom: dashed #632CA6;
}
#statuses-box {
height: 1fr;
width: auto;
}
"""
def __init__(self):
self.__info = Label("test")
super().__init__(
"",
ScrollableContainer(
Horizontal(self.__info, id="issue-info"),
Horizontal(*[Status(str(i)) for i in range(4)], id="statuses-box"),
id="issues-box",
),
)
@property
def info(self) -> Label:
return self.__info
class Sidebar(LabeledBox):
DEFAULT_CSS = """
#sidebar-status {
height: auto;
border-bottom: dashed #632CA6;
}
#sidebar-options {
height: 1fr;
}
"""
def __init__(self):
self.__status = Label("ok")
self.__options = Vertical()
super().__init__(
"",
Container(self.__status, id="sidebar-status"),
Container(self.__options, id="sidebar-options"),
)
@property
def status(self) -> Label:
return self.__status
@property
def options(self) -> Vertical:
return self.__options
class MyScreen(Screen):
DEFAULT_CSS = """
#main-content {
layout: grid;
grid-size: 2;
grid-columns: 1fr 5fr;
grid-rows: 1fr;
}
#main-content-sidebar {
height: 100%;
}
#main-content-rendering {
height: 100%;
}
"""
def compose(self):
yield Header()
yield Container(
Container(Sidebar(), id="main-content-sidebar"),
Container(Rendering(), id="main-content-rendering"),
id="main-content",
)
class MyApp(App):
async def on_mount(self):
self.install_screen(MyScreen(), "myscreen")
await self.push_screen("myscreen")
if __name__ == "__main__":
app = MyApp()
app.run()

View file

@ -0,0 +1,26 @@
from textual.app import App, ComposeResult
from textual_fastdatatable import ArrowBackend, 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:
backend = ArrowBackend.from_records(ROWS, has_header=True)
yield DataTable(backend=backend)
app = TableApp()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,36 @@
from textual.app import App, ComposeResult
from textual.binding import Binding
from textual_fastdatatable import ArrowBackend, DataTable
MOVIES = [
"Severance",
"Foundation",
"Dark",
"The Boys",
"The Last of Us",
"Lost in Space",
"Altered Carbon",
]
class AddColumn(App):
BINDINGS = [
Binding(key="c", action="add_column", description="Add Column"),
]
def compose(self) -> ComposeResult:
backend = ArrowBackend.from_pydict({"Movies": MOVIES})
table = DataTable(backend=backend)
column_idx = table.add_column("No Default")
table.add_column("With Default", default="ABC")
table.add_column("Long Default", default="01234567890123456789")
# Ensure we can update a cell
table.update_cell(2, column_idx, "Hello!")
yield table
app = AddColumn()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,24 @@
from rich.panel import Panel
from rich.text import Text
from textual.app import App
from textual_fastdatatable import DataTable
class AutoHeightRowsApp(App[None]):
def compose(self):
table = DataTable()
self.column = table.add_column("N")
table.add_column("Column", width=10)
table.add_row(3, "hey there", height=None)
table.add_row(1, Text("hey there"), height=None)
table.add_row(5, Text("long string", overflow="fold"), height=None)
table.add_row(2, Panel.fit("Hello\nworld"), height=None)
table.add_row(4, "1\n2\n3\n4\n5\n6\n7", height=None)
yield table
def key_s(self):
self.query_one(DataTable).sort(self.column)
if __name__ == "__main__":
AutoHeightRowsApp().run()

View file

@ -0,0 +1,35 @@
import csv
import io
from textual.app import App, ComposeResult
from textual_fastdatatable import ArrowBackend, DataTable
CSV = """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"""
class TableApp(App):
def compose(self) -> ComposeResult:
rows = csv.reader(io.StringIO(CSV))
labels = next(rows)
data = [row for row in rows]
backend = ArrowBackend.from_pydict(
{label: [row[i] for row in data] for i, label in enumerate(labels)}
)
table = DataTable(
backend=backend, cursor_type="column", fixed_columns=1, fixed_rows=1
)
table.focus()
yield table
if __name__ == "__main__":
app = TableApp()
app.run()

View file

@ -0,0 +1,35 @@
import csv
import io
from textual.app import App, ComposeResult
from textual_fastdatatable import ArrowBackend, DataTable
CSV = """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"""
class TableApp(App):
def compose(self) -> ComposeResult:
rows = csv.reader(io.StringIO(CSV))
labels = next(rows)
data = [row for row in rows]
backend = ArrowBackend.from_pydict(
{label: [row[i] for row in data] for i, label in enumerate(labels)}
)
table = DataTable(
backend=backend, cursor_type="range", max_column_content_width=8
)
table.focus()
yield table
if __name__ == "__main__":
app = TableApp()
app.run()

View file

@ -0,0 +1,26 @@
from textual.app import App, ComposeResult
from textual_fastdatatable import ArrowBackend, DataTable
ROWS = [
("lane", "swimmer", "country", "time"),
(4, "[Joseph Schooling]", "Singapore", 50.39),
(2, "[red]Michael Phelps[/]", "United States", 51.14),
(5, "[bold]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:
backend = ArrowBackend.from_records(ROWS, has_header=True)
yield DataTable(backend=backend, render_markup=False)
app = TableApp()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,22 @@
from textual.app import App, ComposeResult
from textual_fastdatatable import ArrowBackend, DataTable
ROWS = [
("lane", "swimmer", "country", "time"),
(3, "Li Zhuhao", "China", 51.26),
("eight", None, "France", 51.58),
("seven", "Tom Shields", "United States", None),
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
(None, "Darren Burns", "Scotland", 51.84),
]
class TableApp(App):
def compose(self) -> ComposeResult:
backend = ArrowBackend.from_records(ROWS, has_header=True)
yield DataTable(backend=backend, null_rep="[dim]∅ null[/]")
app = TableApp()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,33 @@
import csv
import io
from textual.app import App, ComposeResult
from textual_fastdatatable import ArrowBackend, DataTable
CSV = """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"""
class TableApp(App):
def compose(self) -> ComposeResult:
rows = csv.reader(io.StringIO(CSV))
labels = next(rows)
data = [row for row in rows]
backend = ArrowBackend.from_pydict(
{label: [row[i] for row in data] for i, label in enumerate(labels)}
)
table = DataTable(backend=backend, cursor_type="range")
table.focus()
yield table
if __name__ == "__main__":
app = TableApp()
app.run()

View file

@ -0,0 +1,45 @@
from textual.app import App, ComposeResult
from textual.binding import Binding
from textual_fastdatatable import ArrowBackend, DataTable
ROWS = [
("lane", "swimmer", "country", "time"),
(5, "Chad le Clos", "South Africa", 51.14),
(4, "Joseph Schooling", "Singapore", 50.39),
(2, "Michael Phelps", "United States", 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),
(10, "Darren Burns", "Scotland", 51.84),
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
]
class TableApp(App):
"""Snapshot app for testing removal of rows.
Removes several rows, so we can check that the display of the
DataTable updates as expected."""
BINDINGS = [
Binding("r", "remove_row", "Remove Row"),
]
def compose(self) -> ComposeResult:
backend = ArrowBackend.from_records(ROWS, has_header=True)
yield DataTable(backend=backend)
def on_mount(self) -> None:
table = self.query_one(DataTable)
table.focus()
def action_remove_row(self):
table = self.query_one(DataTable)
table.remove_row(2)
table.remove_row(4)
table.remove_row(6)
app = TableApp()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,36 @@
import csv
import io
from textual.app import App, ComposeResult
from textual_fastdatatable import ArrowBackend, DataTable
CSV = """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"""
class TableApp(App):
def compose(self) -> ComposeResult:
rows = csv.reader(io.StringIO(CSV))
labels = next(rows)
data = [row for row in rows]
backend = ArrowBackend.from_pydict(
{label: [row[i] for row in data] for i, label in enumerate(labels)}
)
table = DataTable(backend=backend)
table.focus()
table.cursor_type = "row"
table.fixed_columns = 1
table.fixed_rows = 1
yield table
if __name__ == "__main__":
app = TableApp()
app.run()

View file

@ -0,0 +1,37 @@
from textual.app import App, ComposeResult
from textual_fastdatatable import DataTable
ROWS = [
("lane", "swimmer", "country", "time"),
(5, "Chad le Clos", "South Africa", 51.14),
(4, "Joseph Schooling", "Singapore", 50.39),
(2, "Michael Phelps", "United States", 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),
(10, "Darren Burns", "Scotland", 51.84),
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
]
class TableApp(App):
def compose(self) -> ComposeResult:
yield DataTable()
def on_mount(self) -> None:
table = self.query_one(DataTable)
table.fixed_rows = 1
table.fixed_columns = 1
table.focus()
rows = iter(ROWS)
column_labels = next(rows)
for column in column_labels:
table.add_column(column, key=column)
for index, row in enumerate(rows):
table.add_row(*row, label=str(index))
app = TableApp()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,40 @@
from textual.app import App, ComposeResult
from textual.binding import Binding
from textual_fastdatatable import ArrowBackend, DataTable
# Shuffled around a bit to exercise sorting.
ROWS = [
("lane", "swimmer", "country", "time"),
(5, "Chad le Clos", "South Africa", 51.14),
(4, "Joseph Schooling", "Singapore", 50.39),
(2, "Michael Phelps", "United States", 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),
(10, "Darren Burns", "Scotland", 51.84),
(1, "Aleksandr Sadovnikov", "Russia", 51.84),
]
class TableApp(App):
BINDINGS = [
Binding("s", "sort", "Sort"),
]
def compose(self) -> ComposeResult:
backend = ArrowBackend.from_records(ROWS, has_header=True)
yield DataTable(backend=backend)
def on_mount(self) -> None:
table = self.query_one(DataTable)
table.focus()
def action_sort(self):
table = self.query_one(DataTable)
table.sort([("time", "ascending"), ("lane", "ascending")])
app = TableApp()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,65 @@
from __future__ import annotations
from textual.app import App, ComposeResult
from textual.widgets import Label
from textual_fastdatatable import ArrowBackend, DataTable
from typing_extensions import Literal
data = [
"Severance",
"Foundation",
"Dark",
]
def make_datatable(
foreground_priority: Literal["css", "renderable"],
background_priority: Literal["css", "renderable"],
) -> DataTable:
backend = ArrowBackend.from_pydict(
{"Movies": [f"[red on blue]{row}" for row in data]}
)
table = DataTable(
backend=backend,
cursor_foreground_priority=foreground_priority,
cursor_background_priority=background_priority,
)
table.zebra_stripes = True
return table
class DataTableCursorStyles(App):
"""Regression test snapshot app which ensures that styles
are layered on top of each other correctly in the DataTable.
In this example, the colour of the text in the cells under
the cursor should not be red, because the CSS should be applied
on top."""
CSS = """
DataTable {margin-bottom: 1;}
DataTable > .datatable--cursor {
color: $secondary;
background: $success;
text-style: bold italic;
}
"""
def compose(self) -> ComposeResult:
priorities: list[
tuple[Literal["css", "renderable"], Literal["css", "renderable"]]
] = [
("css", "css"),
("css", "renderable"),
("renderable", "renderable"),
("renderable", "css"),
]
for foreground, background in priorities:
yield Label(f"Foreground is {foreground!r}, background is {background!r}:")
table = make_datatable(foreground, background)
yield table
app = DataTableCursorStyles()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,58 @@
from pathlib import Path
from textual.app import App, ComposeResult
from textual_fastdatatable import ArrowBackend, DataTable
CSS_PATH = (Path(__file__) / "../datatable_hot_reloading.tcss").resolve()
# Write some CSS to the file before the app loads.
# Then, the test will clear all the CSS to see if the
# hot reloading applies the changes correctly.
CSS_PATH.write_text(
"""\
DataTable > .datatable--cursor {
background: purple;
}
DataTable > .datatable--fixed {
background: red;
}
DataTable > .datatable--fixed-cursor {
background: blue;
}
DataTable > .datatable--header {
background: yellow;
}
DataTable > .datatable--odd-row {
background: pink;
}
DataTable > .datatable--even-row {
background: brown;
}
"""
)
class DataTableHotReloadingApp(App[None]):
CSS_PATH = CSS_PATH
def compose(self) -> ComposeResult:
data = {
# orig test set A width=10, we fake it with spaces
"A ": ["one", "three", "five"],
"B": ["two", "four", "six"],
}
backend = ArrowBackend.from_pydict(data)
yield DataTable(backend, zebra_stripes=True, cursor_type="row", fixed_columns=1)
def on_mount(self) -> None:
self.query_one(DataTable)
if __name__ == "__main__":
app = DataTableHotReloadingApp()
app.run()

View file

@ -0,0 +1 @@
/* This file is purposefully empty. */

View file

@ -0,0 +1,12 @@
from textual.app import App, ComposeResult
from textual_fastdatatable import DataTable
class TableApp(App):
def compose(self) -> ComposeResult:
yield DataTable()
app = TableApp()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,17 @@
from textual.app import App, ComposeResult
from textual_fastdatatable import DataTable
class TableApp(App):
def compose(self) -> ComposeResult:
yield DataTable()
def on_mount(self) -> None:
table = self.query_one(DataTable)
table.add_column("Foo")
table.add_rows([("1",), ("2",)])
app = TableApp()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,16 @@
from pathlib import Path
from textual.app import App, ComposeResult
from textual_fastdatatable import DataTable
class TableApp(App):
def compose(self) -> ComposeResult:
yield DataTable(
data=Path(__file__).parent.parent.parent / "data" / "lap_times_100.parquet"
)
app = TableApp()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,20 @@
from textual.app import App, ComposeResult
from textual_fastdatatable import DataTable
DATA = {
"Foo": list(range(50)),
"Bar": ["0123456789"] * 50,
"Baz": ["IJKLMNOPQRSTUVWXYZ"] * 50,
}
class TableApp(App):
def compose(self) -> ComposeResult:
yield DataTable(
data=DATA, column_labels=["[red]Not Foo[/red]", "Zig", "[reverse]Zag[/]"]
)
app = TableApp()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,25 @@
from textual.app import App, ComposeResult
from textual_fastdatatable 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(data=ROWS)
app = TableApp()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,12 @@
from textual.app import App, ComposeResult
from textual_fastdatatable import DataTable
class TableApp(App):
def compose(self) -> ComposeResult:
yield DataTable(column_labels=["foo [red]foo[/red]", "bar"])
app = TableApp()
if __name__ == "__main__":
app.run()

View file

@ -0,0 +1,12 @@
from textual.app import App, ComposeResult
from textual_fastdatatable import DataTable
class TableApp(App):
def compose(self) -> ComposeResult:
yield DataTable(column_labels=["foo [red]foo[/red]", "bar"])
app = TableApp()
if __name__ == "__main__":
app.run()