Dynamically add tab in Textual

417 views Asked by At

In Textual I'm trying to dynamically add a tab to an application. Here's the full code:

from textual import on
from textual.app import App, ComposeResult
from textual.widgets import DataTable, Select
from textual.widgets import Footer
from textual.widgets import TabbedContent, TabPane, Static

ROWS1 = [
    ("X", "Y"),
    ("A", "B"),
    ("C", "D")
]

ROWS2 = [
    ("X", "Y"),
    ("AA", "BB"),
    ("CC", "DD")
]

ROWS3 = [
    ("1", "2"),
    ("12", "13"),
    ("14", "15")
]


class DynamicTabApp(App):

    def compose(self) -> ComposeResult:
        # Footer to show keys
        yield Selector()
        with TabbedContent(initial="tab1"):
            with TabPane("Tab 1", id="tab1"):  # First tab
                table = DataTable(id="table1")
                table.add_columns(*ROWS1[0])
                table.add_rows(ROWS1[1:])
                yield table

            with TabPane("Tab 2", id="tab2"):
                table = DataTable(id="table2")
                table.add_columns(*ROWS2[0])
                table.add_rows(ROWS2[1:])
                yield table
        yield Footer()

    def action_show_tab(self, tab: str):
        """Switch to a new tab."""
        self.get_child_by_type(TabbedContent).active = tab


class SelectTab(Static):
    DEFAULT_CSS = """
    Screen {
    align: center top;
}

Select {
    width: 60;
    margin: 2;
}
"""

    def compose(self) -> ComposeResult:
        yield Select([('Tab 1', 'tab1'), ('Tab 2', 'tab2'), ('Tab 3', 'tab3')])

    @on(Select.Changed)
    async def select_changed(self, event: Select.Changed):
        if event.value == 'tab3':
            tab_pane = TabPane("Tab 3", id="tab3")
            table = DataTable(id="table3")
            table.add_columns(*ROWS3[0])
            table.add_rows(ROWS3[1:])
            await tab_pane.mount(table)
            tabbed_content = self.app.query_one(TabbedContent)
            await tabbed_content.mount(tab_pane)
        else:
            self.app.get_child_by_type(TabbedContent).active = event.value


class Selector(Static):

    def compose(self) -> ComposeResult:
        """Create child widgets of a stopwatch."""
        yield SelectTab()


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

When I start the application it looks promising: enter image description here

In the select box there is Tab 3 which is not a tab. If gets selected I'm trying to add it dynamically by calling mount on the tabbed_content:

            tab_pane = TabPane("Tab 3", id="tab3")
            table = DataTable(id="table3")
            table.add_columns(*ROWS3[0])
            table.add_rows(ROWS3[1:])
            await tab_pane.mount(table)
            tabbed_content = self.app.query_one(TabbedContent)
            await tabbed_content.mount(tab_pane)

After the selecting tab 3 I get is this:

enter image description here

Which does not add another tab but appends the contents to the bottom of the screen.

1

There are 1 answers

0
Darghal M. On

You are mounting the new tab into an already created tab "tab1" . You need to use the method add_pane() of the class TabbedContent in order to add a new tab . and you can switch to it using the attribute active.

pane = self.query_one("#tab3")
pane = tabbedcontent.add_pane(pane)
tabbedcontent.active = pane.id