Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/magicgui/schema/_guiclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

from typing_extensions import TypeGuard

from magicgui.types import NestedValueWidgets

# fmt: off
class GuiClassProtocol(Protocol):
"""Protocol for a guiclass."""
Expand Down Expand Up @@ -243,7 +245,7 @@ def widget(self) -> ContainerWidget:

def __get__(
self, instance: object | None, owner: type
) -> ContainerWidget[BaseValueWidget] | GuiBuilder:
) -> ContainerWidget[NestedValueWidgets] | GuiBuilder:
if instance is None:
return self
wdg = build_widget(instance)
Expand Down
25 changes: 20 additions & 5 deletions src/magicgui/schema/_ui_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from typing_extensions import TypeGuard, get_args, get_origin

from magicgui.types import JsonStringFormats, Undefined, _Undefined
from magicgui.types import JsonStringFormats, NestedValueWidgets, Undefined, _Undefined

if TYPE_CHECKING:
from collections.abc import Iterable, Iterator, Mapping
Expand Down Expand Up @@ -394,7 +394,9 @@
kwargs.pop("name", None)
return dc.replace(self, **kwargs)

def create_widget(self, value: T | _Undefined = Undefined) -> BaseValueWidget[T]:
def create_widget(
self, value: T | _Undefined = Undefined
) -> BaseValueWidget[T] | NestedValueWidgets:
Comment thread
tlambert03 marked this conversation as resolved.
Outdated
"""Create a new Widget for this field."""
from magicgui.type_map import get_widget_class

Expand Down Expand Up @@ -441,7 +443,20 @@
opts["min"] = d["exclusive_minimum"] + m

value = value if value is not Undefined else self.get_default() # type: ignore
cls, kwargs = get_widget_class(value=value, annotation=self.type, options=opts)
try:
cls, kwargs = get_widget_class(
value=value, annotation=self.type, options=opts
)
except ValueError:
Comment thread
tlambert03 marked this conversation as resolved.
Outdated
try:
wdg = build_widget(self.type)
wdg.label = self.name if self.name else ""
return wdg
except TypeError as e:
raise TypeError(

Check warning on line 456 in src/magicgui/schema/_ui_field.py

View check run for this annotation

Codecov / codecov/patch

src/magicgui/schema/_ui_field.py#L450-L456

Added lines #L450 - L456 were not covered by tests
f"Could not create widget for field {self.name!r} ",
f"with value {value!r}",
) from e
return cls(**kwargs) # type: ignore


Expand Down Expand Up @@ -786,7 +801,7 @@
values: Mapping[str, Any] | None = None,
*,
container_kwargs: Mapping | None = None,
) -> ContainerWidget[BaseValueWidget]:
) -> ContainerWidget[NestedValueWidgets]:
"""Create a container widget from a sequence of UiFields.

This function is the heart of build_widget.
Expand Down Expand Up @@ -849,7 +864,7 @@


# TODO: unify this with magicgui
def build_widget(cls_or_instance: Any) -> ContainerWidget[BaseValueWidget]:
def build_widget(cls_or_instance: Any) -> ContainerWidget[NestedValueWidgets]:
"""Build a magicgui widget from a dataclass, attrs, pydantic, or function."""
values = None if isinstance(cls_or_instance, type) else _get_values(cls_or_instance)
fields = get_ui_fields(cls_or_instance)
Expand Down
10 changes: 9 additions & 1 deletion src/magicgui/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@

if TYPE_CHECKING:
from magicgui.widgets import FunctionGui
from magicgui.widgets.bases import CategoricalWidget, Widget
from magicgui.widgets.bases import (
BaseValueWidget,
CategoricalWidget,
ContainerWidget,
Widget,
)
from magicgui.widgets.protocols import WidgetProtocol


Expand All @@ -29,6 +34,9 @@ class ChoicesDict(TypedDict):
WidgetRef = Union[str, WidgetClass]
#: A :attr:`WidgetClass` (or a string representation of one) and a dict of kwargs
WidgetTuple = tuple[WidgetRef, dict[str, Any]]
#: A [`ValueWidget`][magicgui.widgets.ValueWidget] class or a
#: [`ContainerWidget`][magicgui.widgets.ContainerWidget] class for nesting those
NestedValueWidgets = Union["BaseValueWidget", "ContainerWidget[NestedValueWidgets]"]
#: An iterable that can be used as a valid argument for widget ``choices``
ChoicesIterable = Union[Iterable[tuple[str, Any]], Iterable[Any]]
#: An callback that can be used as a valid argument for widget ``choices``. It takes
Expand Down
12 changes: 7 additions & 5 deletions src/magicgui/widgets/bases/_container_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,11 +426,13 @@

def asdict(self) -> dict[str, Any]:
"""Return state of widget as dict."""
return {
w.name: getattr(w, "value", None)
for w in self._list
if w.name and not w.gui_only
}
ret = {}
for w in self._list:
if w.name and not w.gui_only:
ret[w.name] = getattr(w, "value", None)
if isinstance(w, ContainerWidget) and w.widget_type == "Container":
ret[w.label] = w.asdict()

Check warning on line 434 in src/magicgui/widgets/bases/_container_widget.py

View check run for this annotation

Codecov / codecov/patch

src/magicgui/widgets/bases/_container_widget.py#L434

Added line #L434 was not covered by tests
return ret

def update(
self,
Expand Down