Skip to content

Commit 08f9021

Browse files
add socket_id to UIEventArguments; use new client.target where possible
1 parent 5b42401 commit 08f9021

25 files changed

+108
-69
lines changed

nicegui/air.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ def _handle_event(data: Dict[str, Any]) -> None:
169169
arg0 = json.loads(args[0])
170170
arg0['socket_id'] = client_id # HACK: translate socket_id of ui.scene's init event
171171
args[0] = json.dumps(arg0)
172-
client.handle_event(data['msg'])
172+
client.handle_event(data['sid'], data['msg'])
173173

174174
@self.relay.on('javascript_response')
175175
def _handle_javascript_response(data: Dict[str, Any]) -> None:

nicegui/client.py

+15-6
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,13 @@ def run_javascript(self, code: str, *, timeout: float = 1.0) -> AwaitableRespons
206206
:return: AwaitableResponse that can be awaited to get the result of the JavaScript code
207207
"""
208208
request_id = str(uuid.uuid4())
209-
target_id = self._temporary_socket_id or self.id
209+
target_id = self.target
210210

211211
def send_and_forget():
212212
self.outbox.enqueue_message('run_javascript', {'code': code}, target_id)
213213

214214
async def send_and_wait():
215-
if self is self.auto_index_client:
215+
if self is self.auto_index_client and not self._temporary_socket_id:
216216
raise RuntimeError('Cannot await JavaScript responses on the auto-index page. '
217217
'There could be multiple clients connected and it is not clear which one to wait for.')
218218
self.outbox.enqueue_message('run_javascript', {'code': code, 'request_id': request_id}, target_id)
@@ -223,11 +223,12 @@ async def send_and_wait():
223223
def open(self, target: Union[Callable[..., Any], str], new_tab: bool = False) -> None:
224224
"""Open a new page in the client."""
225225
path = target if isinstance(target, str) else self.page_routes[target]
226-
self.outbox.enqueue_message('open', {'path': path, 'new_tab': new_tab}, self.id)
226+
self.outbox.enqueue_message('open', {'path': path, 'new_tab': new_tab}, self.target)
227227

228228
def download(self, src: Union[str, bytes], filename: Optional[str] = None, media_type: str = '') -> None:
229229
"""Download a file from a given URL or raw bytes."""
230-
self.outbox.enqueue_message('download', {'src': src, 'filename': filename, 'media_type': media_type}, self.id)
230+
self.outbox.enqueue_message('download', {'src': src, 'filename': filename, 'media_type': media_type},
231+
self.target)
231232

232233
def on_connect(self, handler: Union[Callable[..., Any], Awaitable]) -> None:
233234
"""Add a callback to be invoked when the client connects."""
@@ -278,15 +279,15 @@ def _cancel_delete_task(self, document_id: str) -> None:
278279
if document_id in self._delete_tasks:
279280
self._delete_tasks.pop(document_id).cancel()
280281

281-
def handle_event(self, msg: Dict) -> None:
282+
def handle_event(self, sid: str, msg: Dict) -> None:
282283
"""Forward an event to the corresponding element."""
283284
with self:
284285
sender = self.elements.get(msg['id'])
285286
if sender is not None and not sender.is_ignoring_events:
286287
msg['args'] = [None if arg is None else json.loads(arg) for arg in msg.get('args', [])]
287288
if len(msg['args']) == 1:
288289
msg['args'] = msg['args'][0]
289-
sender._handle_event(msg) # pylint: disable=protected-access
290+
sender._handle_event(sid, msg) # pylint: disable=protected-access
290291

291292
def handle_javascript_response(self, msg: Dict) -> None:
292293
"""Store the result of a JavaScript command."""
@@ -354,6 +355,14 @@ def individual_target(self, socket_id: str) -> Iterator[None]:
354355
yield
355356
self._temporary_socket_id = None
356357

358+
@property
359+
def target(self) -> str:
360+
"""Return the target of the client.
361+
362+
This is usually the client ID, but can be overridden by the ``individual_target`` context manager.
363+
"""
364+
return self._temporary_socket_id or self.id
365+
357366
@classmethod
358367
async def prune_instances(cls) -> None:
359368
"""Prune stale clients in an endless loop."""

nicegui/element.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -377,10 +377,10 @@ def on(self,
377377
self.update()
378378
return self
379379

380-
def _handle_event(self, msg: Dict) -> None:
380+
def _handle_event(self, sid: str, msg: Dict) -> None:
381381
listener = self._event_listeners[msg['listener_id']]
382382
storage.request_contextvar.set(listener.request)
383-
args = events.GenericEventArguments(sender=self, client=self.client, args=msg['args'])
383+
args = events.GenericEventArguments(sender=self, client=self.client, socket_id=sid, args=msg['args'])
384384
events.handle_event(listener.handler, args)
385385

386386
def update(self) -> None:

nicegui/elements/button.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def __init__(self,
3939

4040
def on_click(self, callback: Handler[ClickEventArguments]) -> Self:
4141
"""Add a callback to be invoked when the button is clicked."""
42-
self.on('click', lambda _: handle_event(callback, ClickEventArguments(sender=self, client=self.client)), [])
42+
self.on('click', lambda e: handle_event(callback, ClickEventArguments.from_generic_event(e)), [])
4343
return self
4444

4545
def _text_to_model_text(self, text: str) -> None:

nicegui/elements/button_dropdown.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def __init__(self,
4848
self._props['split'] = True
4949

5050
if on_click:
51-
self.on('click', lambda _: handle_event(on_click, ClickEventArguments(sender=self, client=self.client)), [])
51+
self.on('click', lambda e: handle_event(on_click, ClickEventArguments.from_generic_event(e)), [])
5252

5353
def _text_to_model_text(self, text: str) -> None:
5454
self._props['label'] = text

nicegui/elements/chip.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,5 @@ def on_click(self, callback: Handler[ClickEventArguments]) -> Self:
5656
"""Add a callback to be invoked when the chip is clicked."""
5757
self._props['clickable'] = True
5858
self.update()
59-
self.on('click', lambda _: handle_event(callback, ClickEventArguments(sender=self, client=self.client)), [])
59+
self.on('click', lambda e: handle_event(callback, ClickEventArguments.from_generic_event(e)), [])
6060
return self

nicegui/elements/color_picker.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def __init__(self, *,
2626
with self:
2727
def handle_change(e: GenericEventArguments):
2828
for handler in self._pick_handlers:
29-
handle_event(handler, ColorPickEventArguments(sender=self, client=self.client, color=e.args))
29+
handle_event(handler, ColorPickEventArguments.from_generic_event(e, color=e.args))
3030
self.q_color = Element('q-color').on('change', handle_change)
3131

3232
def set_color(self, color: str) -> None:

nicegui/elements/echart.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,8 @@ def __init__(self,
5050
def on_point_click(self, callback: Handler[EChartPointClickEventArguments]) -> Self:
5151
"""Add a callback to be invoked when a point is clicked."""
5252
def handle_point_click(e: GenericEventArguments) -> None:
53-
handle_event(callback, EChartPointClickEventArguments(
54-
sender=self,
55-
client=self.client,
53+
handle_event(callback, EChartPointClickEventArguments.from_generic_event(
54+
e,
5655
component_type=e.args['componentType'],
5756
series_type=e.args['seriesType'],
5857
series_index=e.args['seriesIndex'],

nicegui/elements/interactive_image.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,8 @@ def on_mouse(self, on_mouse: Handler[MouseEventArguments]) -> Self:
7272
"""Add a callback to be invoked when a mouse event occurs."""
7373
def handle_mouse(e: GenericEventArguments) -> None:
7474
args = cast(dict, e.args)
75-
arguments = MouseEventArguments(
76-
sender=self,
77-
client=self.client,
75+
arguments = MouseEventArguments.from_generic_event(
76+
e,
7877
type=args.get('mouse_event_type', ''),
7978
image_x=args.get('image_x', 0.0),
8079
image_y=args.get('image_y', 0.0),

nicegui/elements/item.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def __init__(self, text: str = '', *, on_click: Optional[Handler[ClickEventArgum
3333
def on_click(self, callback: Handler[ClickEventArguments]) -> Self:
3434
"""Add a callback to be invoked when the List Item is clicked."""
3535
self._props['clickable'] = True # idempotent
36-
self.on('click', lambda _: handle_event(callback, ClickEventArguments(sender=self, client=self.client)))
36+
self.on('click', lambda e: handle_event(callback, ClickEventArguments.from_generic_event(e)))
3737
return self
3838

3939

nicegui/elements/joystick.py

+16-11
Original file line numberDiff line numberDiff line change
@@ -35,27 +35,32 @@ def __init__(self, *,
3535
self._move_handlers = [on_move] if on_move else []
3636
self._end_handlers = [on_end] if on_end else []
3737

38-
def handle_start() -> None:
38+
def handle_start(e: GenericEventArguments) -> None:
3939
self.active = True
40-
args = JoystickEventArguments(sender=self, client=self.client, action='start')
40+
args = JoystickEventArguments.from_generic_event(
41+
e,
42+
action='start',
43+
)
4144
for handler in self._start_handlers:
4245
handle_event(handler, args)
4346

4447
def handle_move(e: GenericEventArguments) -> None:
4548
if self.active:
46-
args = JoystickEventArguments(sender=self,
47-
client=self.client,
48-
action='move',
49-
x=float(e.args['data']['vector']['x']),
50-
y=float(e.args['data']['vector']['y']))
49+
args = JoystickEventArguments.from_generic_event(
50+
e,
51+
action='move',
52+
x=float(e.args['data']['vector']['x']),
53+
y=float(e.args['data']['vector']['y']),
54+
)
5155
for handler in self._move_handlers:
5256
handle_event(handler, args)
5357

54-
def handle_end() -> None:
58+
def handle_end(e: GenericEventArguments) -> None:
5559
self.active = False
56-
args = JoystickEventArguments(sender=self,
57-
client=self.client,
58-
action='end')
60+
args = JoystickEventArguments.from_generic_event(
61+
e,
62+
action='end',
63+
)
5964
for handler in self._end_handlers:
6065
handle_event(handler, args)
6166

nicegui/elements/keyboard.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,7 @@ def _handle_key(self, e: GenericEventArguments) -> None:
8888
code=e.args['code'],
8989
location=e.args['location'],
9090
)
91-
arguments = KeyEventArguments(
92-
sender=self,
93-
client=self.client,
94-
action=action,
95-
modifiers=modifiers,
96-
key=key,
97-
)
91+
arguments = KeyEventArguments.from_generic_event(e, action=action, modifiers=modifiers, key=key)
9892
for handler in self._key_handlers:
9993
handle_event(handler, arguments)
10094

nicegui/elements/mixins/selectable_element.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from ...binding import BindableProperty, bind, bind_from, bind_to
66
from ...element import Element
7-
from ...events import Handler, ValueChangeEventArguments, handle_event
7+
from ...events import GenericEventArguments, Handler, ValueChangeEventArguments, handle_event
88

99

1010
class SelectableElement(Element):
@@ -25,7 +25,14 @@ def __init__(self, *,
2525
self.selected = selected
2626
self._props['selected'] = selected
2727
self.set_selected(selected)
28-
self.on('update:selected', lambda e: self.set_selected(e.args))
28+
29+
self._current_socket_id = ''
30+
31+
def handle_selection_change(e: GenericEventArguments) -> None:
32+
self._current_socket_id = e.socket_id
33+
self.set_selected(e.args)
34+
self._current_socket_id = ''
35+
self.on('update:selected', handle_selection_change)
2936

3037
self._selection_change_handlers: List[Handler[ValueChangeEventArguments]] = []
3138
if on_selection_change:
@@ -104,6 +111,7 @@ def _handle_selection_change(self, selected: bool) -> None:
104111
"""
105112
self._props['selected'] = selected
106113
self.update()
107-
args = ValueChangeEventArguments(sender=self, client=self.client, value=self._props['selected'])
114+
args = ValueChangeEventArguments(sender=self, client=self.client, socket_id=self._current_socket_id,
115+
value=self._props['selected'])
108116
for handler in self._selection_change_handlers:
109117
handle_event(handler, args)

nicegui/elements/mixins/value_element.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,18 @@ def __init__(self, *,
3030
) -> None:
3131
super().__init__(**kwargs)
3232
self._send_update_on_value_change = True
33+
self._current_socket_id = ''
3334
self.set_value(value)
3435
self._props[self.VALUE_PROP] = self._value_to_model_value(value)
3536
self._props['loopback'] = self.LOOPBACK
3637
self._change_handlers: List[Handler[ValueChangeEventArguments]] = [on_value_change] if on_value_change else []
3738

3839
def handle_change(e: GenericEventArguments) -> None:
3940
self._send_update_on_value_change = self.LOOPBACK is True
41+
self._current_socket_id = e.socket_id
4042
self.set_value(self._event_args_to_value(e))
4143
self._send_update_on_value_change = True
44+
self._current_socket_id = ''
4245
self.on(f'update:{self.VALUE_PROP}', handle_change, [None], throttle=throttle)
4346

4447
def on_value_change(self, callback: Handler[ValueChangeEventArguments]) -> Self:
@@ -111,7 +114,8 @@ def _handle_value_change(self, value: Any) -> None:
111114
self._props[self.VALUE_PROP] = self._value_to_model_value(value)
112115
if self._send_update_on_value_change:
113116
self.update()
114-
args = ValueChangeEventArguments(sender=self, client=self.client, value=self._value_to_event_value(value))
117+
args = ValueChangeEventArguments(sender=self, client=self.client, socket_id=self._current_socket_id,
118+
value=self._value_to_event_value(value))
115119
for handler in self._change_handlers:
116120
handle_event(handler, args)
117121

nicegui/elements/notification.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def close_button(self, value: Union[bool, str]) -> None:
190190

191191
def on_dismiss(self, callback: Handler[UiEventArguments]) -> Self:
192192
"""Add a callback to be invoked when the notification is dismissed."""
193-
self.on('dismiss', lambda _: handle_event(callback, UiEventArguments(sender=self, client=self.client)), [])
193+
self.on('dismiss', lambda e: handle_event(callback, UiEventArguments.from_generic_event(e)), [])
194194
return self
195195

196196
def dismiss(self) -> None:

nicegui/elements/scene.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,8 @@ async def initialized(self) -> None:
183183
await event.wait()
184184

185185
def _handle_click(self, e: GenericEventArguments) -> None:
186-
arguments = SceneClickEventArguments(
187-
sender=self,
188-
client=self.client,
186+
arguments = SceneClickEventArguments.from_generic_event(
187+
e,
189188
click_type=e.args['click_type'],
190189
button=e.args['button'],
191190
alt=e.args['alt_key'],
@@ -204,9 +203,8 @@ def _handle_click(self, e: GenericEventArguments) -> None:
204203
handle_event(handler, arguments)
205204

206205
def _handle_drag(self, e: GenericEventArguments) -> None:
207-
arguments = SceneDragEventArguments(
208-
sender=self,
209-
client=self.client,
206+
arguments = SceneDragEventArguments.from_generic_event(
207+
e,
210208
type=e.args['type'],
211209
object_id=e.args['object_id'],
212210
object_name=e.args['object_name'],

nicegui/elements/scene_view.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,8 @@ async def initialized(self) -> None:
7373
await event.wait()
7474

7575
def _handle_click(self, e: GenericEventArguments) -> None:
76-
arguments = SceneClickEventArguments(
77-
sender=self,
78-
client=self.client,
76+
arguments = SceneClickEventArguments.from_generic_event(
77+
e,
7978
click_type=e.args['click_type'],
8079
button=e.args['button'],
8180
alt=e.args['alt_key'],

nicegui/elements/scroll_area.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ def on_scroll(self, callback: Handler[ScrollEventArguments]) -> Self:
3636
return self
3737

3838
def _handle_scroll(self, handler: Optional[Handler[ScrollEventArguments]], e: GenericEventArguments) -> None:
39-
handle_event(handler, ScrollEventArguments(
40-
sender=self,
41-
client=self.client,
39+
handle_event(handler, ScrollEventArguments.from_generic_event(
40+
e,
4241
vertical_position=e.args['verticalPosition'],
4342
vertical_percentage=e.args['verticalPercentage'],
4443
vertical_size=e.args['verticalSize'],

nicegui/elements/table.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,15 @@ def handle_selection(e: GenericEventArguments) -> None:
8484
else:
8585
self.selected = [row for row in self.selected if row[row_key] not in e.args['keys']]
8686
self.update()
87-
arguments = TableSelectionEventArguments(sender=self, client=self.client, selection=self.selected)
87+
arguments = TableSelectionEventArguments.from_generic_event(e, selection=self.selected)
8888
for handler in self._selection_handlers:
8989
handle_event(handler, arguments)
9090
self.on('selection', handle_selection, ['added', 'rows', 'keys'])
9191

9292
def handle_pagination_change(e: GenericEventArguments) -> None:
9393
self.pagination = e.args
9494
self.update()
95-
arguments = ValueChangeEventArguments(sender=self, client=self.client, value=self.pagination)
95+
arguments = ValueChangeEventArguments.from_generic_event(e, value=self.pagination)
9696
for handler in self._pagination_change_handlers:
9797
handle_event(handler, arguments)
9898
self.on('update:pagination', handle_pagination_change)

nicegui/elements/tree.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,19 @@ def update_prop(name: str, value: Any) -> None:
6464
def handle_selected(e: GenericEventArguments) -> None:
6565
update_prop('selected', e.args)
6666
for handler in self._select_handlers:
67-
handle_event(handler, ValueChangeEventArguments(sender=self, client=self.client, value=e.args))
67+
handle_event(handler, ValueChangeEventArguments.from_generic_event(e, value=e.args))
6868
self.on('update:selected', handle_selected)
6969

7070
def handle_expanded(e: GenericEventArguments) -> None:
7171
update_prop('expanded', e.args)
7272
for handler in self._expand_handlers:
73-
handle_event(handler, ValueChangeEventArguments(sender=self, client=self.client, value=e.args))
73+
handle_event(handler, ValueChangeEventArguments.from_generic_event(e, value=e.args))
7474
self.on('update:expanded', handle_expanded)
7575

7676
def handle_ticked(e: GenericEventArguments) -> None:
7777
update_prop('ticked', e.args)
7878
for handler in self._tick_handlers:
79-
handle_event(handler, ValueChangeEventArguments(sender=self, client=self.client, value=e.args))
79+
handle_event(handler, ValueChangeEventArguments.from_generic_event(e, value=e.args))
8080
self.on('update:ticked', handle_ticked)
8181

8282
def on_select(self, callback: Handler[ValueChangeEventArguments]) -> Self:

nicegui/elements/upload.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,15 @@ def handle_uploads(self, uploads: List[UploadFile]) -> None:
7777
handle_event(upload_handler, UploadEventArguments(
7878
sender=self,
7979
client=self.client,
80+
socket_id='', # TODO: can we do better here?
8081
content=upload.file,
8182
name=upload.filename or '',
8283
type=upload.content_type or '',
8384
))
8485
multi_upload_args = MultiUploadEventArguments(
8586
sender=self,
8687
client=self.client,
88+
socket_id='', # TODO: can we do better here?
8789
contents=[upload.file for upload in uploads],
8890
names=[upload.filename or '' for upload in uploads],
8991
types=[upload.content_type or '' for upload in uploads],
@@ -103,7 +105,7 @@ def on_multi_upload(self, callback: Handler[MultiUploadEventArguments]) -> Self:
103105

104106
def on_rejected(self, callback: Handler[UiEventArguments]) -> Self:
105107
"""Add a callback to be invoked when a file is rejected."""
106-
self.on('rejected', lambda: handle_event(callback, UiEventArguments(sender=self, client=self.client)), args=[])
108+
self.on('rejected', lambda e: handle_event(callback, UiEventArguments.from_generic_event(e)), args=[])
107109
return self
108110

109111
def reset(self) -> None:

0 commit comments

Comments
 (0)