Skip to content

Commit

Permalink
Add Select.from_values class method for initializing with iterator (#…
Browse files Browse the repository at this point in the history
…3743)

* prototype to use class method to initialize Select object using an iterator

* add docstring and optional arguments to Select.from_values()

* add test widget for Select.from_values() and update documentation to include an example for its use

* add snapshot tests to Select.from_values() class method and update snapshots to include results

* address review comments

---------

Co-authored-by: Rodrigo Girão Serrão <[email protected]>
  • Loading branch information
azinneck0485 and rodrigogiraoserrao authored Dec 5, 2023
1 parent d04c838 commit 87ef1bb
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- Added support for Ctrl+Fn and Ctrl+Shift+Fn keys in urxvt https://github.com/Textualize/textual/pull/3737
- Friendly error messages when trying to mount non-widgets https://github.com/Textualize/textual/pull/3780
- Added `Select.from_values` class method that can be used to initialize a Select control with an iterator of values https://github.com/Textualize/textual/pull/3743

### Fixed

Expand Down
26 changes: 26 additions & 0 deletions docs/examples/widgets/select_from_values_widget.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from textual import on
from textual.app import App, ComposeResult
from textual.widgets import Header, Select

LINES = """I must not fear.
Fear is the mind-killer.
Fear is the little-death that brings total obliteration.
I will face my fear.
I will permit it to pass over me and through me.""".splitlines()


class SelectApp(App):
CSS_PATH = "select.tcss"

def compose(self) -> ComposeResult:
yield Header()
yield Select.from_values(LINES)

@on(Select.Changed)
def select_changed(self, event: Select.Changed) -> None:
self.title = str(event.value)


if __name__ == "__main__":
app = SelectApp()
app.run()
30 changes: 29 additions & 1 deletion docs/widgets/select.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ my_select: Select[int] = Select(options)

If you aren't familiar with typing or don't want to worry about it right now, feel free to ignore it.

## Example
## Examples

### Basic Example

The following example presents a `Select` with a number of options.

Expand All @@ -57,6 +59,32 @@ The following example presents a `Select` with a number of options.
--8<-- "docs/examples/widgets/select.tcss"
```

### Example using Class Method

The following example presents a `Select` created using the `from_values` class method.

=== "Output"

```{.textual path="docs/examples/widgets/select_from_values_widget.py"}
```

=== "Output (expanded)"

```{.textual path="docs/examples/widgets/select_from_values_widget.py" press="tab,enter,down,down"}
```


=== "select_from_values_widget.py"

```python
--8<-- "docs/examples/widgets/select_from_values_widget.py"
```

=== "select.tcss"

```sass
--8<-- "docs/examples/widgets/select.tcss"
```

## Blank state

Expand Down
52 changes: 50 additions & 2 deletions src/textual/widgets/_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,15 +284,15 @@ def __init__(
Args:
options: Options to select from. If no options are provided then
`allow_blank` must be set to `True`.
prompt: Text to show in the control when no option is select.
prompt: Text to show in the control when no option is selected.
allow_blank: Enables or disables the ability to have the widget in a state
with no selection made, in which case its value is set to the constant
[`Select.BLANK`][textual.widgets.Select.BLANK].
value: Initial value selected. Should be one of the values in `options`.
If no initial value is set and `allow_blank` is `False`, the widget
will auto-select the first available option.
name: The name of the select control.
id: The ID of the control the DOM.
id: The ID of the control in the DOM.
classes: The CSS classes of the control.
disabled: Whether the control is disabled or not.
Expand All @@ -305,6 +305,54 @@ def __init__(
self._value = value
self._setup_variables_for_options(options)

@classmethod
def from_values(
cls,
values: Iterable[SelectType],
*,
prompt: str = "Select",
allow_blank: bool = True,
value: SelectType | NoSelection = BLANK,
name: str | None = None,
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
) -> Select[SelectType]:
"""Initialize the Select control with values specified by an arbitrary iterable
The options shown in the control are computed by calling the built-in `str`
on each value.
Args:
values: Values used to generate options to select from.
prompt: Text to show in the control when no option is selected.
allow_blank: Enables or disables the ability to have the widget in a state
with no selection made, in which case its value is set to the constant
[`Select.BLANK`][textual.widgets.Select.BLANK].
value: Initial value selected. Should be one of the values in `values`.
If no initial value is set and `allow_blank` is `False`, the widget
will auto-select the first available value.
name: The name of the select control.
id: The ID of the control in the DOM.
classes: The CSS classes of the control.
disabled: Whether the control is disabled or not.
Returns:
A new Select widget with the provided values as options.
"""
options_iterator = [(str(value), value) for value in values]

return cls(
options_iterator,
prompt=prompt,
allow_blank=allow_blank,
value=value,
name=name,
id=id,
classes=classes,
disabled=disabled,
)

def _setup_variables_for_options(
self,
options: Iterable[tuple[RenderableType, SelectType]],
Expand Down
Loading

0 comments on commit 87ef1bb

Please sign in to comment.