Textualize / textual

The lean application framework for Python. Build sophisticated user interfaces with a simple Python API. Run your apps in the terminal and a web browser.
https://textual.textualize.io/
MIT License
25.05k stars 766 forks source link

Clearing and adding data to `DataTable` without worker will cause it to sometime not display its content. #4928

Closed kwevin closed 1 day ago

kwevin commented 3 weeks ago

Have you checked closed issues? https://github.com/Textualize/textual/issues?q=is%3Aissue+is%3Aclosed

Please give a brief but clear explanation of the issue. If you can, include a complete working example that demonstrates the bug. Check it can run without modifications.

It will be helpful if you run the following command and paste the results:

textual diagnose

Textual Diagnostics

Versions

Name Value
Textual 0.76.0
Rich 13.7.1

Python

Name Value
Version 3.11.1
Implementation CPython
Compiler MSC v.1934 64 bit (AMD64)
Executable C:\Users\Kevin\AppData\Local\pypoetry\Cache\virtualenvs\listentui-qruDwX8r-py3.11\Scripts\python.exe

Operating System

Name Value
System Windows
Release 10
Version 10.0.22621

Terminal

Name Value
Terminal Application Windows Terminal
TERM Not set
COLORTERM Not set
FORCE_COLOR Not set
NO_COLOR Not set

Rich Console options

Name Value
size width=125, height=31
legacy_windows False
min_width 1
max_width 125
is_terminal True
encoding utf-8
max_height 31
justify None
overflow None
no_wrap False
highlight None
markup None
height None

https://github.com/user-attachments/assets/1aecef14-a634-4f5d-a9cc-f310ea40a41e

mre is large because it contains some data, so it is in a gist https://gist.github.com/kwevin/18a543e54ff7336bb802d711481b1d72

github-actions[bot] commented 3 weeks ago

We found the following entries in the FAQ which you may find helpful:

Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.

This is an automated reply, generated by FAQtory

willmcgugan commented 3 weeks ago

Could you fix the formatting on the gist?

TomJGooding commented 3 weeks ago

It looks like the problem is the height=None on this line:

self.table.add_row(*rows, height=None)

This should "auto-detect the optimal height" but seems something isn't quite right.

Here's a more minimal example:

from textual.app import App, ComposeResult
from textual.widgets import DataTable, Input

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 ExampleApp(App):
    CSS = """
    DataTable {
        border: solid red;
    }
    """

    def compose(self) -> ComposeResult:
        yield Input()
        yield DataTable()

    def on_mount(self) -> None:
        table = self.query_one(DataTable)
        table.add_columns(*ROWS[0])
        self.populate_table()

    def on_input_changed(self) -> None:
        self.populate_table()

    def populate_table(self) -> None:
        table = self.query_one(DataTable)
        table.clear()
        for row in ROWS[1:]:
            table.add_row(
                *row,
                height=None,
            )
        self.log.debug("Table populated")

if __name__ == "__main__":
    app = ExampleApp()
    app.run()
willmcgugan commented 3 weeks ago

Yeah, I concur with @TomJGooding Looks like the auto height is breaking something.

TomJGooding commented 3 weeks ago

I confess I'm struggling to follow the code, but posting my findings in case it helps someone else fix this bug.

I think the problem is that when the virtual size is calculated, the self._y_offsets is an empty list:

https://github.com/Textualize/textual/blob/db3fda928ba3f4807f01ae24ecee260ce6206b1b/src/textual/widgets/_data_table.py#L1459-L1462

https://github.com/Textualize/textual/blob/db3fda928ba3f4807f01ae24ecee260ce6206b1b/src/textual/widgets/_data_table.py#L823-L826

Presumably because the row.height is still zero rather than the calculated height here?

https://github.com/Textualize/textual/blob/db3fda928ba3f4807f01ae24ecee260ce6206b1b/src/textual/widgets/_data_table.py#L818-L820

mitaa commented 1 week ago

I have quite a similar issue which might have the same cause. Not sure if I should open a new issue for that.

When the populate_table method is run for the first time (on startup) everything looks correct. When it's run for the second time (on_data_table_cell_selected) the auto-width of the columns is incorrect and column widths seem to correspond to the header.

from textual.app import App, ComposeResult
from textual.widgets import DataTable

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

class ExampleApp(App):
    def compose(self) -> ComposeResult:
        yield DataTable()

    def on_mount(self) -> None:
        self.populate_table()

    def on_data_table_cell_selected(self) -> None:
        self.populate_table()

    def populate_table(self) -> None:
        table = self.query_one(DataTable)
        table.clear(columns=True)
        table.add_columns(*ROWS[0])
        table.add_rows(ROWS[1:])
        self.log.debug("Table populated")

if __name__ == "__main__":
    app = ExampleApp()
    app.run()
TomJGooding commented 1 week ago

@mitaa This looks like the same issue as #4470?

mitaa commented 1 week ago

@TomJGooding You're right! Sorry the confusion.

github-actions[bot] commented 1 day ago

Don't forget to star the repository!

Follow @textualizeio for Textual updates.