diff --git a/bec_widgets/utils/forms_from_types/forms.py b/bec_widgets/utils/forms_from_types/forms.py index fee3905e..915688be 100644 --- a/bec_widgets/utils/forms_from_types/forms.py +++ b/bec_widgets/utils/forms_from_types/forms.py @@ -17,7 +17,6 @@ from bec_widgets.utils.forms_from_types.items import ( DynamicFormItem, DynamicFormItemType, FormItemSpec, - default_widget_types, widget_from_type, ) @@ -93,7 +92,6 @@ class TypedForm(BECWidget, QWidget): self._layout.addWidget(self._form_grid_container) self._form_grid_container.setLayout(QVBoxLayout()) self._form_grid.setLayout(self._new_grid_layout()) - self._widget_types = default_widget_types self._widget_from_type = widget_from_type self._post_init() @@ -112,7 +110,7 @@ class TypedForm(BECWidget, QWidget): label.setProperty("_model_field_name", item.name) label.setToolTip(item.info.description or item.name) grid.addWidget(label, row, 0) - widget = self._widget_from_type(item.item_type, self._widget_types)(parent=self, spec=item) + widget = self._widget_from_type(item.item_type)(parent=self, spec=item) widget.valueChanged.connect(self.value_changed) widget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) grid.addWidget(widget, row, 1) @@ -144,9 +142,9 @@ class TypedForm(BECWidget, QWidget): self._form_grid.setLayout(self._new_grid_layout()) self._form_grid_container.layout().addWidget(self._form_grid) - self.resize() + self.update_size() - def resize(self): + def update_size(self): self._form_grid.adjustSize() self._form_grid_container.adjustSize() self.adjustSize() diff --git a/bec_widgets/utils/forms_from_types/items.py b/bec_widgets/utils/forms_from_types/items.py index a8da67b1..6a64c2f8 100644 --- a/bec_widgets/utils/forms_from_types/items.py +++ b/bec_widgets/utils/forms_from_types/items.py @@ -1,9 +1,10 @@ from __future__ import annotations +import typing from abc import abstractmethod from decimal import Decimal from types import GenericAlias, UnionType -from typing import Callable, Literal, TypedDict +from typing import Callable, Final, Literal from bec_lib.logger import bec_logger from bec_qthemes import material_icon @@ -125,7 +126,7 @@ class ClearableBoolEntry(QWidget): self._false.setToolTip(tooltip) -DynamicFormItemType = str | int | float | Decimal | bool | dict +DynamicFormItemType = str | int | float | Decimal | bool | dict | list class DynamicFormItem(QWidget): @@ -335,11 +336,28 @@ class DictMetadataField(DynamicFormItem): self._main_widget.replace_data(value) +class ListMetadataField(DynamicFormItem): + def __init__(self, *, parent: QWidget | None = None, spec: FormItemSpec) -> None: + super().__init__(parent=parent, spec=spec) + if spec.info.annotation is list: + self.item_type = str + elif isinstance(spec.info.annotation, GenericAlias): + args = set(typing.get_args(spec.info.annotation)) + if args == set((str,)): + self.item_type = str + if args == set((int,)): + self.item_type = int + if args == set((float,)) or args == set((int, float)): + self.item_type = float + else: + self.item_type = str + + WidgetTypeRegistry = dict[ str, tuple[Callable[[type | UnionType | None], bool], type[DynamicFormItem]] ] -default_widget_types: WidgetTypeRegistry = { +DEFAULT_WIDGET_TYPES: Final[WidgetTypeRegistry] = { "str": (lambda anno: anno in [str, str | None, None], StrMetadataField), "int": (lambda anno: anno in [int, int | None], IntMetadataField), "float_decimal": ( @@ -355,14 +373,15 @@ default_widget_types: WidgetTypeRegistry = { "list": ( lambda anno: anno in [list, list | None] or (isinstance(anno, GenericAlias) and anno.__origin__ is list), - StrMetadataField, + ListMetadataField, ), } def widget_from_type( - annotation: type | UnionType | None, widget_types: WidgetTypeRegistry + annotation: type | UnionType | None, widget_types: WidgetTypeRegistry | None = None ) -> type[DynamicFormItem]: + widget_types = widget_types or DEFAULT_WIDGET_TYPES for predicate, widget_type in widget_types.values(): if predicate(annotation): return widget_type @@ -378,6 +397,8 @@ if __name__ == "__main__": # pragma: no cover value3: bool = Field(True) value4: int = Field(123) value5: int | None = Field() + value6: list[int] = Field() + value7: list = Field() app = QApplication([]) w = QWidget() diff --git a/bec_widgets/widgets/services/device_browser/device_item/device_config_form.py b/bec_widgets/widgets/services/device_browser/device_item/device_config_form.py index 815ef902..9592f39e 100644 --- a/bec_widgets/widgets/services/device_browser/device_item/device_config_form.py +++ b/bec_widgets/widgets/services/device_browser/device_item/device_config_form.py @@ -8,9 +8,9 @@ from bec_widgets.utils.colors import get_theme_name from bec_widgets.utils.forms_from_types import styles from bec_widgets.utils.forms_from_types.forms import PydanticModelForm from bec_widgets.utils.forms_from_types.items import ( + DEFAULT_WIDGET_TYPES, BoolMetadataField, BoolToggleMetadataField, - default_widget_types, ) @@ -26,9 +26,9 @@ class DeviceConfigForm(PydanticModelForm): client=client, **kwargs, ) - self._widget_types = default_widget_types.copy() - self._widget_types["bool"] = (lambda anno: anno == bool, BoolToggleMetadataField) - self._widget_types["optional_bool"] = (lambda anno: anno == bool | None, BoolMetadataField) + self._widget_types = DEFAULT_WIDGET_TYPES.copy() + self._widget_types["bool"] = (lambda anno: anno is bool, BoolToggleMetadataField) + self._widget_types["optional_bool"] = (lambda anno: anno is bool | None, BoolMetadataField) self._validity.setVisible(False) self._connect_to_theme_change() self.populate()