-
Notifications
You must be signed in to change notification settings - Fork 815
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
A way to mount a composed widget, but so that its children are mounted separately (ft. Grid) #3483
Comments
It is not at all clear what you want to do here. I'm going to guess you want a grid with title in the first column and input in the next? To do that you would need separate widgets for label / input, not a compound widget. Then your compose would look like the following: yield Label("input 1")
yield Input(id="input1")
yield Label("input 1")
yield Input(id="input2") |
Of course I know I can do it without the compund widget and it will work. That's why this issue was created - because it would be useful to be able to do this with the compund widget also. The point is that I want to have it in a compound widget - the mentioned example is simple to be easy to read, but in a real application I don't want to have different logic in 1000 places, I want to have it in a common class. I mean creating complex controls (custom widgets) like def compose(self) -> ComposeResult:
yield Input()
yield Select() now imagine that I need to add the |
@mzebrak In order to assist you, we need to know what you are trying to achieve. Not what you tried that didn't work. |
As I clearly indicated - I want to create a custom widget that consists of |
Something akin to this? from textual import on
from textual.app import App, ComposeResult
from textual.containers import Grid
from textual.widgets import Label, Input, Log
class TitledInput(Grid):
DEFAULT_CSS = """
TitledInput {
grid-size: 2;
height: auto;
}
Label {
background: green;
}
"""
def __init__(self, title: str) -> None:
super().__init__()
self.title = title
def compose(self) -> ComposeResult:
yield Label(self.title)
yield Input()
class KindaGridApp(App[None]):
def compose(self) -> ComposeResult:
yield Log()
for n in range(4):
yield TitledInput(f"Input {n}")
@on(Input.Changed)
def update_logs(self, event: Input.Changed) -> None:
self.query_one(Log).write_line(f"{event.control.parent.title} changed: {event.value}")
if __name__ == "__main__":
KindaGridApp().run() |
I think I may still be struggling to comprehend the issue being described here, because as I look at the above I sense the broad approach I suggested here would be applicable. |
|
I wonder if you are struggling with composing nested widgets? This code implements a form. |
This is exactly what is this issue about - the possibility of 'yielding' so that this widget was taken into account by the textual (it was mounted), and instead of it itself - yield its children I'm not saying that it has to be done with
Unfortunately, I still see that maybe you don't understand me and I don't know why you are trying to abandon the use of compound widget when even a similar example is covered in the documentation. In the example you link to, a completely different approach is used, the elements are stripped down into prime factors . This is what I want to avoid. Instead, I'm asking about a way/new feature to do this using a compound widget. Why compound widget? Why not break it down into prime factors as you suggest - because, as I have already mentioned, since these are elements related to each other and only collected in a group in the compound widget class - I want their logic to be included here as well, and it does not have to be externalized. It's just that since Textual allows feature compound widgets - I would like to use it, I don't want to decompose it... for me it is one whole - one of my own widget/component. I also want to reuse it in many places, I don't need several elements for this like separate So where is the problem? So everything boils down to a simple question - how to control width/height of given elements using compound widget in Grid? Currently this is not possible, but maybe it should be. Modified example of link, so we can have everything in a grid. I hope I don't have to explain why grid... I want to easily manage this layout when adding, for example, another element in a new column to the middle row, it would get complicated with from textual.app import App, ComposeResult
from textual.containers import Grid
from textual.widget import Widget
from textual.widgets import Input, Label, Select, Static
class InputWithLabel(Widget):
"""An input with a label."""
DEFAULT_CSS = """
InputWithLabel {
layout: horizontal;
height: auto;
}
InputWithLabel Label {
padding: 1;
text-align: right;
background: red;
}
InputWithLabel Input {
width: 1fr;
}
"""
def __init__(self, input_label: str, with_select: bool = False) -> None:
self.input_label = input_label
self.with_select = with_select
super().__init__()
def compose(self) -> ComposeResult:
yield Label(self.input_label)
yield Input()
if self.with_select:
yield Select([("a", "A"), ("b", "B"), ("c", "C")])
class CompoundApp(App):
CSS = """
InputWithLabel {
width: 80%;
margin: 1;
}
Grid {
grid-size: 3;
grid-columns: 12 1fr 1fr;
grid-rows: 4;
height: auto !important;
}
"""
def compose(self) -> ComposeResult:
with Grid():
yield from InputWithLabel("First Name").compose()
yield Static() # empty cell
yield from InputWithLabel("Last Name", with_select=True).compose()
yield from InputWithLabel("Email").compose()
yield Static() # empty cell
if __name__ == "__main__":
app = CompoundApp()
app.run() |
Given the above example, this takes me back to my original suggestion. If I were to create the layout you were going for there, I'd do it like this: from textual.app import App, ComposeResult
from textual.containers import Grid
from textual.widgets import Label, Input, Select
class StandardRow(Grid):
"""A standard row in the display.
Other compound widgets inherit from this.
"""
DEFAULT_CSS = """
StandardRow {
grid-size: 3;
grid-columns: 12 1fr 1fr;
height: auto;
margin-bottom: 1;
}
"""
class InputWithLabel(StandardRow):
"""A labeled input row with optional select."""
def __init__(self, input_label: str, with_select: bool = False) -> None:
self.input_label = input_label
self.with_select = with_select
super().__init__()
def compose(self) -> ComposeResult:
yield Label(self.input_label)
yield Input()
if self.with_select:
yield Select([("a", "A"), ("b", "B"), ("c", "C")])
class Issue3483Example(App[None]):
def compose(self) -> ComposeResult:
yield InputWithLabel("First Name")
yield InputWithLabel("Last Name", with_select=True)
yield InputWithLabel("Email")
if __name__ == "__main__":
Issue3483Example().run() This retains the Here is the result of your code and mine side-by-side: |
See Textualize/textual#3483 (comment) for the specific context.
@mzebrak I am going to close this issue. We've tried very hard to help you, but we still don't know what you are asking for that Textual doesn't already do by default. If you can think of an alternative way to express your problem, then feel free to open another issue. But please don't continue to use constructs that we have already explained won't work, like yield from compose. And please when you say you think something doesn't work, tells us why you think that. |
Don't forget to star the repository! Follow @textualizeio for Textual updates. |
@davep Thanks for this example, it seems quite spot on. On However I have the impression that this is not a perfect solution, because if there is an external grid container that covers it all, it is more difficult to make any unforeseen modifications to the layout. However, this is probably the best solution in a given situation, if we reject the idea of something like |
Glad to hear it helped. Having a base class with the common styling would be one common idiom and one I use as much as possible as it makes it very easy to maintain code.
It's always best to assume that all examples given are intended for the latest stable release of Textual as of the time of writing; even more the case when an issue is raised where the author has not included the output of |
AFAIK now there is no way to deal with such a situation, and I noticed that it would be a very useful possibility. For example, in this simple example - when we want to have a some common part of the code, so
TitledInput
is created, but it can't be used in a grid correctly.And the
yield from <>.compose()
is a problem because the parent widget is not mounted andyield <>
is also a problem as it is mounted as a whole and not separate parts.The text was updated successfully, but these errors were encountered: