Howto build compound widgets? Best Practice Question #4383
-
It's another beginner question... I build a UI for a number of database tables. Each table should be edited in a form in a TabPane inside a TabbedContent. To navigate in the tables searching the row the user is looking for and to save or delete them (an some more) I would like to offer the user a "navi-and-filter/choose-area" on the left, the form should be shown on the right. Therefore I would like to build a Navi-Widget which will be instantiated and yielded in each of my TabPanes as the "navi-areas". As I understand, it would not be a real But how to build this Navi? What I tried until now: (have a look in the code below)
for Wdg in navi_factory('nav-1'):
yield Wdg
NaviFact = navi_generator('nav-2')
for Wdg in NaviFact():
yield Wdg
navifact = NaviFactory('nav-3')
for Wdg in navifact.navi_factory():
yield Wdg What I would like to build:
The code I wrote until now:Of course the CSS is more then minimal and can be ignored. #!/usr/bin/env python3
from textual.app import App
from textual.containers import Horizontal, Vertical, VerticalScroll
from textual.widgets import Button, Input, Label, Placeholder, Static, TabbedContent, TabPane
def navi_factory(base_name):
with Vertical():
with Horizontal():
yield Label('Filter')
yield Input(id=f'filter-{base_name}')
Btn = Button('Save', id=f'save-{base_name}')
Btn.tooltip = f'Save Form {base_name}'
yield Btn
Btn = Button('Delete', id=f'delete-{base_name}')
Btn.tooltip = f'Delete Data {base_name}'
yield Btn
with VerticalScroll():
for id in range(25):
Btn = Button(
f'{id:2} Filtered from Database',
id=f'show-{id}-{base_name}')
Btn.tooltip = f'Show Data with ID={id} in Form {base_name}'
yield Btn
def navi_generator(base_name):
def navi_factory():
with Vertical():
with Horizontal():
yield Label('Filter')
yield Input(id=f'filter-{base_name}')
Btn = Button('Save', id=f'save-{base_name}')
Btn.tooltip = f'Save Form {base_name}'
yield Btn
Btn = Button('Delete', id=f'delete-{base_name}')
Btn.tooltip = f'Delete Data {base_name}'
yield Btn
with VerticalScroll():
for id in range(25):
Btn = Button(
f'{id:2} Filtered from Database',
id=f'show-{id}-{base_name}')
Btn.tooltip = f'Show Data with ID={id} in Form {base_name}'
yield Btn
return navi_factory
class NaviFactory():
def __init__(self, base_name):
self.base_name = base_name
def navi_factory(self):
with Vertical():
with Horizontal():
yield Label('Filter')
yield Input(id=f'filter-{self.base_name}')
Btn = Button('Save', id=f'save-{self.base_name}')
Btn.tooltip = f'Save Form {self.base_name}'
yield Btn
Btn = Button('Delete', id=f'delete-{self.base_name}')
Btn.tooltip = f'Delete Data {self.base_name}'
yield Btn
with VerticalScroll():
for id in range(25):
Btn = Button(
f'{id:2} Filtered from Database',
id=f'show-{id}-{self.base_name}')
Btn.tooltip = f'Show Data with ID={id} in Form {self.base_name}'
yield Btn
class Full(App):
CSS = """
Placeholder {
width: 50%;
}
Input {
width: 10;
}
VerticalScroll {
height: 80%;
}
"""
def compose(self):
with TabbedContent():
with TabPane('Tab 1'):
with Horizontal():
for Wdg in navi_factory('nav-1'):
yield Wdg
yield Placeholder('Form Tab 1')
with TabPane('Tab 2'):
with Horizontal():
NaviFact = navi_generator('nav-2')
for Wdg in NaviFact():
yield Wdg
yield Placeholder('Form Tab 2')
with TabPane('Tab 3'):
with Horizontal():
navifact = NaviFactory('nav-3')
for Wdg in navifact.navi_factory():
yield Wdg
yield Placeholder('Form Tab 3')
# with TabPane('Tab 4'):
# with Horizontal():
# yield Navi('nav-3')
# yield Placeholder('Form Tab 3')
if __name__ == '__main__':
app = Full()
app.run() I would be glad to get some hints. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 6 replies
-
Could you explain a bit more what part you are struggling with? There's an example of a compound widget in the docs which might be helpful (though I do think perhaps this needs expanding) |
Beta Was this translation helpful? Give feedback.
-
Well, in this case it was TomjGoodings hint to the docu, which solves the problem. For the sake of completeness, have a look to the solution below. One question remains: why isn't it necessary to pass any parameter in the new build Widget's super().__init__() Isn't it possible to pass the new build For the sake of completeness, have a look to the solution below: class Navi(Widget):
DEFAULT_CSS = """
Navi {
width: 30%;
}
"""
def __init__(self, base_name):
self.base_name = base_name
super().__init__()
def compose(self):
# the rest does what the navi_factory did; nothing news from here down
with Vertical():
with Horizontal():
yield Label('Filter')
yield Input(id=f'filter-{self.base_name}')
Btn = Button('Save', id=f'save-{self.base_name}')
Btn.tooltip = f'Save Form {self.base_name}'
yield Btn
Btn = Button('Delete', id=f'delete-{self.base_name}')
Btn.tooltip = f'Delete Data {self.base_name}'
yield Btn
with VerticalScroll():
for id in range(25):
Btn = Button(
f'{id:2} Filtered from Database',
id=f'show-{id}-{self.base_name}')
Btn.tooltip = f'Show Data with ID={id} in Form {self.base_name}'
yield Btn |
Beta Was this translation helpful? Give feedback.
Could you explain a bit more what part you are struggling with? There's an example of a compound widget in the docs which might be helpful (though I do think perhaps this needs expanding)