Skip to content

Commit

Permalink
A little cleanup from self-review
Browse files Browse the repository at this point in the history
  • Loading branch information
mofojed committed Nov 21, 2023
1 parent 1f486b7 commit c8cdf4d
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 21 deletions.
36 changes: 17 additions & 19 deletions plugins/ui/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -1436,13 +1436,13 @@ use_table_listener(

###### Parameters

| Parameter | Type | Description |
|---------------|--------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `table` | `Table` | The table to listen to. |
| `listener` | `Callable[[TableUpdate, bool], None] \| TableListener` | Either a function or a [TableListener](https://deephaven.io/core/pydoc/code/deephaven.table_listener.html#deephaven.table_listener.TableListener) with an on_update function. The function must take a [TableUpdate](https://deephaven.io/core/pydoc/code/deephaven.table_listener.html#deephaven.table_listener.TableUpdate) and is_replay bool. [More table listener info](https://deephaven.io/core/docs/how-to-guides/table-listeners-python/) |
| `description` | `str \| None` | An optional description for the UpdatePerformanceTracker to append to the listener’s entry description, default is None.
| `do_replay` | `bool` | Whether to replay the initial snapshot of the table, default is False. |
| `replay_lock` | `LockType` | The lock type used during replay, default is ‘shared’, can also be ‘exclusive’. |
| Parameter | Type | Description |
| ------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `table` | `Table` | The table to listen to. |
| `listener` | `Callable[[TableUpdate, bool], None] \| TableListener` | Either a function or a [TableListener](https://deephaven.io/core/pydoc/code/deephaven.table_listener.html#deephaven.table_listener.TableListener) with an on_update function. The function must take a [TableUpdate](https://deephaven.io/core/pydoc/code/deephaven.table_listener.html#deephaven.table_listener.TableUpdate) and is_replay bool. [More table listener info](https://deephaven.io/core/docs/how-to-guides/table-listeners-python/) |
| `description` | `str \| None` | An optional description for the UpdatePerformanceTracker to append to the listener’s entry description, default is None. |
| `do_replay` | `bool` | Whether to replay the initial snapshot of the table, default is False. |
| `replay_lock` | `LockType` | The lock type used during replay, default is ‘shared’, can also be ‘exclusive’. |

#### Custom Types

Expand Down Expand Up @@ -1515,7 +1515,6 @@ class LinkPoint(TypedDict):

|


#### Context

By default, the context of a `@ui.component` will be created per client session (same as [Parameterized Query's "parallel universe" today](https://github.com/deephaven-ent/iris/blob/868b868fc9e180ee948137b10b6addbac043605e/ParameterizedQuery/src/main/java/io/deephaven/query/parameterized/impl/ParameterizedQueryServerImpl.java#L140)). However, it would be interesting if it were possible to share a context among all sessions for the current user, and/or share a context with other users even; e.g. if one user selects and applies a filter, it updates immediately for all other users with that dashboard open. So three cases:
Expand Down Expand Up @@ -1650,15 +1649,15 @@ sequenceDiagram
UIP->>SP: Render tft
SP->>SP: Run sym_exchange
Note over SP: sym_exchange executes, running text_filter_table twice
SP-->>UIP: Result (flex([tft1, tft2]))
UIP-->>W: Display (flex([tft1, tft2]))
SP-->>UIP: Result (document=flex([tft1, tft2]), exported_objects=[tft1, tft2])
UIP-->>W: Display Result
U->>UIP: Change text input 1
UIP->>SP: Change state
SP->>SP: Run sym_exchange
Note over SP: sym_exchange executes, text_filter_table only <br/>runs once for the one changed input
SP-->>UIP: Result (flex([tft1', tft2]))
UIP-->>W: Display (flex([tft1', tft2]))
Note over SP: sym_exchange executes, text_filter_table only <br/>runs once for the one changed input<br/>only exports the new table, as client already has previous tables
SP-->>UIP: Result (document=flex([tft1', tft2], exported_objects=[tft1']))
UIP-->>W: Display Result
```

##### Communication/Callbacks
Expand All @@ -1685,12 +1684,11 @@ sequenceDiagram

A component that is created on the server side runs through a few steps before it is rendered on the client side:

1. Element - The basis for all UI components. Generally a `FunctionElement`, and does not run the function until it is requested by the UI. The result can change depending on the context that it is rendered in (e.g. what "state" is set).
2. RenderedNode - After an element has been rendered using a renderer, it becomes a `RenderedNode`. This is an immutable representation of the document.
3. JSONEncodedNode - The `RenderedNode` is then encoded into JSON using `NodeEncoder`. It pulls out all the objects and maps them to exported objects, and all the callables to be mapped to commands that can be accepted by JSON-RPC. This is the final representation of the document that is sent to the client.
4. ElementPanel - Client side where it's receiving the `documentUpdated` from the server plugin, and then rendering the `JSONEncodedNode` into a `ElementPanel` (e.g. a `GoldenLayout` panel). Decodes the JSON, maps all the exported objects to the actual objects, and all the callables to async methods that will call to the server.
5. ElementView - Renders the decoded panel into the UI. Picks the element based on the name of it.
6. ObjectView - Render an exported object
1. [Element](./src/deephaven/ui/elements/Element.py) - The basis for all UI components. Generally a [FunctionElement](./src/deephaven/ui/elements/FunctionElement.py) created by a script using the [@ui.component](./src/deephaven/ui/components/make_component.py) decorator, and does not run the function until it is rendered. The result can change depending on the context that it is rendered in (e.g. what "state" is set).
2. [ElementMessageStream](./src/deephaven/ui/object_types/ElementMessageStream.py) - The `ElementMessageStream` is responsible for rendering one instance of an element in a specific rendering context and handling the server-client communication. The element is rendered to create a [RenderedNode](./src/deephaven/ui/renderer/RenderedNode.py), which is an immutable representation of a rendered document. The `RenderedNode` is then encoded into JSON using [NodeEncoder](./src/deephaven/ui/renderer/NodeEncoder.py), which pulls out all the non-serializable objects (such as Tables) and maps them to exported objects, and all the callables to be mapped to commands that can be accepted by JSON-RPC. This is the final representation of the document that is sent to the client, and ultimately handled by the `WidgetHandler`.
3. [DashboardPlugin](./src/js/src/DashboardPlugin.tsx) - Client side `DashboardPlugin` that listens for when a widget of type `Element` is opened, and manage the `WidgetHandler` instances that are created for each widget.
4. [WidgetHandler](./src/js/src/WidgetHandler.tsx) - Uses JSON-RPC communication with an `ElementMessageStream` instance to load the initial rendered document and associated exported objects. Listens for any changes and updates the document accordingly.
5. [DocumentHandler](./src/js/src/DocumentHandler.tsx) - Handles the root of a rendered document, laying out the appropriate panels or dashboard specified.

#### Other Decisions

Expand Down
9 changes: 7 additions & 2 deletions plugins/ui/src/deephaven/ui/components/make_component.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import functools
import logging
from typing import Any, Callable
from .._internal import get_component_qualname
from ..elements import FunctionElement

logger = logging.getLogger(__name__)


def make_component(func):
def make_component(func: Callable[..., Any]):
"""
Create a FunctionalElement from the passed in function.
Args:
func: The function to create a FunctionalElement from.
Runs when the component is being rendered.
"""

@functools.wraps(func)
def make_component_node(*args, **kwargs):
def make_component_node(*args: Any, **kwargs: Any):
component_type = get_component_qualname(func)

return FunctionElement(component_type, lambda: func(*args, **kwargs))
Expand Down
30 changes: 30 additions & 0 deletions plugins/ui/src/deephaven/ui/object_types/ElementMessageStream.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,36 @@


class ElementMessageStream(MessageStream):
_manager: JSONRPCResponseManager
"""
Handle incoming requests from the client.
"""

_dispatcher: Dispatcher
"""
The dispatcher to use when client calls callables.
"""

_encoder: NodeEncoder
"""
Encoder to use to encode the document.
"""

_message_id: int
"""
The next message ID to use.
"""

_element: Element
"""
The element to render.
"""

_connection: MessageStream
"""
The connection to send the rendered element to.
"""

def __init__(self, element: Element, connection: MessageStream):
"""
Create a new ElementMessageStream. Renders the element in a render context, and sends the rendered result to the
Expand Down

0 comments on commit c8cdf4d

Please sign in to comment.