ex4nicegui 0.6.9__py3-none-any.whl → 0.7.1__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.
- ex4nicegui/bi/dataSourceFacade.py +20 -20
- ex4nicegui/bi/index.py +20 -23
- ex4nicegui/reactive/EChartsComponent/ECharts.py +9 -8
- ex4nicegui/reactive/__init__.py +14 -0
- ex4nicegui/reactive/base.py +64 -55
- ex4nicegui/reactive/mixins/backgroundColor.py +5 -5
- ex4nicegui/reactive/mixins/disableable.py +8 -8
- ex4nicegui/reactive/mixins/textColor.py +10 -10
- ex4nicegui/reactive/officials/aggrid.py +5 -5
- ex4nicegui/reactive/officials/avatar.py +86 -0
- ex4nicegui/reactive/officials/badge.py +102 -0
- ex4nicegui/reactive/officials/button.py +15 -15
- ex4nicegui/reactive/officials/checkbox.py +6 -7
- ex4nicegui/reactive/officials/chip.py +6 -7
- ex4nicegui/reactive/officials/circular_progress.py +7 -7
- ex4nicegui/reactive/officials/color_picker.py +8 -8
- ex4nicegui/reactive/officials/column.py +5 -5
- ex4nicegui/reactive/officials/date.py +6 -6
- ex4nicegui/reactive/officials/dialog.py +49 -0
- ex4nicegui/reactive/officials/echarts.py +49 -51
- ex4nicegui/reactive/officials/expansion.py +6 -6
- ex4nicegui/reactive/officials/icon.py +7 -13
- ex4nicegui/reactive/officials/image.py +6 -6
- ex4nicegui/reactive/officials/input.py +10 -11
- ex4nicegui/reactive/officials/knob.py +7 -7
- ex4nicegui/reactive/officials/label.py +11 -9
- ex4nicegui/reactive/officials/linear_progress.py +7 -7
- ex4nicegui/reactive/officials/number.py +10 -10
- ex4nicegui/reactive/officials/radio.py +10 -10
- ex4nicegui/reactive/officials/row.py +5 -5
- ex4nicegui/reactive/officials/select.py +11 -10
- ex4nicegui/reactive/officials/slider.py +6 -6
- ex4nicegui/reactive/officials/switch.py +6 -7
- ex4nicegui/reactive/officials/tab.py +0 -12
- ex4nicegui/reactive/officials/tab_panels.py +5 -5
- ex4nicegui/reactive/officials/table.py +13 -13
- ex4nicegui/reactive/officials/tabs.py +5 -5
- ex4nicegui/reactive/officials/textarea.py +6 -6
- ex4nicegui/reactive/officials/toggle.py +88 -0
- ex4nicegui/reactive/officials/tooltip.py +40 -0
- ex4nicegui/reactive/q_pagination.py +5 -5
- ex4nicegui/reactive/systems/reactive_system.py +2 -2
- ex4nicegui/reactive/vfor.js +14 -4
- ex4nicegui/reactive/vfor.py +128 -58
- ex4nicegui/reactive/view_model.py +160 -0
- ex4nicegui/reactive/vmodel.py +42 -12
- ex4nicegui/utils/signals.py +23 -21
- {ex4nicegui-0.6.9.dist-info → ex4nicegui-0.7.1.dist-info}/METADATA +223 -48
- {ex4nicegui-0.6.9.dist-info → ex4nicegui-0.7.1.dist-info}/RECORD +51 -45
- {ex4nicegui-0.6.9.dist-info → ex4nicegui-0.7.1.dist-info}/LICENSE +0 -0
- {ex4nicegui-0.6.9.dist-info → ex4nicegui-0.7.1.dist-info}/WHEEL +0 -0
ex4nicegui/reactive/vfor.py
CHANGED
|
@@ -3,7 +3,7 @@ from nicegui.element import Element
|
|
|
3
3
|
from nicegui import ui
|
|
4
4
|
from ex4nicegui.utils.clientScope import _CLIENT_SCOPE_MANAGER
|
|
5
5
|
from ex4nicegui.utils.signals import (
|
|
6
|
-
|
|
6
|
+
Ref,
|
|
7
7
|
on,
|
|
8
8
|
to_ref,
|
|
9
9
|
to_ref_wrapper,
|
|
@@ -21,7 +21,7 @@ from typing import (
|
|
|
21
21
|
TypeVar,
|
|
22
22
|
Generic,
|
|
23
23
|
Union,
|
|
24
|
-
|
|
24
|
+
Literal,
|
|
25
25
|
)
|
|
26
26
|
from functools import partial
|
|
27
27
|
from dataclasses import dataclass
|
|
@@ -38,35 +38,90 @@ class VforItem(Empty):
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
class VforContainer(Element, component="vfor.js"):
|
|
41
|
-
def __init__(self) -> None:
|
|
41
|
+
def __init__(self, transition_props: Optional[Dict[str, Any]] = None) -> None:
|
|
42
42
|
super().__init__()
|
|
43
43
|
self._props["itemIds"] = []
|
|
44
44
|
|
|
45
|
+
if transition_props:
|
|
46
|
+
self._props["transitionProps"] = transition_props
|
|
47
|
+
|
|
45
48
|
def update_items(self, item_ids: List[Dict]):
|
|
46
49
|
self._props["itemIds"] = item_ids
|
|
47
50
|
self.update()
|
|
48
51
|
|
|
49
52
|
|
|
53
|
+
def wrapper_getter_setter(ref: Ref, index: Ref[int], *keys: Union[str, int]):
|
|
54
|
+
proxy = ref.value
|
|
55
|
+
|
|
56
|
+
def getter():
|
|
57
|
+
item = proxy[index.value]
|
|
58
|
+
result = item
|
|
59
|
+
|
|
60
|
+
for k in keys:
|
|
61
|
+
result = result[k]
|
|
62
|
+
return result
|
|
63
|
+
|
|
64
|
+
def setter(value):
|
|
65
|
+
item = proxy[index.value]
|
|
66
|
+
|
|
67
|
+
if len(keys) == 0:
|
|
68
|
+
proxy[index.value] = value
|
|
69
|
+
|
|
70
|
+
if len(keys) == 1:
|
|
71
|
+
item[keys[0]] = value
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
obj = item[keys[0]]
|
|
75
|
+
for k in keys[1:-1]:
|
|
76
|
+
obj = obj[k]
|
|
77
|
+
obj[keys[-1]] = value
|
|
78
|
+
|
|
79
|
+
return getter, setter
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class VforStoreItem(Generic[_T], RefWrapper[_T]):
|
|
83
|
+
def __init__(
|
|
84
|
+
self,
|
|
85
|
+
source: _T_data,
|
|
86
|
+
index: Ref[int],
|
|
87
|
+
keys: Optional[List[Union[str, int]]] = None,
|
|
88
|
+
) -> None:
|
|
89
|
+
self._source = source
|
|
90
|
+
self._data_index = index
|
|
91
|
+
self._keys = keys or []
|
|
92
|
+
|
|
93
|
+
getter, setter = wrapper_getter_setter(source, index, *self._keys)
|
|
94
|
+
super().__init__(getter, setter)
|
|
95
|
+
|
|
96
|
+
def __getitem__(self, key: Union[str, int]):
|
|
97
|
+
return VforStoreItem(self._source, self._data_index, self._keys + [key])
|
|
98
|
+
|
|
99
|
+
|
|
50
100
|
class VforStore(Generic[_T]):
|
|
51
101
|
def __init__(self, source: _T_data, index: int) -> None:
|
|
52
102
|
self._source = source
|
|
103
|
+
self._raw_index = index
|
|
53
104
|
self._data_index = to_ref(index)
|
|
54
105
|
|
|
55
106
|
@property
|
|
56
107
|
def row_index(self):
|
|
108
|
+
"""Returns the responsive index of the current row."""
|
|
57
109
|
return self._data_index
|
|
58
110
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
111
|
+
@property
|
|
112
|
+
def raw_index(self):
|
|
113
|
+
"""Returns the index of the current row."""
|
|
114
|
+
return self._raw_index
|
|
62
115
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
)
|
|
67
|
-
wrapper._is_readonly = True
|
|
116
|
+
def get_item(self) -> _T:
|
|
117
|
+
"""Returns the current item."""
|
|
118
|
+
return to_value(self._source)[self.raw_index] # type: ignore
|
|
68
119
|
|
|
69
|
-
|
|
120
|
+
def get(self) -> Ref[_T]:
|
|
121
|
+
"""Returns a ref object of the current item.
|
|
122
|
+
Suitable for immutable types such as `int`, `str`, `float`, etc.
|
|
123
|
+
"""
|
|
124
|
+
return VforStoreItem(self._source, self._data_index) # type: ignore
|
|
70
125
|
|
|
71
126
|
def update(self, index: int):
|
|
72
127
|
self._data_index.value = index
|
|
@@ -80,10 +135,6 @@ class StoreItem:
|
|
|
80
135
|
scope: Scope
|
|
81
136
|
|
|
82
137
|
|
|
83
|
-
def _get_key_with_index(idx: int, data: Any):
|
|
84
|
-
return idx
|
|
85
|
-
|
|
86
|
-
|
|
87
138
|
def _get_key_with_getter(attr: str, idx: int, data: Any):
|
|
88
139
|
return get_attribute(data, attr)
|
|
89
140
|
|
|
@@ -97,28 +148,27 @@ class vfor(Generic[_T]):
|
|
|
97
148
|
|
|
98
149
|
|
|
99
150
|
## Examples
|
|
100
|
-
```python
|
|
101
|
-
from ex4nicegui.reactive import rxui
|
|
102
|
-
from ex4nicegui import to_ref
|
|
103
|
-
items = to_ref(
|
|
104
|
-
[
|
|
105
|
-
{"message": "foo", "done": False},
|
|
106
|
-
{"message": "bar", "done": True},
|
|
107
|
-
]
|
|
108
|
-
)
|
|
109
151
|
|
|
152
|
+
.. code-block:: python
|
|
153
|
+
from ex4nicegui.reactive import rxui
|
|
154
|
+
from ex4nicegui import to_ref
|
|
155
|
+
items = to_ref(
|
|
156
|
+
[
|
|
157
|
+
{"message": "foo", "done": False},
|
|
158
|
+
{"message": "bar", "done": True},
|
|
159
|
+
]
|
|
160
|
+
)
|
|
110
161
|
|
|
111
|
-
@rxui.vfor(items,key='message')
|
|
112
|
-
def _(store: rxui.VforStore):
|
|
113
|
-
msg_ref = store.get("message") # this is ref object
|
|
114
162
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
rxui.input(value=msg_ref)
|
|
119
|
-
rxui.checkbox(text=msg_ref, value=store.get("done"))
|
|
163
|
+
@rxui.vfor(items,key='message')
|
|
164
|
+
def _(store: rxui.VforStore):
|
|
165
|
+
msg_ref = store.get("message") # this is ref object
|
|
120
166
|
|
|
121
|
-
|
|
167
|
+
# type text into the input box and
|
|
168
|
+
# the title of the checkbox changes sync
|
|
169
|
+
with ui.card():
|
|
170
|
+
rxui.input(value=msg_ref)
|
|
171
|
+
rxui.checkbox(text=msg_ref, value=store.get("done"))
|
|
122
172
|
|
|
123
173
|
"""
|
|
124
174
|
|
|
@@ -128,9 +178,9 @@ class vfor(Generic[_T]):
|
|
|
128
178
|
*,
|
|
129
179
|
key: Optional[Union[str, Callable[[int, Any], Any]]] = None,
|
|
130
180
|
) -> None:
|
|
131
|
-
self._vfor_container = VforContainer()
|
|
132
181
|
self._data = to_ref_wrapper(lambda: data) if is_reactive(data) else data
|
|
133
|
-
self._get_key =
|
|
182
|
+
self._get_key = vfor.index_key
|
|
183
|
+
self._transition_props = {}
|
|
134
184
|
|
|
135
185
|
if isinstance(key, str):
|
|
136
186
|
self._get_key = partial(_get_key_with_getter, key)
|
|
@@ -139,11 +189,27 @@ class vfor(Generic[_T]):
|
|
|
139
189
|
|
|
140
190
|
self._store_map: Dict[Union[Any, int], StoreItem] = {}
|
|
141
191
|
|
|
192
|
+
def transition_group(
|
|
193
|
+
self,
|
|
194
|
+
css=True,
|
|
195
|
+
name: Optional[str] = "list",
|
|
196
|
+
duration: Optional[float] = None,
|
|
197
|
+
type: Optional[Literal["transition", "animation"]] = None,
|
|
198
|
+
mode: Optional[Literal["in-out", "out-in", "default"]] = None,
|
|
199
|
+
appear=False,
|
|
200
|
+
):
|
|
201
|
+
kws = {k: v for k, v in locals().items() if k != "self" and v is not None}
|
|
202
|
+
self._transition_props.update(**kws)
|
|
203
|
+
|
|
204
|
+
return self
|
|
205
|
+
|
|
142
206
|
def __call__(self, fn: Callable[[Any], None]):
|
|
207
|
+
_vfor_container = VforContainer(self._transition_props)
|
|
208
|
+
|
|
143
209
|
def build_element(index: int, value):
|
|
144
210
|
key = self._get_key(index, value)
|
|
145
211
|
|
|
146
|
-
with
|
|
212
|
+
with _vfor_container, VforItem() as element:
|
|
147
213
|
store = VforStore(self._data, index) # type: ignore
|
|
148
214
|
scope = _CLIENT_SCOPE_MANAGER.new_scope()
|
|
149
215
|
|
|
@@ -153,18 +219,27 @@ class vfor(Generic[_T]):
|
|
|
153
219
|
|
|
154
220
|
return key, element, store, scope
|
|
155
221
|
|
|
156
|
-
for idx, value in enumerate(to_value(self._data)): # type: ignore
|
|
157
|
-
key, element, store, scope = build_element(idx, value)
|
|
158
|
-
self._store_map[key] = StoreItem(store, element.id, scope)
|
|
159
|
-
|
|
160
222
|
ng_client = ui.context.client
|
|
161
223
|
|
|
162
|
-
@on(self._data, deep=True)
|
|
224
|
+
@on(self._data, deep=True, priority_level=-1)
|
|
163
225
|
def _():
|
|
164
226
|
data_map = {
|
|
165
227
|
self._get_key(idx, d): d for idx, d in enumerate(to_value(self._data))
|
|
166
228
|
}
|
|
167
229
|
|
|
230
|
+
# remove item if it's not in the new data
|
|
231
|
+
remove_items = [
|
|
232
|
+
(key, value)
|
|
233
|
+
for key, value in self._store_map.items()
|
|
234
|
+
if key not in data_map
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
for key, item in remove_items:
|
|
238
|
+
target = ng_client.elements.get(item.elementId)
|
|
239
|
+
_vfor_container.remove(target) # type: ignore
|
|
240
|
+
item.scope.dispose()
|
|
241
|
+
del self._store_map[key]
|
|
242
|
+
|
|
168
243
|
for idx, (key, value) in enumerate(data_map.items()):
|
|
169
244
|
store_item = self._store_map.get(key)
|
|
170
245
|
if store_item:
|
|
@@ -177,22 +252,17 @@ class vfor(Generic[_T]):
|
|
|
177
252
|
key, element, store, score = build_element(idx, value)
|
|
178
253
|
self._store_map[key] = StoreItem(store, element.id, score)
|
|
179
254
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
for key, value in self._store_map.items()
|
|
184
|
-
if key not in data_map
|
|
255
|
+
item_ids = [
|
|
256
|
+
{"key": key, "elementId": self._store_map.get(key).elementId} # type: ignore
|
|
257
|
+
for key in data_map.keys()
|
|
185
258
|
]
|
|
186
259
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
260
|
+
_vfor_container.update_items(item_ids)
|
|
261
|
+
|
|
262
|
+
@staticmethod
|
|
263
|
+
def value_key(_, data):
|
|
264
|
+
return data
|
|
192
265
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
for key in data_map.keys()
|
|
197
|
-
]
|
|
198
|
-
)
|
|
266
|
+
@staticmethod
|
|
267
|
+
def index_key(idx, _):
|
|
268
|
+
return idx
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Callable, Union, Type, TypeVar
|
|
3
|
+
from ex4nicegui.utils.signals import (
|
|
4
|
+
deep_ref,
|
|
5
|
+
is_ref,
|
|
6
|
+
to_value,
|
|
7
|
+
to_ref,
|
|
8
|
+
on,
|
|
9
|
+
to_raw,
|
|
10
|
+
ref_computed as computed,
|
|
11
|
+
Ref,
|
|
12
|
+
)
|
|
13
|
+
from ex4nicegui.utils.types import ReadonlyRef
|
|
14
|
+
from functools import partial
|
|
15
|
+
from signe.core.reactive import NoProxy
|
|
16
|
+
|
|
17
|
+
_CACHED_VARS_FLAG = "__vm_cached__"
|
|
18
|
+
|
|
19
|
+
_T = TypeVar("_T")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ViewModel(NoProxy):
|
|
23
|
+
"""A base class for reactive view models.
|
|
24
|
+
|
|
25
|
+
@see - https://github.com/CrystalWindSnake/ex4nicegui/blob/main/README.en.md#ViewModel
|
|
26
|
+
|
|
27
|
+
@中文文档 - https://gitee.com/carson_add/ex4nicegui/tree/main/#viewmodel
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
.. code-block:: python
|
|
31
|
+
from ex4nicegui import rxui
|
|
32
|
+
|
|
33
|
+
class MyVm(rxui.ViewModel):
|
|
34
|
+
count = rxui.var(0)
|
|
35
|
+
data = rxui.var(lambda: [1,2,3])
|
|
36
|
+
|
|
37
|
+
vm = MyVm()
|
|
38
|
+
|
|
39
|
+
rxui.label(vm.count)
|
|
40
|
+
rxui.number(value=vm.count)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self):
|
|
46
|
+
for name, value in self.__class__.__dict__.items():
|
|
47
|
+
if is_ref(value):
|
|
48
|
+
setattr(self, name, deep_ref(to_value(value)))
|
|
49
|
+
if callable(value) and hasattr(value, _CACHED_VARS_FLAG):
|
|
50
|
+
setattr(self, name, computed(partial(value, self)))
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def display(model: Union[ViewModel, Type]):
|
|
54
|
+
result = to_ref("")
|
|
55
|
+
|
|
56
|
+
watch_refs = _recursive_to_refs(model)
|
|
57
|
+
|
|
58
|
+
all_refs = {
|
|
59
|
+
key: value for key, value in model.__dict__.items() if is_ref(value)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@on(watch_refs)
|
|
63
|
+
def _():
|
|
64
|
+
result.value = str(
|
|
65
|
+
{key: _recursive_to_value(value) for key, value in all_refs.items()}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
return result
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _recursive_to_value(value_or_model):
|
|
72
|
+
value = to_raw(to_value(value_or_model))
|
|
73
|
+
|
|
74
|
+
if isinstance(value, ViewModel):
|
|
75
|
+
all_refs = {
|
|
76
|
+
key: value for key, value in value.__dict__.items() if is_ref(value)
|
|
77
|
+
}
|
|
78
|
+
return {key: _recursive_to_value(value) for key, value in all_refs.items()}
|
|
79
|
+
elif isinstance(value, dict):
|
|
80
|
+
return {key: _recursive_to_value(val) for key, val in value.items()}
|
|
81
|
+
elif isinstance(value, list):
|
|
82
|
+
return [_recursive_to_value(val) for val in value]
|
|
83
|
+
else:
|
|
84
|
+
return value
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _recursive_to_refs(model):
|
|
88
|
+
result = []
|
|
89
|
+
stack = [model]
|
|
90
|
+
|
|
91
|
+
def handle_ref(value):
|
|
92
|
+
if is_ref(value):
|
|
93
|
+
stack.append(value.value)
|
|
94
|
+
result.append(value)
|
|
95
|
+
|
|
96
|
+
elif isinstance(value, (ViewModel, Type)):
|
|
97
|
+
stack.append(value)
|
|
98
|
+
elif isinstance(value, dict):
|
|
99
|
+
stack.append(value)
|
|
100
|
+
|
|
101
|
+
while stack:
|
|
102
|
+
current = stack.pop()
|
|
103
|
+
|
|
104
|
+
if isinstance(current, (ViewModel, Type)):
|
|
105
|
+
for key, value in current.__dict__.items():
|
|
106
|
+
handle_ref(value)
|
|
107
|
+
|
|
108
|
+
elif isinstance(current, dict):
|
|
109
|
+
for key, value in current.items():
|
|
110
|
+
handle_ref(value)
|
|
111
|
+
|
|
112
|
+
elif isinstance(current, list):
|
|
113
|
+
for value in current:
|
|
114
|
+
handle_ref(value)
|
|
115
|
+
|
|
116
|
+
return result
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
_T_Var_Value = TypeVar("_T_Var_Value")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def var(value: Union[_T_Var_Value, Callable[[], _T_Var_Value]]) -> Ref[_T_Var_Value]:
|
|
123
|
+
"""Create a reactive variable. Only use within rxui.ViewModel.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
value (Union[_T_Var_Value, Callable[[], _T_Var_Value]]): The initial value or a function to generate the initial value.
|
|
127
|
+
|
|
128
|
+
Example:
|
|
129
|
+
.. code-block:: python
|
|
130
|
+
from ex4nicegui import rxui
|
|
131
|
+
class MyVm(rxui.ViewModel):
|
|
132
|
+
count = rxui.var(0)
|
|
133
|
+
data = rxui.var(lambda: [1,2,3])
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
"""
|
|
137
|
+
if callable(value):
|
|
138
|
+
return deep_ref(value())
|
|
139
|
+
return deep_ref(value)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def cached_var(func: Callable[..., _T]) -> ReadonlyRef[_T]:
|
|
143
|
+
"""A decorator to cache the result of a function. Only use within rxui.ViewModel.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
func (Callable): The function to cache.
|
|
147
|
+
|
|
148
|
+
Example:
|
|
149
|
+
.. code-block:: python
|
|
150
|
+
from ex4nicegui import rxui
|
|
151
|
+
class MyVm(rxui.ViewModel):
|
|
152
|
+
name = rxui.var("John")
|
|
153
|
+
|
|
154
|
+
@rxui.cached_var
|
|
155
|
+
def uppper_name(self):
|
|
156
|
+
return self.name.value.upper()
|
|
157
|
+
|
|
158
|
+
"""
|
|
159
|
+
setattr(func, _CACHED_VARS_FLAG, None)
|
|
160
|
+
return func # type: ignore
|
ex4nicegui/reactive/vmodel.py
CHANGED
|
@@ -26,6 +26,7 @@ import executing
|
|
|
26
26
|
import ast
|
|
27
27
|
import warnings
|
|
28
28
|
from ex4nicegui.reactive.systems.object_system import get_attribute, set_attribute
|
|
29
|
+
from ex4nicegui.utils.types import _TMaybeRef as TMaybeRef, Ref
|
|
29
30
|
|
|
30
31
|
_T = TypeVar("_T")
|
|
31
32
|
|
|
@@ -138,23 +139,24 @@ def vmodel(expr: Any, *attrs: Union[str, int]) -> TRef[Any]:
|
|
|
138
139
|
expr (Any): _description_
|
|
139
140
|
|
|
140
141
|
## Examples
|
|
141
|
-
```python
|
|
142
|
-
from ex4nicegui.reactive import rxui
|
|
143
|
-
from ex4nicegui import deep_ref
|
|
144
142
|
|
|
145
|
-
|
|
143
|
+
.. code-block:: python
|
|
144
|
+
from ex4nicegui.reactive import rxui
|
|
145
|
+
from ex4nicegui import deep_ref
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
data = deep_ref({"a": 1, "b": [1, 2, 3, 4]})
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
rxui.input(value=data.value["a"])
|
|
149
|
+
rxui.label(lambda: f"{data.value=!s}")
|
|
151
150
|
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
# No binding effect
|
|
152
|
+
rxui.input(value=data.value["a"])
|
|
153
|
+
|
|
154
|
+
# readonly binding
|
|
155
|
+
rxui.input(value=lambda: data.value["a"])
|
|
156
|
+
|
|
157
|
+
# two-way binding
|
|
158
|
+
rxui.input(value=rxui.vmodel(data.value,'a'))
|
|
154
159
|
|
|
155
|
-
# two-way binding
|
|
156
|
-
rxui.input(value=rxui.vmodel(data.value["a"]))
|
|
157
|
-
```
|
|
158
160
|
|
|
159
161
|
"""
|
|
160
162
|
|
|
@@ -205,3 +207,31 @@ def vmodel(expr: Any, *attrs: Union[str, int]) -> TRef[Any]:
|
|
|
205
207
|
TRef,
|
|
206
208
|
wrapper,
|
|
207
209
|
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def vmodel_with_index(ref: Ref, index: TMaybeRef[int], *keys: Union[str, int]) -> Ref:
|
|
213
|
+
proxy = ref.value
|
|
214
|
+
|
|
215
|
+
def getter():
|
|
216
|
+
item = proxy[to_value(index)]
|
|
217
|
+
result = item
|
|
218
|
+
|
|
219
|
+
for k in keys:
|
|
220
|
+
result = get_attribute(result, k)
|
|
221
|
+
return result
|
|
222
|
+
|
|
223
|
+
def setter(value):
|
|
224
|
+
item = proxy[to_value(index)]
|
|
225
|
+
|
|
226
|
+
if len(keys) == 1:
|
|
227
|
+
set_attribute(item, keys[0], value)
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
obj = get_attribute(item, keys[0])
|
|
231
|
+
|
|
232
|
+
for k in keys[1:-1]:
|
|
233
|
+
set_attribute(obj, k, get_attribute(obj, k))
|
|
234
|
+
|
|
235
|
+
set_attribute(obj, keys[-1], value)
|
|
236
|
+
|
|
237
|
+
return RefWrapper(getter, setter) # type: ignore
|
ex4nicegui/utils/signals.py
CHANGED
|
@@ -54,11 +54,12 @@ def to_value(obj: Union[_TMaybeRef[T], RefWrapper]) -> T:
|
|
|
54
54
|
obj (Union[_TMaybeRef[T], RefWrapper]): A getter function, an existing ref, or a non-function value.
|
|
55
55
|
|
|
56
56
|
## Example
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
|
|
58
|
+
.. code-block:: python
|
|
59
|
+
to_value(1) # 1
|
|
60
|
+
to_value(lambda: 1) # 1
|
|
61
|
+
to_value(to_ref(1)) # 1
|
|
62
|
+
|
|
62
63
|
"""
|
|
63
64
|
if is_ref(obj):
|
|
64
65
|
return obj.value # type: ignore
|
|
@@ -239,26 +240,27 @@ def event_batch(event_fn: Callable[..., None]):
|
|
|
239
240
|
Args:
|
|
240
241
|
event_fn (Callable[..., None]): event callback
|
|
241
242
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
## Example
|
|
244
|
+
|
|
245
|
+
.. code-block:: python
|
|
246
|
+
from nicegui import ui
|
|
247
|
+
from ex4nicegui import on, to_ref, effect, ref_computed, batch
|
|
248
|
+
|
|
249
|
+
a = to_ref(0)
|
|
250
|
+
b = to_ref(0)
|
|
251
|
+
text = ref_computed(lambda: f"a={a.value};b={b.value}")
|
|
246
252
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
253
|
+
@on([a, b, text])
|
|
254
|
+
def when_vars_changed():
|
|
255
|
+
ui.notify(f"a:{a.value};b:{b.value};text={text.value}")
|
|
250
256
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
257
|
+
@event_batch
|
|
258
|
+
def when_click():
|
|
259
|
+
a.value += 1
|
|
260
|
+
b.value += 1
|
|
254
261
|
|
|
255
|
-
|
|
256
|
-
def when_click():
|
|
257
|
-
a.value += 1
|
|
258
|
-
b.value += 1
|
|
262
|
+
ui.button("change all values", on_click=when_click)
|
|
259
263
|
|
|
260
|
-
ui.button("change all values", on_click=when_click)
|
|
261
|
-
```
|
|
262
264
|
"""
|
|
263
265
|
|
|
264
266
|
def wrap(*args, **kwargs):
|