ex4nicegui 0.5.1__py3-none-any.whl → 0.5.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. ex4nicegui/__init__.py +3 -6
  2. ex4nicegui/bi/dataSourceFacade.py +16 -61
  3. ex4nicegui/bi/elements/text.py +1 -1
  4. ex4nicegui/bi/elements/ui_aggrid.py +2 -2
  5. ex4nicegui/bi/elements/ui_echarts.py +3 -3
  6. ex4nicegui/bi/elements/ui_slider.py +1 -1
  7. ex4nicegui/bi/elements/ui_table.py +2 -2
  8. ex4nicegui/bi/protocols.py +0 -1
  9. ex4nicegui/bi/types.py +0 -1
  10. ex4nicegui/experimental_/__init__.py +4 -0
  11. ex4nicegui/experimental_/gridLayout/__init__.py +7 -0
  12. ex4nicegui/gsap/__init__.py +11 -0
  13. ex4nicegui/gsap/timeline.js +56 -0
  14. ex4nicegui/gsap/timeline.py +78 -0
  15. ex4nicegui/layout/__init__.py +13 -2
  16. ex4nicegui/layout/gridFlex/__init__.py +1 -1
  17. ex4nicegui/layout/gridFlex/gridFlex.py +1 -3
  18. ex4nicegui/layout/rxFlex/__init__.py +0 -1
  19. ex4nicegui/layout/rxFlex/index.py +16 -6
  20. ex4nicegui/reactive/__init__.py +57 -2
  21. ex4nicegui/reactive/fileWatcher.py +1 -1
  22. ex4nicegui/reactive/local_file_picker.py +1 -1
  23. ex4nicegui/reactive/officials/button.py +0 -1
  24. ex4nicegui/reactive/officials/circular_progress.py +64 -0
  25. ex4nicegui/reactive/officials/column.py +19 -4
  26. ex4nicegui/reactive/officials/html.py +1 -1
  27. ex4nicegui/reactive/officials/knob.py +75 -0
  28. ex4nicegui/reactive/officials/label.py +0 -1
  29. ex4nicegui/reactive/officials/number.py +1 -2
  30. ex4nicegui/reactive/officials/row.py +18 -4
  31. ex4nicegui/reactive/q_pagination.py +33 -29
  32. ex4nicegui/reactive/usePagination.py +6 -4
  33. ex4nicegui/reactive/utils.py +42 -3
  34. ex4nicegui/reactive/vfor.py +19 -39
  35. ex4nicegui/reactive/vmodel.py +210 -0
  36. ex4nicegui/tools/__init__.py +0 -1
  37. ex4nicegui/tools/debug.py +1 -1
  38. ex4nicegui/utils/signals.py +12 -2
  39. {ex4nicegui-0.5.1.dist-info → ex4nicegui-0.5.3.dist-info}/METADATA +60 -34
  40. {ex4nicegui-0.5.1.dist-info → ex4nicegui-0.5.3.dist-info}/RECORD +43 -38
  41. {ex4nicegui-0.5.1.dist-info → ex4nicegui-0.5.3.dist-info}/LICENSE +0 -0
  42. {ex4nicegui-0.5.1.dist-info → ex4nicegui-0.5.3.dist-info}/WHEEL +0 -0
  43. {ex4nicegui-0.5.1.dist-info → ex4nicegui-0.5.3.dist-info}/top_level.txt +0 -0
@@ -1,18 +1,33 @@
1
1
  from typing import (
2
2
  Any,
3
3
  )
4
+ from ex4nicegui.reactive.utils import ParameterClassifier
5
+ from ex4nicegui.utils.signals import (
6
+ _TMaybeRef as TMaybeRef,
7
+ )
4
8
  from nicegui import ui
5
9
  from .base import BindableUi
6
10
 
7
11
 
8
12
  class ColumnBindableUi(BindableUi[ui.column]):
9
- def __init__(
10
- self,
11
- ) -> None:
12
- element = ui.column()
13
+ def __init__(self, *, wrap: TMaybeRef[bool] = True) -> None:
14
+ pc = ParameterClassifier(locals(), maybeRefs=["wrap"], events=[])
15
+ element = ui.column(**pc.get_values_kws())
13
16
 
14
17
  super().__init__(element)
15
18
 
19
+ for key, value in pc.get_bindings().items():
20
+ self.bind_prop(key, value) # type: ignore
21
+
22
+ def bind_prop(self, prop: str, ref_ui: TMaybeRef):
23
+ if prop == "wrap":
24
+ return self.bind_wrap(ref_ui)
25
+
26
+ return super().bind_prop(prop, ref_ui)
27
+
28
+ def bind_wrap(self, ref_ui: TMaybeRef):
29
+ self.bind_classes({"wrap": ref_ui})
30
+
16
31
  def __enter__(self):
17
32
  self.element.__enter__()
18
33
  return self
@@ -5,7 +5,7 @@ from ex4nicegui.utils.signals import (
5
5
  to_value,
6
6
  )
7
7
  from nicegui import ui
8
- from .base import BindableUi, _bind_color
8
+ from .base import BindableUi
9
9
 
10
10
 
11
11
  class HtmlComponent(ui.element, component="html.js"):
@@ -0,0 +1,75 @@
1
+ from typing import (
2
+ Any,
3
+ Callable,
4
+ Optional,
5
+ )
6
+ from ex4nicegui.reactive.utils import ParameterClassifier
7
+ from ex4nicegui.utils.apiEffect import ui_effect
8
+
9
+ from ex4nicegui.utils.signals import (
10
+ ReadonlyRef,
11
+ _TMaybeRef as TMaybeRef,
12
+ to_value,
13
+ )
14
+ from nicegui import ui
15
+ from .base import BindableUi, DisableableMixin
16
+
17
+
18
+ class KnobBindableUi(
19
+ BindableUi[ui.knob],
20
+ DisableableMixin,
21
+ ):
22
+ def __init__(
23
+ self,
24
+ value: TMaybeRef[float] = 0.0,
25
+ *,
26
+ min: TMaybeRef[float] = 0.0, # pylint: disable=redefined-builtin
27
+ max: TMaybeRef[float] = 1.0, # pylint: disable=redefined-builtin
28
+ step: TMaybeRef[float] = 0.01,
29
+ color: Optional[TMaybeRef[str]] = "primary",
30
+ center_color: Optional[TMaybeRef[str]] = None,
31
+ track_color: Optional[TMaybeRef[str]] = None,
32
+ size: Optional[TMaybeRef[str]] = None,
33
+ show_value: TMaybeRef[bool] = False,
34
+ on_change: Optional[Callable[..., Any]] = None,
35
+ ) -> None:
36
+ pc = ParameterClassifier(
37
+ locals(),
38
+ maybeRefs=[
39
+ "value",
40
+ "min",
41
+ "max",
42
+ "step",
43
+ "color",
44
+ "center_color",
45
+ "track_color",
46
+ "size",
47
+ "show_value",
48
+ ],
49
+ v_model=("value", "on_change"),
50
+ events=["on_change"],
51
+ )
52
+
53
+ value_kws = pc.get_values_kws()
54
+ element = ui.knob(**value_kws)
55
+ super().__init__(element) # type: ignore
56
+
57
+ for key, value in pc.get_bindings().items():
58
+ self.bind_prop(key, value) # type: ignore
59
+
60
+ @property
61
+ def value(self):
62
+ return self.element.value
63
+
64
+ def bind_prop(self, prop: str, ref_ui: ReadonlyRef):
65
+ if prop == "value":
66
+ return self.bind_value(ref_ui)
67
+
68
+ return super().bind_prop(prop, ref_ui)
69
+
70
+ def bind_value(self, ref_ui: ReadonlyRef[float]):
71
+ @ui_effect
72
+ def _():
73
+ self.element.set_value(to_value(ref_ui))
74
+
75
+ return self
@@ -1,6 +1,5 @@
1
1
  from typing import Any
2
2
  from ex4nicegui.reactive.utils import ParameterClassifier
3
- from ex4nicegui.utils.apiEffect import ui_effect
4
3
  from ex4nicegui.utils.signals import (
5
4
  to_value,
6
5
  _TMaybeRef as TMaybeRef,
@@ -4,7 +4,6 @@ from typing import (
4
4
  Optional,
5
5
  TypeVar,
6
6
  Dict,
7
- Union,
8
7
  )
9
8
  from ex4nicegui.reactive.utils import ParameterClassifier
10
9
  from ex4nicegui.utils.apiEffect import ui_effect
@@ -26,7 +25,7 @@ class NumberBindableUi(BindableUi[ui.number]):
26
25
  label: Optional[TMaybeRef[str]] = None,
27
26
  *,
28
27
  placeholder: Optional[TMaybeRef[str]] = None,
29
- value: TMaybeRef[Union[float, None]] = None,
28
+ value: Optional[TMaybeRef[float]] = None,
30
29
  min: Optional[TMaybeRef[float]] = None,
31
30
  max: Optional[TMaybeRef[float]] = None,
32
31
  step: Optional[TMaybeRef[float]] = None,
@@ -1,17 +1,31 @@
1
1
  from typing import (
2
2
  Any,
3
3
  )
4
+ from ex4nicegui.reactive.utils import ParameterClassifier
5
+ from ex4nicegui.utils.signals import (
6
+ _TMaybeRef as TMaybeRef,
7
+ )
4
8
  from nicegui import ui
5
9
  from .base import BindableUi
6
10
 
7
11
 
8
12
  class RowBindableUi(BindableUi[ui.row]):
9
- def __init__(
10
- self,
11
- ) -> None:
12
- element = ui.row()
13
+ def __init__(self, *, wrap: TMaybeRef[bool] = True) -> None:
14
+ pc = ParameterClassifier(locals(), maybeRefs=["wrap"], events=[])
15
+ element = ui.row(**pc.get_values_kws())
13
16
 
14
17
  super().__init__(element)
18
+ for key, value in pc.get_bindings().items():
19
+ self.bind_prop(key, value) # type: ignore
20
+
21
+ def bind_prop(self, prop: str, ref_ui: TMaybeRef):
22
+ if prop == "wrap":
23
+ return self.bind_wrap(ref_ui)
24
+
25
+ return super().bind_prop(prop, ref_ui)
26
+
27
+ def bind_wrap(self, ref_ui: TMaybeRef):
28
+ self.bind_classes({"wrap": ref_ui})
15
29
 
16
30
  def __enter__(self):
17
31
  self.element.__enter__()
@@ -1,45 +1,49 @@
1
- from typing import cast
2
- from nicegui.element import Element
1
+ from typing import Any, Optional, Callable
2
+ from ex4nicegui.reactive.utils import ParameterClassifier
3
3
  from ex4nicegui.utils.signals import (
4
- Ref,
5
- effect,
6
4
  to_value,
7
5
  _TMaybeRef as TMaybeRef,
8
6
  )
7
+ from nicegui import ui
8
+ from ex4nicegui.reactive.officials.base import BindableUi
9
9
 
10
10
 
11
- class QPagination(Element):
12
- VALUE_PROP: str = "model-value"
13
- LOOPBACK = False
14
-
11
+ class PaginationBindableUi(BindableUi[ui.pagination]):
15
12
  def __init__(
16
13
  self,
17
- value: TMaybeRef[int] = 1,
18
- min: TMaybeRef[int] = 1,
19
- max: TMaybeRef[int] = 5,
14
+ min: TMaybeRef[int],
15
+ max: TMaybeRef[int],
16
+ *, # pylint: disable=redefined-builtin
17
+ direction_links: TMaybeRef[bool] = False,
18
+ value: TMaybeRef[int] = ..., # type: ignore
19
+ on_change: Optional[Callable[..., Any]] = None,
20
20
  ) -> None:
21
- super().__init__(tag="q-pagination")
21
+ pc = ParameterClassifier(
22
+ locals(),
23
+ maybeRefs=["min", "max", "direction_links", "value"],
24
+ v_model=("value", "on_change"),
25
+ events=["on_change"],
26
+ )
22
27
 
23
- self.__value = value
24
- self.__min = min
25
- self.__max = max
28
+ element = ui.pagination(**pc.get_values_kws())
29
+ super().__init__(element)
26
30
 
27
- def onchange(e):
28
- arg_value = cast(int, e.args)
31
+ for key, value in pc.get_bindings().items():
32
+ self.bind_prop(key, value) # type: ignore
29
33
 
30
- self._props["model-value"] = arg_value
31
- if isinstance(self.__value, Ref):
32
- self.__value.value = arg_value
33
- self.update()
34
+ @property
35
+ def value(self):
36
+ return self.element.value
34
37
 
35
- self.on("update:model-value", onchange)
38
+ def bind_prop(self, prop: str, ref_ui: TMaybeRef):
39
+ if prop == "value":
40
+ return self.bind_value(ref_ui)
36
41
 
37
- @effect
38
- def _():
39
- self._props["model-value"] = to_value(value)
40
- self._props["min"] = to_value(min)
42
+ return super().bind_prop(prop, ref_ui)
41
43
 
42
- if self.__max is not None:
43
- self._props["max"] = to_value(self.__max)
44
+ def bind_value(self, ref_ui: TMaybeRef[int]):
45
+ @self._ui_effect
46
+ def _():
47
+ self.element.set_value(to_value(ref_ui))
44
48
 
45
- self.update()
49
+ return self
@@ -6,10 +6,10 @@ from ex4nicegui.utils.signals import (
6
6
  effect,
7
7
  _TMaybeRef as TMaybeRef,
8
8
  )
9
- from typing import Any, TypeVar
9
+ from typing import Any
10
10
  from typing_extensions import Protocol
11
11
  import math
12
- from ex4nicegui.reactive.q_pagination import QPagination
12
+ from ex4nicegui.reactive.q_pagination import PaginationBindableUi
13
13
 
14
14
 
15
15
  def _clamp(value, min_v, max_v) -> int:
@@ -97,6 +97,8 @@ class PaginationRef:
97
97
  return cp
98
98
 
99
99
  def create_q_pagination(self):
100
- page = QPagination(self.current_page, max=self.page_count)
101
- page.props("boundary-links direction-links")
100
+ page = PaginationBindableUi(
101
+ min=1, max=self.page_count, direction_links=True, value=self.current_page
102
+ )
103
+ page.props("boundary-links")
102
104
  return page
@@ -1,9 +1,33 @@
1
- from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, cast
1
+ from typing import (
2
+ Any,
3
+ Callable,
4
+ Dict,
5
+ Iterable,
6
+ List,
7
+ Optional,
8
+ Protocol,
9
+ Tuple,
10
+ cast,
11
+ runtime_checkable,
12
+ Union,
13
+ )
2
14
 
3
15
  from ex4nicegui.utils.signals import is_ref, to_value, is_setter_ref
4
16
  from nicegui.events import handle_event
5
17
 
6
18
 
19
+ @runtime_checkable
20
+ class GetItemProtocol(Protocol):
21
+ def __getitem__(self, key):
22
+ ...
23
+
24
+
25
+ @runtime_checkable
26
+ class SetItemProtocol(Protocol):
27
+ def __setitem__(self, key, value):
28
+ ...
29
+
30
+
7
31
  def _convert_kws_ref2value(kws: Dict) -> Dict:
8
32
  return {key: to_value(value) for key, value in kws.items()}
9
33
 
@@ -24,7 +48,7 @@ class ParameterClassifier:
24
48
  if extend_kws:
25
49
  exclude.append(extend_kws)
26
50
 
27
- self._args = {
51
+ self._args: Dict[str, Any] = {
28
52
  k: v
29
53
  for k, v in args.items()
30
54
  if k != "self" and k[0] != "_" and (k not in exclude)
@@ -70,7 +94,22 @@ class ParameterClassifier:
70
94
 
71
95
  def get_bindings(self) -> Dict:
72
96
  return {
73
- k: v
97
+ k.replace("_", "-"): v
74
98
  for k, v in self._args.items()
75
99
  if (k in self.maybeRefs and (is_ref(v) or isinstance(v, Callable)))
76
100
  }
101
+
102
+
103
+ def get_attribute(obj: Union[object, GetItemProtocol], name: Union[str, int]) -> Any:
104
+ if isinstance(obj, (GetItemProtocol)):
105
+ return obj[name]
106
+ return getattr(obj, name) # type: ignore
107
+
108
+
109
+ def set_attribute(
110
+ obj: Union[object, SetItemProtocol], name: Union[str, int], value: Any
111
+ ) -> None:
112
+ if isinstance(obj, SetItemProtocol):
113
+ obj[name] = value
114
+ else:
115
+ setattr(obj, name, value) # type: ignore
@@ -2,13 +2,18 @@ from __future__ import annotations
2
2
  from nicegui.element import Element
3
3
  from nicegui import ui
4
4
  from ex4nicegui.utils.clientScope import _CLIENT_SCOPE_MANAGER
5
- from ex4nicegui.utils.signals import ReadonlyRef, on, to_ref, to_ref_wrapper
5
+ from ex4nicegui.utils.signals import (
6
+ TReadonlyRef,
7
+ on,
8
+ to_ref,
9
+ to_ref_wrapper,
10
+ TGetterOrReadonlyRef,
11
+ )
6
12
  from typing import (
7
13
  Any,
8
14
  Callable,
9
15
  Dict,
10
16
  List,
11
- Mapping,
12
17
  Optional,
13
18
  TypeVar,
14
19
  Generic,
@@ -17,11 +22,11 @@ from typing import (
17
22
  )
18
23
  from functools import partial
19
24
  from dataclasses import dataclass
20
- from signe.core.reactive import DictProxy as signe_DictProxy
21
25
  from signe.core.scope import Scope
26
+ from .utils import get_attribute
22
27
 
23
28
  _T = TypeVar("_T")
24
- _T_data = ReadonlyRef[List[Any]]
29
+ _T_data = TGetterOrReadonlyRef[List[Any]]
25
30
 
26
31
 
27
32
  class VforStore(Generic[_T]):
@@ -33,29 +38,17 @@ class VforStore(Generic[_T]):
33
38
  def row_index(self):
34
39
  return self._data_index
35
40
 
36
- def get(self, attr: Optional[str] = None) -> _T:
37
- item = self._source.value[self._data_index.value]
38
-
39
- if attr:
40
- setter = None
41
- if isinstance(item, signe_DictProxy):
42
-
43
- def base_setter(value):
44
- item[attr] = value
41
+ def get(self) -> TReadonlyRef[_T]:
42
+ def base_setter(value):
43
+ self._source.value[self._data_index.value] = value
45
44
 
46
- setter = base_setter
47
- else:
48
- setter = lambda x: _set_attribute(item, attr, x) # noqa: E731
49
-
50
- return cast(
51
- _T,
52
- to_ref_wrapper(
53
- lambda: _get_attribute(item, attr),
54
- setter,
55
- ),
56
- )
45
+ wrapper = to_ref_wrapper(
46
+ lambda: self._source.value[self._data_index.value],
47
+ base_setter,
48
+ )
49
+ wrapper._is_readonly = True
57
50
 
58
- return item
51
+ return cast(TReadonlyRef, wrapper)
59
52
 
60
53
  def update(self, index: int):
61
54
  self._data_index.value = index
@@ -73,25 +66,12 @@ class VforContainer(Element, component="vfor.js"):
73
66
  pass
74
67
 
75
68
 
76
- def _get_attribute(obj: Union[object, Mapping], name: str) -> Any:
77
- if isinstance(obj, Mapping):
78
- return obj[name]
79
- return getattr(obj, name)
80
-
81
-
82
- def _set_attribute(obj: Union[object, Mapping], name: str, value: Any) -> None:
83
- if isinstance(obj, dict):
84
- obj[name] = value
85
- else:
86
- setattr(obj, name, value)
87
-
88
-
89
69
  def _get_key_with_index(idx: int, data: Any):
90
70
  return idx
91
71
 
92
72
 
93
73
  def _get_key_with_getter(attr: str, idx: int, data: Any):
94
- return _get_attribute(data, attr)
74
+ return get_attribute(data, attr)
95
75
 
96
76
 
97
77
  class vfor(Generic[_T]):
@@ -0,0 +1,210 @@
1
+ from __future__ import annotations
2
+ import inspect
3
+ from io import BytesIO
4
+ from ex4nicegui.utils.signals import (
5
+ RefWrapper,
6
+ TRef,
7
+ to_raw,
8
+ is_setter_ref,
9
+ to_ref_wrapper,
10
+ to_value,
11
+ is_reactive,
12
+ is_setter_ref,
13
+ to_ref_wrapper,
14
+ to_value,
15
+ )
16
+ from typing import (
17
+ Any,
18
+ Callable,
19
+ TypeVar,
20
+ Union,
21
+ cast,
22
+ Tuple,
23
+ NamedTuple,
24
+ )
25
+
26
+ from types import FrameType
27
+ import tokenize
28
+ import executing
29
+ import ast
30
+ import warnings
31
+ from .utils import get_attribute, set_attribute
32
+
33
+ _T = TypeVar("_T")
34
+
35
+
36
+ def get_caller() -> FrameType:
37
+ return inspect.currentframe().f_back.f_back # type: ignore
38
+
39
+
40
+ def get_args_code(caller: FrameType) -> str:
41
+ node = executing.Source.executing(caller).node
42
+
43
+ assert isinstance(node, ast.Call)
44
+
45
+ source = inspect.getsource(inspect.getmodule(caller)) # type: ignore
46
+ key_code = ast.get_source_segment(source, node.args[0])
47
+ return key_code # type: ignore
48
+
49
+
50
+ class CodeInfo(NamedTuple):
51
+ model: str
52
+ is_ref: bool
53
+ keys: Tuple[Union[str, int], ...] = tuple() # type: ignore
54
+
55
+
56
+ def normalize(token: tokenize.TokenInfo):
57
+ if token.type == tokenize.NUMBER:
58
+ return int(token.string)
59
+
60
+ if token.type == tokenize.STRING:
61
+ return token.string[1:-1]
62
+
63
+ return token.string
64
+
65
+
66
+ def parse_code(code: str) -> CodeInfo:
67
+ tokens = [
68
+ t
69
+ for t in tokenize.tokenize(BytesIO(code.encode("utf8")).readline)
70
+ if t.type in (tokenize.NAME, tokenize.NUMBER, tokenize.OP, tokenize.STRING)
71
+ ]
72
+
73
+ model = tokens[0].string
74
+ is_ref = (
75
+ len(tokens) >= 3
76
+ and tokens[1].type == tokenize.OP
77
+ and tokens[1].string == "."
78
+ and tokens[2].string == "value"
79
+ )
80
+
81
+ keys_search_start = 3 if is_ref else 1
82
+
83
+ keys = tuple(
84
+ normalize(token)
85
+ for token in tokens[keys_search_start:]
86
+ if token.type != tokenize.OP
87
+ )
88
+
89
+ return CodeInfo(model, is_ref, keys)
90
+
91
+
92
+ def create_writeable_wrapper(expr, ref_data, attrs: Tuple[Union[str, int], ...]):
93
+ if not attrs:
94
+
95
+ def maybe_ref_getter():
96
+ return to_value(expr)
97
+
98
+ def reactive_getter():
99
+ return to_raw(expr)
100
+
101
+ def setter(value):
102
+ pass
103
+
104
+ wrapper = to_ref_wrapper(
105
+ reactive_getter if is_reactive(expr) else maybe_ref_getter,
106
+ setter,
107
+ )
108
+ wrapper._is_readonly = False
109
+ return wrapper
110
+
111
+ def getter():
112
+ obj = get_attribute(to_value(ref_data), attrs[0])
113
+ for attr in attrs[1:]:
114
+ obj = get_attribute(obj, attr)
115
+
116
+ return obj
117
+
118
+ def setter(value):
119
+ obj = to_value(ref_data)
120
+
121
+ for attr in attrs[:-1]:
122
+ obj = get_attribute(obj, attr)
123
+
124
+ set_attribute(obj, attrs[-1], value)
125
+
126
+ wrapper = to_ref_wrapper(
127
+ getter,
128
+ setter,
129
+ )
130
+ wrapper._is_readonly = False
131
+ return wrapper
132
+
133
+
134
+ def vmodel(expr: Any, *attrs: Union[str, int]) -> TRef[Any]:
135
+ """Create a two-way binding on a form input element or a component.
136
+
137
+ @see - https://github.com/CrystalWindSnake/ex4nicegui/blob/main/README.en.md#vmodel
138
+ @中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main/#vmodel
139
+
140
+ Args:
141
+ expr (Any): _description_
142
+
143
+ ## Examples
144
+ ```python
145
+ from ex4nicegui.reactive import rxui
146
+ from ex4nicegui import deep_ref
147
+
148
+ data = deep_ref({"a": 1, "b": [1, 2, 3, 4]})
149
+
150
+ rxui.label(lambda: f"{data.value=!s}")
151
+
152
+ # No binding effect
153
+ rxui.input(value=data.value["a"])
154
+
155
+ # readonly binding
156
+ rxui.input(value=lambda: data.value["a"])
157
+
158
+ # two-way binding
159
+ rxui.input(value=rxui.vmodel(data.value["a"]))
160
+ ```
161
+
162
+ """
163
+
164
+ assert not isinstance(expr, Callable), "argument expr cannot be a function"
165
+
166
+ if isinstance(expr, RefWrapper):
167
+ expr._is_readonly = False
168
+
169
+ if is_setter_ref(expr):
170
+ if attrs:
171
+ wrapper = create_writeable_wrapper(expr, expr, attrs)
172
+
173
+ return cast(
174
+ TRef,
175
+ wrapper,
176
+ )
177
+
178
+ return cast(
179
+ TRef,
180
+ expr,
181
+ )
182
+
183
+ caller = get_caller()
184
+ code = get_args_code(caller)
185
+
186
+ info = parse_code(code)
187
+ ref_data = caller.f_locals.get(info.model) or caller.f_globals.get(info.model)
188
+ assert ref_data is not None, f"{info.model} not found"
189
+ all_attrs = (*info.keys, *attrs)
190
+
191
+ if not all_attrs:
192
+ warn_mes = ""
193
+ if is_reactive(expr) or is_setter_ref(expr):
194
+ warn_mes = rf"""Expression missing the key,result is read-only binding.Maybe you meant `{code}['key']`"""
195
+ elif not info.is_ref:
196
+ warn_mes = """Maybe you don't need to use vmodel"""
197
+ else:
198
+ warn_mes = rf"""No binding.Maybe you meant `{info.model}`"""
199
+
200
+ warnings.warn(
201
+ warn_mes,
202
+ stacklevel=2,
203
+ )
204
+
205
+ wrapper = create_writeable_wrapper(expr, ref_data, all_attrs)
206
+
207
+ return cast(
208
+ TRef,
209
+ wrapper,
210
+ )
@@ -1 +0,0 @@
1
- from .debug import display_ref_vars_ui
ex4nicegui/tools/debug.py CHANGED
@@ -2,7 +2,7 @@ from signe.core.effect import Effect
2
2
  import ex4nicegui.reactive as rxui
3
3
  from ex4nicegui.utils.signals import ReadonlyRef, DescReadonlyRef, Ref, ref_computed
4
4
  from nicegui import ui
5
- from typing import Callable, Dict, TypeVar, Generic
5
+ from typing import Dict, TypeVar
6
6
 
7
7
  T = TypeVar("T")
8
8