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:
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:
Which does not add another tab but appends the contents to the bottom of the screen.
You are mounting the new tab into an already created tab "tab1" . You need to use the method
add_pane()
of the classTabbedContent
in order to add a new tab . and you can switch to it using the attributeactive
.