Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to make DataTable columns expand to fill available space #5455

Closed
nvictor opened this issue Jan 4, 2025 · 6 comments
Closed

Ability to make DataTable columns expand to fill available space #5455

nvictor opened this issue Jan 4, 2025 · 6 comments

Comments

@nvictor
Copy link

nvictor commented Jan 4, 2025

Hello,

There could be a way to do this already, but I want columns in my DataTable to expand to fill the available space and not their contents. Here's a minimal code that shows how I am setting my fixed width values. I am trying to determine the right width.

Thanks a lot

from textual.app import App
from textual.widgets import Header, Footer, DataTable
from textual.containers import Container
from textual.binding import Binding


class DataApp(App):
    CSS = """
    #container {
        border: heavy $accent;
        margin: 2 4;
        height: 100%;
        width: 100%;
        border-title-align: center;
    }

    DataTable {
        height: 100%;
        overflow: auto;
    }
    """
    BINDINGS = [
        Binding("q", "quit", "Quit"),
    ]

    def __init__(self):
        super().__init__()
        self.data_table = None

    def compose(self):
        yield Header(show_clock=True)
        with Container(id="container"):
            yield DataTable(id="table")
        yield Footer()

    async def on_mount(self):
        self.data_table = self.query_one(DataTable)

        # TODO(victor): Make width expand to fill the available space
        self.data_table.add_column("Column 1", width=20)
        self.data_table.add_column("Column 2", width=20)

        self.run_worker(self.load_data(), exclusive=True, thread=True)

    async def load_data(self):
        self.call_from_thread(self.data_table.set_loading, True)
        self.call_from_thread(self.data_table.clear)

        for i in range(10):
            self.call_from_thread(
                self.data_table.add_row,
                f"Cell {(i*2)+1}",
                f"Cell {(i*2)+2}",
                label=str(i),
            )

        self.query_one("#container").border_subtitle = f"Page 1"
        self.call_from_thread(self.data_table.set_loading, False)


if __name__ == "__main__":
    DataApp().run()
Copy link

github-actions bot commented Jan 4, 2025

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

@nvictor
Copy link
Author

nvictor commented Jan 4, 2025

Oh more info based on the automated response above. I want to constraint my columns to fit in the container without a scrollbar.

@TomJGooding
Copy link
Contributor

TomJGooding commented Jan 5, 2025

This issue of how to expand DataTable columns to fill available space seems to be a frequently asked question.

Unfortunately, currently the built-in column options are limited to either the optimal width or a fixed width. Hopefully proportional column widths will be supported in future versions.

But in the meantime, here's my attempt at creating a 'stretchy' table, where all columns adapt to fill the available width. If you want only certain columns to be 'stretchy', hopefully this provides a jumping-off point.

First, we need to determine the actual size of the table widget. Obviously this will change when the terminal window is resized, so we will need an event handler for the Resize event.

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


class StetchyDataTable(DataTable):
    def on_resize(self, event: events.Resize) -> None:
        self.notify(str(event.size))


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

    def on_mount(self) -> None:
        table = self.query_one(StetchyDataTable)
        table.add_columns(*[f"C{col}" for col in range(1, 4)])
        for row in range(1, 6):
            table.add_row(*[f"R{row}C{col}" for col in range(1, 4)])


if __name__ == "__main__":
    app = ExampleApp()
    app.run()

This StretchyDataTable widget simply extends the DataTable to notify when it has been resized. When you run this example app, notice that an initial Resize event is sent before even before you resize the terminal window.

To calculate the 'stretchy' columns widths, divide the table width by the number of columns.

column_width = event.size.width // len(self.columns)

Then update the column widths and refresh the widget.

class StretchyDataTable(DataTable):
    def on_resize(self, event: events.Resize) -> None:
        column_width = event.size.width // len(self.columns)
        for column in self.columns.values():
            column.auto_width = False
            column.width = column_width
        self.refresh()

@TomJGooding
Copy link
Contributor

TomJGooding commented Jan 5, 2025

Oops, I realised that the above code doesn't account for the cell padding. The column width should instead be calculated something like this:

total_width = event.size.width
total_padding = 2 * (self.cell_padding * len(self.columns))
column_width = (total_width - total_padding) // len(self.columns)

@nvictor
Copy link
Author

nvictor commented Jan 5, 2025

Thank you so much for this solution! It helped me achieve my goal 😊

I have to mention though that if your rows have a label column you have to also remove the label column width (and padding) from total width.

Thanks for pointing me in the right direction.

@nvictor nvictor closed this as completed Jan 5, 2025
Copy link

github-actions bot commented Jan 5, 2025

Don't forget to star the repository!

Follow @textualizeio for Textual updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants