Skip to content

Commit

Permalink
add socket_id to UIEventArguments; use new client.target where …
Browse files Browse the repository at this point in the history
…possible
  • Loading branch information
falkoschindler committed Feb 7, 2025
1 parent 5b42401 commit 08f9021
Show file tree
Hide file tree
Showing 25 changed files with 108 additions and 69 deletions.
2 changes: 1 addition & 1 deletion nicegui/air.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ def _handle_event(data: Dict[str, Any]) -> None:
arg0 = json.loads(args[0])
arg0['socket_id'] = client_id # HACK: translate socket_id of ui.scene's init event
args[0] = json.dumps(arg0)
client.handle_event(data['msg'])
client.handle_event(data['sid'], data['msg'])

@self.relay.on('javascript_response')
def _handle_javascript_response(data: Dict[str, Any]) -> None:
Expand Down
21 changes: 15 additions & 6 deletions nicegui/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,13 @@ def run_javascript(self, code: str, *, timeout: float = 1.0) -> AwaitableRespons
:return: AwaitableResponse that can be awaited to get the result of the JavaScript code
"""
request_id = str(uuid.uuid4())
target_id = self._temporary_socket_id or self.id
target_id = self.target

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

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

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

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

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

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

@property
def target(self) -> str:
"""Return the target of the client.
This is usually the client ID, but can be overridden by the ``individual_target`` context manager.
"""
return self._temporary_socket_id or self.id

@classmethod
async def prune_instances(cls) -> None:
"""Prune stale clients in an endless loop."""
Expand Down
4 changes: 2 additions & 2 deletions nicegui/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,10 +377,10 @@ def on(self,
self.update()
return self

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

def update(self) -> None:
Expand Down
2 changes: 1 addition & 1 deletion nicegui/elements/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def __init__(self,

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

def _text_to_model_text(self, text: str) -> None:
Expand Down
2 changes: 1 addition & 1 deletion nicegui/elements/button_dropdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def __init__(self,
self._props['split'] = True

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

def _text_to_model_text(self, text: str) -> None:
self._props['label'] = text
Expand Down
2 changes: 1 addition & 1 deletion nicegui/elements/chip.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@ def on_click(self, callback: Handler[ClickEventArguments]) -> Self:
"""Add a callback to be invoked when the chip is clicked."""
self._props['clickable'] = True
self.update()
self.on('click', lambda _: handle_event(callback, ClickEventArguments(sender=self, client=self.client)), [])
self.on('click', lambda e: handle_event(callback, ClickEventArguments.from_generic_event(e)), [])
return self
2 changes: 1 addition & 1 deletion nicegui/elements/color_picker.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __init__(self, *,
with self:
def handle_change(e: GenericEventArguments):
for handler in self._pick_handlers:
handle_event(handler, ColorPickEventArguments(sender=self, client=self.client, color=e.args))
handle_event(handler, ColorPickEventArguments.from_generic_event(e, color=e.args))
self.q_color = Element('q-color').on('change', handle_change)

def set_color(self, color: str) -> None:
Expand Down
5 changes: 2 additions & 3 deletions nicegui/elements/echart.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,8 @@ def __init__(self,
def on_point_click(self, callback: Handler[EChartPointClickEventArguments]) -> Self:
"""Add a callback to be invoked when a point is clicked."""
def handle_point_click(e: GenericEventArguments) -> None:
handle_event(callback, EChartPointClickEventArguments(
sender=self,
client=self.client,
handle_event(callback, EChartPointClickEventArguments.from_generic_event(
e,
component_type=e.args['componentType'],
series_type=e.args['seriesType'],
series_index=e.args['seriesIndex'],
Expand Down
5 changes: 2 additions & 3 deletions nicegui/elements/interactive_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,8 @@ def on_mouse(self, on_mouse: Handler[MouseEventArguments]) -> Self:
"""Add a callback to be invoked when a mouse event occurs."""
def handle_mouse(e: GenericEventArguments) -> None:
args = cast(dict, e.args)
arguments = MouseEventArguments(
sender=self,
client=self.client,
arguments = MouseEventArguments.from_generic_event(
e,
type=args.get('mouse_event_type', ''),
image_x=args.get('image_x', 0.0),
image_y=args.get('image_y', 0.0),
Expand Down
2 changes: 1 addition & 1 deletion nicegui/elements/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __init__(self, text: str = '', *, on_click: Optional[Handler[ClickEventArgum
def on_click(self, callback: Handler[ClickEventArguments]) -> Self:
"""Add a callback to be invoked when the List Item is clicked."""
self._props['clickable'] = True # idempotent
self.on('click', lambda _: handle_event(callback, ClickEventArguments(sender=self, client=self.client)))
self.on('click', lambda e: handle_event(callback, ClickEventArguments.from_generic_event(e)))
return self


Expand Down
27 changes: 16 additions & 11 deletions nicegui/elements/joystick.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,32 @@ def __init__(self, *,
self._move_handlers = [on_move] if on_move else []
self._end_handlers = [on_end] if on_end else []

def handle_start() -> None:
def handle_start(e: GenericEventArguments) -> None:
self.active = True
args = JoystickEventArguments(sender=self, client=self.client, action='start')
args = JoystickEventArguments.from_generic_event(
e,
action='start',
)
for handler in self._start_handlers:
handle_event(handler, args)

def handle_move(e: GenericEventArguments) -> None:
if self.active:
args = JoystickEventArguments(sender=self,
client=self.client,
action='move',
x=float(e.args['data']['vector']['x']),
y=float(e.args['data']['vector']['y']))
args = JoystickEventArguments.from_generic_event(
e,
action='move',
x=float(e.args['data']['vector']['x']),
y=float(e.args['data']['vector']['y']),
)
for handler in self._move_handlers:
handle_event(handler, args)

def handle_end() -> None:
def handle_end(e: GenericEventArguments) -> None:
self.active = False
args = JoystickEventArguments(sender=self,
client=self.client,
action='end')
args = JoystickEventArguments.from_generic_event(
e,
action='end',
)
for handler in self._end_handlers:
handle_event(handler, args)

Expand Down
8 changes: 1 addition & 7 deletions nicegui/elements/keyboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,7 @@ def _handle_key(self, e: GenericEventArguments) -> None:
code=e.args['code'],
location=e.args['location'],
)
arguments = KeyEventArguments(
sender=self,
client=self.client,
action=action,
modifiers=modifiers,
key=key,
)
arguments = KeyEventArguments.from_generic_event(e, action=action, modifiers=modifiers, key=key)
for handler in self._key_handlers:
handle_event(handler, arguments)

Expand Down
14 changes: 11 additions & 3 deletions nicegui/elements/mixins/selectable_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from ...binding import BindableProperty, bind, bind_from, bind_to
from ...element import Element
from ...events import Handler, ValueChangeEventArguments, handle_event
from ...events import GenericEventArguments, Handler, ValueChangeEventArguments, handle_event


class SelectableElement(Element):
Expand All @@ -25,7 +25,14 @@ def __init__(self, *,
self.selected = selected
self._props['selected'] = selected
self.set_selected(selected)
self.on('update:selected', lambda e: self.set_selected(e.args))

self._current_socket_id = ''

def handle_selection_change(e: GenericEventArguments) -> None:
self._current_socket_id = e.socket_id
self.set_selected(e.args)
self._current_socket_id = ''
self.on('update:selected', handle_selection_change)

self._selection_change_handlers: List[Handler[ValueChangeEventArguments]] = []
if on_selection_change:
Expand Down Expand Up @@ -104,6 +111,7 @@ def _handle_selection_change(self, selected: bool) -> None:
"""
self._props['selected'] = selected
self.update()
args = ValueChangeEventArguments(sender=self, client=self.client, value=self._props['selected'])
args = ValueChangeEventArguments(sender=self, client=self.client, socket_id=self._current_socket_id,
value=self._props['selected'])
for handler in self._selection_change_handlers:
handle_event(handler, args)
6 changes: 5 additions & 1 deletion nicegui/elements/mixins/value_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@ def __init__(self, *,
) -> None:
super().__init__(**kwargs)
self._send_update_on_value_change = True
self._current_socket_id = ''
self.set_value(value)
self._props[self.VALUE_PROP] = self._value_to_model_value(value)
self._props['loopback'] = self.LOOPBACK
self._change_handlers: List[Handler[ValueChangeEventArguments]] = [on_value_change] if on_value_change else []

def handle_change(e: GenericEventArguments) -> None:
self._send_update_on_value_change = self.LOOPBACK is True
self._current_socket_id = e.socket_id
self.set_value(self._event_args_to_value(e))
self._send_update_on_value_change = True
self._current_socket_id = ''
self.on(f'update:{self.VALUE_PROP}', handle_change, [None], throttle=throttle)

def on_value_change(self, callback: Handler[ValueChangeEventArguments]) -> Self:
Expand Down Expand Up @@ -111,7 +114,8 @@ def _handle_value_change(self, value: Any) -> None:
self._props[self.VALUE_PROP] = self._value_to_model_value(value)
if self._send_update_on_value_change:
self.update()
args = ValueChangeEventArguments(sender=self, client=self.client, value=self._value_to_event_value(value))
args = ValueChangeEventArguments(sender=self, client=self.client, socket_id=self._current_socket_id,
value=self._value_to_event_value(value))
for handler in self._change_handlers:
handle_event(handler, args)

Expand Down
2 changes: 1 addition & 1 deletion nicegui/elements/notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def close_button(self, value: Union[bool, str]) -> None:

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

def dismiss(self) -> None:
Expand Down
10 changes: 4 additions & 6 deletions nicegui/elements/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,8 @@ async def initialized(self) -> None:
await event.wait()

def _handle_click(self, e: GenericEventArguments) -> None:
arguments = SceneClickEventArguments(
sender=self,
client=self.client,
arguments = SceneClickEventArguments.from_generic_event(
e,
click_type=e.args['click_type'],
button=e.args['button'],
alt=e.args['alt_key'],
Expand All @@ -204,9 +203,8 @@ def _handle_click(self, e: GenericEventArguments) -> None:
handle_event(handler, arguments)

def _handle_drag(self, e: GenericEventArguments) -> None:
arguments = SceneDragEventArguments(
sender=self,
client=self.client,
arguments = SceneDragEventArguments.from_generic_event(
e,
type=e.args['type'],
object_id=e.args['object_id'],
object_name=e.args['object_name'],
Expand Down
5 changes: 2 additions & 3 deletions nicegui/elements/scene_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,8 @@ async def initialized(self) -> None:
await event.wait()

def _handle_click(self, e: GenericEventArguments) -> None:
arguments = SceneClickEventArguments(
sender=self,
client=self.client,
arguments = SceneClickEventArguments.from_generic_event(
e,
click_type=e.args['click_type'],
button=e.args['button'],
alt=e.args['alt_key'],
Expand Down
5 changes: 2 additions & 3 deletions nicegui/elements/scroll_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ def on_scroll(self, callback: Handler[ScrollEventArguments]) -> Self:
return self

def _handle_scroll(self, handler: Optional[Handler[ScrollEventArguments]], e: GenericEventArguments) -> None:
handle_event(handler, ScrollEventArguments(
sender=self,
client=self.client,
handle_event(handler, ScrollEventArguments.from_generic_event(
e,
vertical_position=e.args['verticalPosition'],
vertical_percentage=e.args['verticalPercentage'],
vertical_size=e.args['verticalSize'],
Expand Down
4 changes: 2 additions & 2 deletions nicegui/elements/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ def handle_selection(e: GenericEventArguments) -> None:
else:
self.selected = [row for row in self.selected if row[row_key] not in e.args['keys']]
self.update()
arguments = TableSelectionEventArguments(sender=self, client=self.client, selection=self.selected)
arguments = TableSelectionEventArguments.from_generic_event(e, selection=self.selected)
for handler in self._selection_handlers:
handle_event(handler, arguments)
self.on('selection', handle_selection, ['added', 'rows', 'keys'])

def handle_pagination_change(e: GenericEventArguments) -> None:
self.pagination = e.args
self.update()
arguments = ValueChangeEventArguments(sender=self, client=self.client, value=self.pagination)
arguments = ValueChangeEventArguments.from_generic_event(e, value=self.pagination)
for handler in self._pagination_change_handlers:
handle_event(handler, arguments)
self.on('update:pagination', handle_pagination_change)
Expand Down
6 changes: 3 additions & 3 deletions nicegui/elements/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,19 @@ def update_prop(name: str, value: Any) -> None:
def handle_selected(e: GenericEventArguments) -> None:
update_prop('selected', e.args)
for handler in self._select_handlers:
handle_event(handler, ValueChangeEventArguments(sender=self, client=self.client, value=e.args))
handle_event(handler, ValueChangeEventArguments.from_generic_event(e, value=e.args))
self.on('update:selected', handle_selected)

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

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

def on_select(self, callback: Handler[ValueChangeEventArguments]) -> Self:
Expand Down
4 changes: 3 additions & 1 deletion nicegui/elements/upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,15 @@ def handle_uploads(self, uploads: List[UploadFile]) -> None:
handle_event(upload_handler, UploadEventArguments(
sender=self,
client=self.client,
socket_id='', # TODO: can we do better here?
content=upload.file,
name=upload.filename or '',
type=upload.content_type or '',
))
multi_upload_args = MultiUploadEventArguments(
sender=self,
client=self.client,
socket_id='', # TODO: can we do better here?
contents=[upload.file for upload in uploads],
names=[upload.filename or '' for upload in uploads],
types=[upload.content_type or '' for upload in uploads],
Expand All @@ -103,7 +105,7 @@ def on_multi_upload(self, callback: Handler[MultiUploadEventArguments]) -> Self:

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

def reset(self) -> None:
Expand Down
Loading

0 comments on commit 08f9021

Please sign in to comment.