textual: DataTable render_line redraw bug when using `update_cell()` method
When updating cells in a data table (via the update_cell() method) that would require the column to expand the row is redrawn incorrectly. As you can see in the code the update_width=True parameter is passed to the update_cell() method.
As you can see in VIDEO 1 below, if you insert spaces, the column width actually updates but the remaining characters are not drawn correctly. Oddly enough, when you hover over the cell or when you backspace once the row redraws correctly.
As you can see in VIDEO 2 below, if you add alphanumeric characters, the row draws correctly except for the last character is missing once the text reaches the end of the original column width.
I didn’t have time to fully investigate the issue but the problem seems to stem from calls to the console.render_lines() method and specifically the Styled() renderable that includes Padding(cell, (0, self.cell_padding)). If you change the second argument self.cell_padding of the supplied tuple to 0, things seem to work but then any padding is not accounted for. I ran out of time to dig any deeper, but hopefully this helps.
I ran into this problem inside of a larger project, so I am including a minimal example here that has the same issue and is what was used when creating the videos.
Let me know if you have other questions. Thanks!
# Textual Diagnostics
## Versions
| Name | Value |
|---------|--------|
| Textual | 0.40.0 |
| Rich | 13.5.2 |
## Python
| Name | Value |
|----------------|------------------------------------------------------------------|
| Version | 3.11.6 |
| Implementation | CPython |
| Compiler | Clang 15.0.0 (clang-1500.0.40.1) |
| Executable | /Users/jbd/Dropbox/DEV/projects/textual-play/venv/bin/python3.11 |
## Operating System
| Name | Value |
|---------|-------------------------------------------------------------------------------------------------------|
| System | Darwin |
| Release | 23.0.0 |
| Version | Darwin Kernel Version 23.0.0: Fri Sep 15 14:41:43 PDT 2023; root:xnu-10002.1.13~1/RELEASE_ARM64_T6000 |
## Terminal
| Name | Value |
|----------------------|--------------------|
| Terminal Application | iTerm.app (3.4.21) |
| TERM | xterm-256color |
| COLORTERM | truecolor |
| FORCE_COLOR | *Not set* |
| NO_COLOR | *Not set* |
## Rich Console options
| Name | Value |
|----------------|---------------------|
| size | width=81, height=25 |
| legacy_windows | False |
| min_width | 1 |
| max_width | 81 |
| is_terminal | False |
| encoding | utf-8 |
| max_height | 25 |
| justify | None |
| overflow | None |
| no_wrap | False |
| highlight | None |
| markup | None |
| height | None |
Videos
VIDEO 1 https://github.com/Textualize/textual/assets/44387852/7d521232-4a5d-4969-8a76-00c37f7e78d7
VIDEO 2 https://github.com/Textualize/textual/assets/44387852/81846302-66ff-46ea-b1d9-79e3dbf0e1aa
Minimal Example Code
from textual import on
from textual.app import App, ComposeResult
from textual.events import Ready
from textual.widgets import DataTable, Input
ROWS = [
("DSC-BLAH-1-2023-10.jpg", "DSC-BLAH-1-2023-10.jpg"),
("DSC-BLAH-2-2023-10.jpg", "DSC-BLAH-2-2023-10.jpg"),
("DSC-BLAH-3-2023-10.jpg", "DSC-BLAH-3-2023-10.jpg"),
("DSC-BLAH-4-2023-10.jpg", "DSC-BLAH-4-2023-10.jpg"),
("DSC-BLAH-5-2023-10.jpg", "DSC-BLAH-5-2023-10.jpg"),
]
class TableApp(App):
def compose(self) -> ComposeResult:
yield DataTable(cursor_type="row")
yield Input(placeholder="Find", id="find")
yield Input(placeholder="Replace", id="replace")
def on_mount(self) -> None:
table = self.query_one(DataTable)
table.add_column("Current Name", key="current_name")
table.add_column("New Name", key="new_name")
table.add_rows(ROWS)
@on(Ready)
def focus_input(self) -> None:
self.query_one("#find", Input).focus()
@on(Input.Changed, "#find")
@on(Input.Changed, "#replace")
def update_data_table(self) -> None:
dt = self.query_one(DataTable)
find_str = self.query_one("#find", Input).value
replace_str = self.query_one("#replace", Input).value
for row_key in dt.rows:
name, _ = dt.get_row(row_key)
if name.find(find_str) >= 0:
new_name = name.replace(find_str, replace_str)
else:
new_name = name
dt.update_cell(row_key, "new_name", new_name, update_width=True)
app = TableApp()
if __name__ == "__main__":
app.run()
About this issue
- Original URL
- State: closed
- Created 9 months ago
- Comments: 16 (12 by maintainers)
Going to re-open this. It should work out-of-the-box
Looking into this now. This is a fairly recent regression - introduced in commit
dfba992722, from this PR: https://github.com/Textualize/textual/pull/3213Glad I might have helped, but I was a bit surprised you closed this issue!
I think this needs further investigation, as updating the DataTable from an Input feels like it should just work.
But I’m not sure of the proper fix and I will leave it up to the Textual maintainers.
If you inspect the
linesreturn in the_render_cell()method, you see all kinds of weirdness (extra spaces, cropped letters, too many spaces) getting returned.So, it seems, by simply adding
+1to theget_render_width()method from theColumnclass, the missing characters show correctly. Naturally, this breaks a handful of tests, so I’ll need to look at those closer. You can the characters showing in my example and your example below.I did notice another oddity that shows up in the current version of the code and also in my
+1version. As you can see in the GIF below, when you press the backspace key, the column width actually grows by 1 (instead of shrinking). It correctly adjusts (temporarily) when the mouse hovers over the row/column.I re-uploaded the videos as MP4 so they should work now. Same error occurs in your example.
VIDEO 1 https://github.com/Textualize/textual/assets/44387852/a32a1146-76f4-4840-baa5-aa318aa97558
VIDEO 2 https://github.com/Textualize/textual/assets/44387852/eec26691-3494-413a-858a-bf287066eaa6
YOUR EXAMPLE