From bc87f4ed7a0b04f2a6a565283eac563c23332050 Mon Sep 17 00:00:00 2001 From: dsmmcken Date: Wed, 24 Apr 2024 12:03:18 -0400 Subject: [PATCH 1/7] docs: start plotting docs including one-click behaviour - Reename examples as docs - Create a dh.ui plotting doc - Rename assets as _assets for folder sort order - Change links from assets to _assets - Prettier changes on md files touched --- plugins/ui/DESIGN.md | 100 ++++++++++-------- plugins/ui/README.md | 2 +- plugins/ui/docs/Plotting.md | 100 ++++++++++++++++++ plugins/ui/{examples => docs}/README.md | 50 ++++----- .../assets => docs/_assets}/button_events.png | Bin .../_assets}/change_monitor.png | Bin .../assets => docs/_assets}/checkbox.png | Bin .../assets => docs/_assets}/counter.png | Bin .../assets => docs/_assets}/docker.png | Bin .../assets => docs/_assets}/double-tft.png | Bin .../assets => docs/_assets}/double_table.png | Bin .../assets => docs/_assets}/foo_bar.png | Bin .../assets => docs/_assets}/form.png | Bin .../assets => docs/_assets}/form_submit.png | Bin .../_assets}/handling_events.png | Bin .../assets => docs/_assets}/hello_world.png | Bin .../_assets}/multiwave_dashboard.png | Bin .../assets => docs/_assets}/my_dash.png | Bin .../assets => docs/_assets}/picker.png | Bin .../assets => docs/_assets}/range_table.png | Bin .../assets => docs/_assets}/shared_state.png | Bin .../assets => docs/_assets}/stock_rollup.png | Bin .../_assets}/stock_widget_plot.png | Bin .../_assets}/stock_widget_table_invalid.png | Bin .../_assets}/stock_widget_table_valid.png | Bin .../assets => docs/_assets}/table_events.png | Bin .../assets => docs/_assets}/table_hooks.png | Bin .../assets => docs/_assets}/text_field.png | Bin .../_assets}/text_filter_table.png | Bin .../assets => docs/_assets}/wave_input.png | Bin .../assets => docs/_assets}/waves.png | Bin .../_assets}/waves_with_plot.png | Bin 32 files changed, 180 insertions(+), 72 deletions(-) create mode 100644 plugins/ui/docs/Plotting.md rename plugins/ui/{examples => docs}/README.md (96%) rename plugins/ui/{examples/assets => docs/_assets}/button_events.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/change_monitor.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/checkbox.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/counter.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/docker.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/double-tft.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/double_table.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/foo_bar.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/form.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/form_submit.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/handling_events.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/hello_world.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/multiwave_dashboard.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/my_dash.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/picker.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/range_table.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/shared_state.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/stock_rollup.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/stock_widget_plot.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/stock_widget_table_invalid.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/stock_widget_table_valid.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/table_events.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/table_hooks.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/text_field.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/text_filter_table.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/wave_input.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/waves.png (100%) rename plugins/ui/{examples/assets => docs/_assets}/waves_with_plot.png (100%) diff --git a/plugins/ui/DESIGN.md b/plugins/ui/DESIGN.md index cbdb538bb..a50bc1912 100644 --- a/plugins/ui/DESIGN.md +++ b/plugins/ui/DESIGN.md @@ -1045,11 +1045,11 @@ ui.section( ###### Parameters -| Parameter | Type | Description | -| ----------- | ------- | ----------------------------------------- | -| `*children` | `Item` | The options to render within the section. | +| Parameter | Type | Description | +| ----------- | ------------- | ----------------------------------------- | +| `*children` | `Item` | The options to render within the section. | | `title` | `str \| None` | The title of the section. | -| `**props` | `Any` | Any other Section prop | +| `**props` | `Any` | Any other Section prop | ##### ui.picker @@ -1189,6 +1189,7 @@ picker7 = ui.picker( ``` ###### ui.list_action_group + A group of action buttons that can be used to create a list of actions. This component should be used within the actions prop of a `ListView` component. @@ -1202,16 +1203,16 @@ def list_action_group( ``` ###### Parameters -| Parameter | Type | Description | -|-------------------------|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| -| `*children` | `ActionGroupItem` | The actions to render within the action group. | -| `on_action` | `Callable[[ActionKey, Key], None] \| None` | Handler that is called when an item is pressed. The first argument is the key of the action, the second argument is the key of the list_view item. | -| `on_selection_change` | `Callable[[Selection, Key], None] \| None` | Handler that is called when the selection changes. The first argument is the selection, the second argument is the key of the list_view item. | -| `**props` | `Any` | Any other [ActionGroup](https://react-spectrum.adobe.com/react-spectrum/ActionGroup.html) prop. | - +| Parameter | Type | Description | +| --------------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| `*children` | `ActionGroupItem` | The actions to render within the action group. | +| `on_action` | `Callable[[ActionKey, Key], None] \| None` | Handler that is called when an item is pressed. The first argument is the key of the action, the second argument is the key of the list_view item. | +| `on_selection_change` | `Callable[[Selection, Key], None] \| None` | Handler that is called when the selection changes. The first argument is the selection, the second argument is the key of the list_view item. | +| `**props` | `Any` | Any other [ActionGroup](https://react-spectrum.adobe.com/react-spectrum/ActionGroup.html) prop. | ###### ui.list_action_menu + A group of action buttons that can be used to create a list of actions. This component should be used within the actions prop of a `ListView` component. @@ -1225,17 +1226,20 @@ def list_action_menu( ``` ###### Parameters -| Parameter | Type | Description | -|-------------------------|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------| -| `*children` | `ActionMenuItem` | The options to render within the picker. | -| `on_action` | `Callable[[ActionKey, Key], None] \| None` | Handler that is called when an item is pressed. The first argument is the key of the action, the second argument is the key of the list_view item. | -| `on_open_change` | `Callable[[bool, Key], None] \| None` | The first argument is a boolean indicating if the menu is open, the second argument is the key of the list_view item. | -| `**props` | `Any` | Any other [ActionMenu](https://react-spectrum.adobe.com/react-spectrum/ActionMenu.html) prop. | + +| Parameter | Type | Description | +| ---------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| `*children` | `ActionMenuItem` | The options to render within the picker. | +| `on_action` | `Callable[[ActionKey, Key], None] \| None` | Handler that is called when an item is pressed. The first argument is the key of the action, the second argument is the key of the list_view item. | +| `on_open_change` | `Callable[[bool, Key], None] \| None` | The first argument is a boolean indicating if the menu is open, the second argument is the key of the list_view item. | +| `**props` | `Any` | Any other [ActionMenu](https://react-spectrum.adobe.com/react-spectrum/ActionMenu.html) prop. | ###### ui.list_view -A list view that can be used to create a list of items. Children should be one of two types: -1. If children are of type `Item`, they are the list items. -2. If children are of type `Table`, the values in the table are the list items. There can only be one child, the `Table`. + +A list view that can be used to create a list of items. Children should be one of two types: + +1. If children are of type `Item`, they are the list items. +2. If children are of type `Table`, the values in the table are the list items. There can only be one child, the `Table`. ```py import deephaven.ui as ui @@ -1249,28 +1253,28 @@ ui.list_view( default_selected_keys: Selection | None = None, selected_keys: Selection | None = None, render_empty_state: Element | None = None, - on_selection_change: Callable[[Selection], None] | None = None, + on_selection_change: Callable[[Selection], None] | None = None, on_change: Callable[[Selection], None] | None = None, **props: Any ) -> ListViewElement ``` ###### Parameters -| Parameter | Type | Description | -|-------------------------|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `*children` | `Item \| Table` | The options to render within the list_view. | -| `key_column` | `ColumnName \| None` | Only valid if children are of type `Table`. The column of values to use as item keys. Defaults to the first column. | -| `label_column` | `ColumnName \| None` | Only valid if children are of type `Table`. The column of values to display as primary text. Defaults to the `key_column` value. | -| `description_column` | `ColumnName \| None` | Only valid if children are of type `Table`. The column of values to display as descriptions. | -| `icon_column` | `ColumnName \| None` | Only valid if children are of type `Table`. The column of values to map to icons. | -| `actions` | `ListActionGroupElement \| ListActionMenuElement \| None` | Only valid if children are of type Table. The action group or menus to render for all elements within the list view. | -| `default_selected_keys` | `Selection \| None` | The initial selected keys in the collection (uncontrolled). | -| `selected_keys` | `Selection \| None` | The currently selected keys in the collection (controlled). | -| `render_empty_state` | `Element \| None` | Sets what the `list_view` should render when there is no content to display. | -| `on_selection_change` | `Callable[[Selection], None] \| None` | Handler that is called when the selections changes. | -| `on_change` | `Callable[[Selection], None] \| None` | Alias of `on_selection_change`. Handler that is called when the selections changes. | -| `**props` | `Any` | Any other [ListView](https://react-spectrum.adobe.com/react-spectrum/ListView.html) prop, with the exception of `items`, `dragAndDropHooks`, and `onLoadMore`. | +| Parameter | Type | Description | +| ----------------------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `*children` | `Item \| Table` | The options to render within the list_view. | +| `key_column` | `ColumnName \| None` | Only valid if children are of type `Table`. The column of values to use as item keys. Defaults to the first column. | +| `label_column` | `ColumnName \| None` | Only valid if children are of type `Table`. The column of values to display as primary text. Defaults to the `key_column` value. | +| `description_column` | `ColumnName \| None` | Only valid if children are of type `Table`. The column of values to display as descriptions. | +| `icon_column` | `ColumnName \| None` | Only valid if children are of type `Table`. The column of values to map to icons. | +| `actions` | `ListActionGroupElement \| ListActionMenuElement \| None` | Only valid if children are of type Table. The action group or menus to render for all elements within the list view. | +| `default_selected_keys` | `Selection \| None` | The initial selected keys in the collection (uncontrolled). | +| `selected_keys` | `Selection \| None` | The currently selected keys in the collection (controlled). | +| `render_empty_state` | `Element \| None` | Sets what the `list_view` should render when there is no content to display. | +| `on_selection_change` | `Callable[[Selection], None] \| None` | Handler that is called when the selections changes. | +| `on_change` | `Callable[[Selection], None] \| None` | Alias of `on_selection_change`. Handler that is called when the selections changes. | +| `**props` | `Any` | Any other [ListView](https://react-spectrum.adobe.com/react-spectrum/ListView.html) prop, with the exception of `items`, `dragAndDropHooks`, and `onLoadMore`. | ```py import deephaven.ui as ui @@ -1355,19 +1359,22 @@ list_view7 = ui.list_view( ``` ###### ui.date_picker -A date picker that can be used to select a date. + +A date picker that can be used to select a date. There are three types that can be passed in to the props that control the date format: + 1. `LocalDate`: A LocalDate is a date without a time zone in the ISO-8601 system, such as "2007-12-03" or "2057-01-28". -This will create a date picker with a granularity of days. + This will create a date picker with a granularity of days. 2. `Instant`: An Instant represents an unambiguous specific point on the timeline, such as 2021-04-12T14:13:07 UTC. -This will create a date picker with a granularity of seconds in UTC. + This will create a date picker with a granularity of seconds in UTC. 3. `ZonedDateTime`: A ZonedDateTime represents an unambiguous specific point on the timeline with an associated time zone, such as 2021-04-12T14:13:07 America/New_York. -This will create a date picker with a granularity of seconds in the specified time zone. + This will create a date picker with a granularity of seconds in the specified time zone. -The format of the date picker and the type of the value passed to the `on_change` handler +The format of the date picker and the type of the value passed to the `on_change` handler is determined by the type of the following props in order of precedence: -1. `value` + +1. `value` 2. `default_value` 3. `placeholder_value` @@ -1389,8 +1396,9 @@ ui.date_picker( ``` ###### Parameters + | Parameter | Type | Description | -|----------------------|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| -------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `placeholder_value` | `Date \| None` | A placeholder date that influences the format of the placeholder shown when no value is selected. Defaults to today at midnight in the user's timezone. | | `value` | `Date \| None` | The current value (controlled). | | `default_value` | `Date \| None` | The default value (uncontrolled). | @@ -1516,7 +1524,7 @@ ui.combo_box( ###### Parameters | Parameter | Type | Description | -| ---------------------- | ----------------------------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ---------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `*children` | `Item \| SectionElement \| Table \| PartitionedTable` | The options to render within the combo_box. | | `key_column` | `ColumnName \| None` | Only valid if children are of type `Table` or `PartitionedTable`. The column of values to use as item keys. Defaults to the first column. | | `label_column` | `ColumnName \| None` | Only valid if children are of type `Table` or `PartitionedTable`. The column of values to display as primary text. Defaults to the `key_column` value. | @@ -1530,7 +1538,7 @@ ui.combo_box( | `on_input_change` | `Callable[[str], None] \| None` | Handler that is called when the search input value changes. | | `on_selection_change` | `Callable[[Key], None] \| None` | Handler that is called when the selection changes. | | `on_change` | `Callable[[Key], None] \| None` | Alias of `on_selection_change`. Handler that is called when the selection changes. | -| `on_open_change` | `Callable[[bool, MenuTriggerAction], None] \| None` | Method that is called when the open state of the menu changes. Returns the new open state and the action that caused the opening of the menu. | +| `on_open_change` | `Callable[[bool, MenuTriggerAction], None] \| None` | Method that is called when the open state of the menu changes. Returns the new open state and the action that caused the opening of the menu. | | `**props` | `Any` | Any other [Combo_Box](https://react-spectrum.adobe.com/react-spectrum/ComboBox.html) prop, with the exception of `items`, `validate`, `errorMessage` (as a callback) and `onLoadMore` | ```py @@ -2186,7 +2194,7 @@ ListViewItem = Stringable | ItemElement LocalDateConvertible = Union[None, LocalDate, str, datetime.date, datetime.datetime, numpy.datetime64, pandas.Timestamp] InstantConvertible = Union[None, Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp] ZonedDateTimeConvertible = Union[None, ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp] -Date = Instant | LocalDate | ZonedDateTime | LocalDateConvertible | InstantConvertible | ZonedDateTimeConvertible +Date = Instant | LocalDate | ZonedDateTime | LocalDateConvertible | InstantConvertible | ZonedDateTimeConvertible Granularity = Literal["DAY", "HOUR", "MINUTE", "SECOND"] MenuTriggerAction = Literal["FOCUS", "INPUT", "MANUAL"] @@ -2358,7 +2366,7 @@ tft = double_text_filter_table(_stocks) Which should result in a UI like this: -![Double Text Filter Tables](examples/assets/double-tft.png) +![Double Text Filter Tables](docs/_assets/double-tft.png) How does that look when the notebook is executed? When does each code block execute? diff --git a/plugins/ui/README.md b/plugins/ui/README.md index 7dd21cfdc..25bc1f55c 100644 --- a/plugins/ui/README.md +++ b/plugins/ui/README.md @@ -32,7 +32,7 @@ tox -e py ## Usage -Once you have the JS and python plugins installed and the server started, you can use deephaven.ui. See [examples](examples/README.md) for examples. +Once you have the JS and python plugins installed and the server started, you can use deephaven.ui. See [examples](docs/README.md) for examples. ## Logging diff --git a/plugins/ui/docs/Plotting.md b/plugins/ui/docs/Plotting.md new file mode 100644 index 000000000..23c219fac --- /dev/null +++ b/plugins/ui/docs/Plotting.md @@ -0,0 +1,100 @@ +--- +title: Plotting and dh.ui +--- + +Creating dynamic plots that respond to user input is a common task in data analysis. The `dh.ui` module provides a simple interface for creating interactive plots using the `deephaven-express` library. This guide will show you how to create plots that updates based on user input. + +## Plotting a filtered table + +This example demonstrates how to create a simple line plot that updates based on user input. The plot will display the price of a stock filtered based on the stock symbol entered by the user. Here we have used a `ui.text_field` to get the value, but it could be driven by any dh.ui input, including double clicking on a value from a `ui.table`. We've previously referred to this sort of behaviour as a "one-click" component in enterprise, as the plot updates as soon as the user enters a filter. + +```python +import deephaven.plot.express as dx +import deephaven.ui as ui + +_stocks = dx.data.stocks() + +@ui.component +def plot_filtered_table(table, inital_value): + text, set_text = ui.use_state("DOG") + # the filter is memoized so that it is only recalculated when the text changes + filtered_table = ui.use_memo( + lambda: table.where(f"sym = `{text.upper()}`"), [table, text] + ) + return [ + ui.text_field(value=text, on_change=set_text), + dx.line(filtered_table, x="timestamp", y="price", title=f"Filtered by: {text}"), + ] + + +p = plot_filtered_table(_stocks, "DOG") +``` + +## Plotting a paritioned table + +Using a partitioned table, as opposed to a where, can be more efficient if you are going to be filtering the same table multiple times with different values. This is because the partitioning is only done once, and then the key is selected from the partitioned table. Compared to using a where statement, it can be faster to return results, but at the expense of the query engine using more memory. Depending on the size of your table and the number of unique values in the partition key, this can be a tradeoff worth making or not. + +```python +import deephaven.plot.express as dx +import deephaven.ui as ui + +_stocks = dx.data.stocks() + +@ui.component +def plot_paritioned_table(table, inital_value): + text, set_text = ui.use_state(inital_value) + # memoize the parition by so that it only performed once + paritioned_table = ui.use_memo(lambda: table.partition_by(["sym"]), [table]) + constituent_table = ui.use_memo( + lambda: paritioned_table.get_constituent(text.upper()), [paritioned_table, text] + ) + return [ + ui.text_field(value=text, on_change=set_text), + # only attempt to plot valid parition keys + dx.line( + constituent_table, x="timestamp", y="price", title=f"Parition key: {text}" + ) + if constituent_table != None + else ui.text("Please enter a valid parition."), + ] + + +p = plot_paritioned_table(_stocks, "DOG") +``` + +## Combining a fitler and a parition by + +Deephaven express allows you to plot by a partition and assign unique colors to each key. Sometimes as a user you may also want to filter the data in addition to partitioning it. We've previously referred to this as a "one-click plot by" behaviour in enterprise. This can be done by either filtering the table first and then partitioning it, or partitioning it first and then filtering it. The choice of which to use depends on the size of the table and the number of unique values in the partition key. The first example is more like a traditional "one-click" component, and the second is more like a parameterized query. Both will give you the same result, but the first one may return results faster, whereas the second one may be more memory efficient. + +```python +import deephaven.plot.express as dx +import deephaven.ui as ui + +_stocks = dx.data.stocks() + +# component using a parition_by[a,b] once, then filter on a +@ui.component +def parition_then_filter(table, by, inital_value): + text, set_text = ui.use_state(inital_value) + paritioned_table = ui.use_memo(lambda: table.partition_by(by),[table, by]) + filtered = ui.use_memo(lambda: paritioned_table.filter(f"{by[0]} = `{text.upper()}`"), [text, paritioned_table]) + return [ + ui.text_field(value=text, on_change=set_text), + dx.line(filtered, x="timestamp", y="price", by=[f"{by[1]}"]) + ] + +# component using a where on a, then re-paritions on b +@ui.component +def where_then_parition(table, by, inital_value): + text, set_text = ui.use_state(inital_value) + filtered = ui.use_memo(lambda: table.where(f"{by[0]} = `{text.upper()}`"),[text, table]) + return [ + ui.text_field(value=text, on_change=set_text), + dx.line(filtered, x="timestamp", y="price", by=[f"{by[1]}"]) + ] + + +# outputs the same thing, done two different ways depending on how you want the work done +ptf = parition_then_filter(_stocks,["sym", "exchange"], "DOG") +wtp = where_then_parition(_stocks,["sym", "exchange"], "DOG") +``` diff --git a/plugins/ui/examples/README.md b/plugins/ui/docs/README.md similarity index 96% rename from plugins/ui/examples/README.md rename to plugins/ui/docs/README.md index e1e4002b7..a385611b8 100644 --- a/plugins/ui/examples/README.md +++ b/plugins/ui/docs/README.md @@ -11,7 +11,7 @@ docker run --rm --name deephaven-ui -p 10000:10000 --pull=always ghcr.io/deephav ``` You'll need to find the link to open the UI in the Docker logs: -![docker](assets/docker.png) +![docker](_assets/docker.png) # Using components @@ -27,7 +27,7 @@ The `ui` package contains many _components_, which you can display in the UI: hello_world = ui.heading("Hello World!") ``` -![Basic Hello World example.](assets/hello_world.png) +![Basic Hello World example.](_assets/hello_world.png) By assigning the component to the `hello_world` variable, it displays in the UI in a panel named `hello_world`. @@ -39,7 +39,7 @@ Write functions to handle events. To write a button that will print event detail my_button = ui.button("Click Me!", on_press=lambda e: print(f"Button was clicked! {e}")) ``` -![Whenever the button is pressed, event details are printed to the console.](assets/handling_events.png) +![Whenever the button is pressed, event details are printed to the console.](_assets/handling_events.png) ## Creating components @@ -59,7 +59,7 @@ def ui_foo_bar(): foo_bar = ui_foo_bar() ``` -![Custom component being displayed.](assets/foo_bar.png) +![Custom component being displayed.](_assets/foo_bar.png) ## Using state @@ -98,7 +98,7 @@ c1 = ui_counter() c2 = ui_counter() ``` -![Each counter has its own state.](assets/counter.png) +![Each counter has its own state.](_assets/counter.png) > [!NOTE] > Functions are prefixed with `use_` are called _hooks_. `use_state` is built-in to deephaven.ui, and there are other hooks built-in shown below. You can also create your own hooks. @@ -160,7 +160,7 @@ def ui_shared_state(): shared_state = ui_shared_state() ``` -![Buttons will always be in sync with shared state.](assets/shared_state.png) +![Buttons will always be in sync with shared state.](_assets/shared_state.png) # Examples @@ -181,7 +181,7 @@ def ui_input(): my_input = ui_input() ``` -![Text field.](assets/text_field.png) +![Text field.](_assets/text_field.png) ## Checkbox (boolean) @@ -201,7 +201,7 @@ def ui_checkbox(): my_checkbox = ui_checkbox() ``` -![Checkbox](assets/checkbox.png) +![Checkbox](_assets/checkbox.png) ## Picker (string values) @@ -238,7 +238,7 @@ def ui_picker(): my_picker = ui_picker() ``` -![Use a picker to select from a list of items](assets/picker.png) +![Use a picker to select from a list of items](_assets/picker.png) ## Form (two variables) @@ -261,7 +261,7 @@ def ui_form(): my_form = ui_form() ``` -![Form with multiple inputs.](assets/form.png) +![Form with multiple inputs.](_assets/form.png) ## Form with submit @@ -284,7 +284,7 @@ def ui_form_submit(): my_form_submit = ui_form_submit() ``` -![Submitting a form and printing out the data.](assets/form_submit.png) +![Submitting a form and printing out the data.](_assets/form_submit.png) ## Button events @@ -320,7 +320,7 @@ def ui_button_events(): my_button_events = ui_button_events() ``` -![Print the details of all events when pressing a button.](assets/button_events.png) +![Print the details of all events when pressing a button.](_assets/button_events.png) # Data Examples @@ -352,7 +352,7 @@ def ui_text_filter_table(source, column): my_text_filter_table = ui_text_filter_table(stocks, "sym") ``` -![Table with a text field for filtering.](assets/text_filter_table.png) +![Table with a text field for filtering.](_assets/text_filter_table.png) ## Table with range filter @@ -415,9 +415,9 @@ def ui_stock_widget_table(source, default_sym="", default_exchange=""): my_stock_widget_table = ui_stock_widget_table(stocks, "", "") ``` -![Stock Widget Table Invalid Input](assets/stock_widget_table_invalid.png) +![Stock Widget Table Invalid Input](_assets/stock_widget_table_invalid.png) -![Stock Widget Table Valid Input](assets/stock_widget_table_valid.png) +![Stock Widget Table Valid Input](_assets/stock_widget_table_valid.png) ## Plot with filters @@ -451,7 +451,7 @@ def ui_stock_widget_plot(source, default_sym="", default_exchange=""): my_stock_widget_plot = ui_stock_widget_plot(stocks, "CAT", "TPET") ``` -![Stock Widget Plot](assets/stock_widget_plot.png) +![Stock Widget Plot](_assets/stock_widget_plot.png) # Dashboard Examples @@ -513,7 +513,7 @@ my_dash = ui.dashboard( ) ``` -![Stock Dashboard](assets/my_dash.png) +![Stock Dashboard](_assets/my_dash.png) ## Custom Components Dashboard @@ -611,7 +611,7 @@ def multiwave(): mw = ui.dashboard(multiwave()) ``` -![Multiwave Dashboard](assets/multiwave_dashboard.png) +![Multiwave Dashboard](_assets/multiwave_dashboard.png) # Other Examples @@ -666,7 +666,7 @@ def waves(): w = waves() ``` -![Waves](assets/waves.png) +![Waves](_assets/waves.png) ## Custom hook @@ -730,7 +730,7 @@ def waves(): w = waves() ``` -![Wave Input](assets/wave_input.png) +![Wave Input](_assets/wave_input.png) We can then re-use that hook to make a component that displays a plot as well: @@ -762,7 +762,7 @@ def waves_with_plot(): wp = waves_with_plot() ``` -![Waves with plot](assets/waves_with_plot.png) +![Waves with plot](_assets/waves_with_plot.png) ## Using Panels @@ -910,7 +910,7 @@ def double_table(source): dt = double_table(stocks) ``` -![Double Table](assets/double_table.png) +![Double Table](_assets/double_table.png) ## Stock rollup @@ -994,7 +994,7 @@ def stock_table(source): st = stock_table(stocks) ``` -![Stock Rollup](assets/stock_rollup.png) +![Stock Rollup](_assets/stock_rollup.png) ## Listening to Table Updates @@ -1092,7 +1092,7 @@ Without the `use_liveness_scope` wrapping the lamdba, the newly created live tab For more information on liveness scopes and why they are needed, see the [liveness scope documentation](https://deephaven.io/core/docs/conceptual/liveness-scope-concept/). -![Change Monitor](assets/change_monitor.png) +![Change Monitor](_assets/change_monitor.png) ## Tabs @@ -1194,7 +1194,7 @@ def watch_lizards(source: Table): watch = watch_lizards(stocks) ``` -![Table Hooks](assets/table_hooks.png) +![Table Hooks](_assets/table_hooks.png) ## Multi-threading diff --git a/plugins/ui/examples/assets/button_events.png b/plugins/ui/docs/_assets/button_events.png similarity index 100% rename from plugins/ui/examples/assets/button_events.png rename to plugins/ui/docs/_assets/button_events.png diff --git a/plugins/ui/examples/assets/change_monitor.png b/plugins/ui/docs/_assets/change_monitor.png similarity index 100% rename from plugins/ui/examples/assets/change_monitor.png rename to plugins/ui/docs/_assets/change_monitor.png diff --git a/plugins/ui/examples/assets/checkbox.png b/plugins/ui/docs/_assets/checkbox.png similarity index 100% rename from plugins/ui/examples/assets/checkbox.png rename to plugins/ui/docs/_assets/checkbox.png diff --git a/plugins/ui/examples/assets/counter.png b/plugins/ui/docs/_assets/counter.png similarity index 100% rename from plugins/ui/examples/assets/counter.png rename to plugins/ui/docs/_assets/counter.png diff --git a/plugins/ui/examples/assets/docker.png b/plugins/ui/docs/_assets/docker.png similarity index 100% rename from plugins/ui/examples/assets/docker.png rename to plugins/ui/docs/_assets/docker.png diff --git a/plugins/ui/examples/assets/double-tft.png b/plugins/ui/docs/_assets/double-tft.png similarity index 100% rename from plugins/ui/examples/assets/double-tft.png rename to plugins/ui/docs/_assets/double-tft.png diff --git a/plugins/ui/examples/assets/double_table.png b/plugins/ui/docs/_assets/double_table.png similarity index 100% rename from plugins/ui/examples/assets/double_table.png rename to plugins/ui/docs/_assets/double_table.png diff --git a/plugins/ui/examples/assets/foo_bar.png b/plugins/ui/docs/_assets/foo_bar.png similarity index 100% rename from plugins/ui/examples/assets/foo_bar.png rename to plugins/ui/docs/_assets/foo_bar.png diff --git a/plugins/ui/examples/assets/form.png b/plugins/ui/docs/_assets/form.png similarity index 100% rename from plugins/ui/examples/assets/form.png rename to plugins/ui/docs/_assets/form.png diff --git a/plugins/ui/examples/assets/form_submit.png b/plugins/ui/docs/_assets/form_submit.png similarity index 100% rename from plugins/ui/examples/assets/form_submit.png rename to plugins/ui/docs/_assets/form_submit.png diff --git a/plugins/ui/examples/assets/handling_events.png b/plugins/ui/docs/_assets/handling_events.png similarity index 100% rename from plugins/ui/examples/assets/handling_events.png rename to plugins/ui/docs/_assets/handling_events.png diff --git a/plugins/ui/examples/assets/hello_world.png b/plugins/ui/docs/_assets/hello_world.png similarity index 100% rename from plugins/ui/examples/assets/hello_world.png rename to plugins/ui/docs/_assets/hello_world.png diff --git a/plugins/ui/examples/assets/multiwave_dashboard.png b/plugins/ui/docs/_assets/multiwave_dashboard.png similarity index 100% rename from plugins/ui/examples/assets/multiwave_dashboard.png rename to plugins/ui/docs/_assets/multiwave_dashboard.png diff --git a/plugins/ui/examples/assets/my_dash.png b/plugins/ui/docs/_assets/my_dash.png similarity index 100% rename from plugins/ui/examples/assets/my_dash.png rename to plugins/ui/docs/_assets/my_dash.png diff --git a/plugins/ui/examples/assets/picker.png b/plugins/ui/docs/_assets/picker.png similarity index 100% rename from plugins/ui/examples/assets/picker.png rename to plugins/ui/docs/_assets/picker.png diff --git a/plugins/ui/examples/assets/range_table.png b/plugins/ui/docs/_assets/range_table.png similarity index 100% rename from plugins/ui/examples/assets/range_table.png rename to plugins/ui/docs/_assets/range_table.png diff --git a/plugins/ui/examples/assets/shared_state.png b/plugins/ui/docs/_assets/shared_state.png similarity index 100% rename from plugins/ui/examples/assets/shared_state.png rename to plugins/ui/docs/_assets/shared_state.png diff --git a/plugins/ui/examples/assets/stock_rollup.png b/plugins/ui/docs/_assets/stock_rollup.png similarity index 100% rename from plugins/ui/examples/assets/stock_rollup.png rename to plugins/ui/docs/_assets/stock_rollup.png diff --git a/plugins/ui/examples/assets/stock_widget_plot.png b/plugins/ui/docs/_assets/stock_widget_plot.png similarity index 100% rename from plugins/ui/examples/assets/stock_widget_plot.png rename to plugins/ui/docs/_assets/stock_widget_plot.png diff --git a/plugins/ui/examples/assets/stock_widget_table_invalid.png b/plugins/ui/docs/_assets/stock_widget_table_invalid.png similarity index 100% rename from plugins/ui/examples/assets/stock_widget_table_invalid.png rename to plugins/ui/docs/_assets/stock_widget_table_invalid.png diff --git a/plugins/ui/examples/assets/stock_widget_table_valid.png b/plugins/ui/docs/_assets/stock_widget_table_valid.png similarity index 100% rename from plugins/ui/examples/assets/stock_widget_table_valid.png rename to plugins/ui/docs/_assets/stock_widget_table_valid.png diff --git a/plugins/ui/examples/assets/table_events.png b/plugins/ui/docs/_assets/table_events.png similarity index 100% rename from plugins/ui/examples/assets/table_events.png rename to plugins/ui/docs/_assets/table_events.png diff --git a/plugins/ui/examples/assets/table_hooks.png b/plugins/ui/docs/_assets/table_hooks.png similarity index 100% rename from plugins/ui/examples/assets/table_hooks.png rename to plugins/ui/docs/_assets/table_hooks.png diff --git a/plugins/ui/examples/assets/text_field.png b/plugins/ui/docs/_assets/text_field.png similarity index 100% rename from plugins/ui/examples/assets/text_field.png rename to plugins/ui/docs/_assets/text_field.png diff --git a/plugins/ui/examples/assets/text_filter_table.png b/plugins/ui/docs/_assets/text_filter_table.png similarity index 100% rename from plugins/ui/examples/assets/text_filter_table.png rename to plugins/ui/docs/_assets/text_filter_table.png diff --git a/plugins/ui/examples/assets/wave_input.png b/plugins/ui/docs/_assets/wave_input.png similarity index 100% rename from plugins/ui/examples/assets/wave_input.png rename to plugins/ui/docs/_assets/wave_input.png diff --git a/plugins/ui/examples/assets/waves.png b/plugins/ui/docs/_assets/waves.png similarity index 100% rename from plugins/ui/examples/assets/waves.png rename to plugins/ui/docs/_assets/waves.png diff --git a/plugins/ui/examples/assets/waves_with_plot.png b/plugins/ui/docs/_assets/waves_with_plot.png similarity index 100% rename from plugins/ui/examples/assets/waves_with_plot.png rename to plugins/ui/docs/_assets/waves_with_plot.png From c7bd44c91b24e7cd25d883e2c0f82904b28c4d2d Mon Sep 17 00:00:00 2001 From: dsmmcken Date: Wed, 24 Apr 2024 12:09:17 -0400 Subject: [PATCH 2/7] blacken --- plugins/ui/docs/Plotting.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/plugins/ui/docs/Plotting.md b/plugins/ui/docs/Plotting.md index 23c219fac..1b86c70ae 100644 --- a/plugins/ui/docs/Plotting.md +++ b/plugins/ui/docs/Plotting.md @@ -14,6 +14,7 @@ import deephaven.ui as ui _stocks = dx.data.stocks() + @ui.component def plot_filtered_table(table, inital_value): text, set_text = ui.use_state("DOG") @@ -40,6 +41,7 @@ import deephaven.ui as ui _stocks = dx.data.stocks() + @ui.component def plot_paritioned_table(table, inital_value): text, set_text = ui.use_state(inital_value) @@ -76,25 +78,31 @@ _stocks = dx.data.stocks() @ui.component def parition_then_filter(table, by, inital_value): text, set_text = ui.use_state(inital_value) - paritioned_table = ui.use_memo(lambda: table.partition_by(by),[table, by]) - filtered = ui.use_memo(lambda: paritioned_table.filter(f"{by[0]} = `{text.upper()}`"), [text, paritioned_table]) + paritioned_table = ui.use_memo(lambda: table.partition_by(by), [table, by]) + filtered = ui.use_memo( + lambda: paritioned_table.filter(f"{by[0]} = `{text.upper()}`"), + [text, paritioned_table], + ) return [ ui.text_field(value=text, on_change=set_text), - dx.line(filtered, x="timestamp", y="price", by=[f"{by[1]}"]) + dx.line(filtered, x="timestamp", y="price", by=[f"{by[1]}"]), ] + # component using a where on a, then re-paritions on b @ui.component def where_then_parition(table, by, inital_value): text, set_text = ui.use_state(inital_value) - filtered = ui.use_memo(lambda: table.where(f"{by[0]} = `{text.upper()}`"),[text, table]) + filtered = ui.use_memo( + lambda: table.where(f"{by[0]} = `{text.upper()}`"), [text, table] + ) return [ ui.text_field(value=text, on_change=set_text), - dx.line(filtered, x="timestamp", y="price", by=[f"{by[1]}"]) + dx.line(filtered, x="timestamp", y="price", by=[f"{by[1]}"]), ] # outputs the same thing, done two different ways depending on how you want the work done -ptf = parition_then_filter(_stocks,["sym", "exchange"], "DOG") -wtp = where_then_parition(_stocks,["sym", "exchange"], "DOG") +ptf = parition_then_filter(_stocks, ["sym", "exchange"], "DOG") +wtp = where_then_parition(_stocks, ["sym", "exchange"], "DOG") ``` From 823d25ad0d5d9b65105e1db0a8d31bbfeb631d74 Mon Sep 17 00:00:00 2001 From: dsmmcken Date: Wed, 24 Apr 2024 12:28:06 -0400 Subject: [PATCH 3/7] remaining --- plugins/ui/docs/Plotting.md | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/plugins/ui/docs/Plotting.md b/plugins/ui/docs/Plotting.md index 1b86c70ae..0626cac5f 100644 --- a/plugins/ui/docs/Plotting.md +++ b/plugins/ui/docs/Plotting.md @@ -16,7 +16,7 @@ _stocks = dx.data.stocks() @ui.component -def plot_filtered_table(table, inital_value): +def plot_filtered_table(table, initial_value): text, set_text = ui.use_state("DOG") # the filter is memoized so that it is only recalculated when the text changes filtered_table = ui.use_memo( @@ -31,7 +31,7 @@ def plot_filtered_table(table, inital_value): p = plot_filtered_table(_stocks, "DOG") ``` -## Plotting a paritioned table +## Plotting a partitioned table Using a partitioned table, as opposed to a where, can be more efficient if you are going to be filtering the same table multiple times with different values. This is because the partitioning is only done once, and then the key is selected from the partitioned table. Compared to using a where statement, it can be faster to return results, but at the expense of the query engine using more memory. Depending on the size of your table and the number of unique values in the partition key, this can be a tradeoff worth making or not. @@ -43,28 +43,28 @@ _stocks = dx.data.stocks() @ui.component -def plot_paritioned_table(table, inital_value): - text, set_text = ui.use_state(inital_value) - # memoize the parition by so that it only performed once - paritioned_table = ui.use_memo(lambda: table.partition_by(["sym"]), [table]) +def plot_partitioned_table(table, initial_value): + text, set_text = ui.use_state(initial_value) + # memoize the partition by so that it only performed once + partitioned_table = ui.use_memo(lambda: table.partition_by(["sym"]), [table]) constituent_table = ui.use_memo( - lambda: paritioned_table.get_constituent(text.upper()), [paritioned_table, text] + lambda: partitioned_table.get_constituent(text.upper()), [partitioned_table, text] ) return [ ui.text_field(value=text, on_change=set_text), - # only attempt to plot valid parition keys + # only attempt to plot valid partition keys dx.line( - constituent_table, x="timestamp", y="price", title=f"Parition key: {text}" + constituent_table, x="timestamp", y="price", title=f"partition key: {text}" ) if constituent_table != None - else ui.text("Please enter a valid parition."), + else ui.text("Please enter a valid partition."), ] -p = plot_paritioned_table(_stocks, "DOG") +p = plot_partitioned_table(_stocks, "DOG") ``` -## Combining a fitler and a parition by +## Combining a fitler and a partition by Deephaven express allows you to plot by a partition and assign unique colors to each key. Sometimes as a user you may also want to filter the data in addition to partitioning it. We've previously referred to this as a "one-click plot by" behaviour in enterprise. This can be done by either filtering the table first and then partitioning it, or partitioning it first and then filtering it. The choice of which to use depends on the size of the table and the number of unique values in the partition key. The first example is more like a traditional "one-click" component, and the second is more like a parameterized query. Both will give you the same result, but the first one may return results faster, whereas the second one may be more memory efficient. @@ -74,14 +74,14 @@ import deephaven.ui as ui _stocks = dx.data.stocks() -# component using a parition_by[a,b] once, then filter on a +# component using a partition_by[a,b] once, then filter on a @ui.component -def parition_then_filter(table, by, inital_value): - text, set_text = ui.use_state(inital_value) - paritioned_table = ui.use_memo(lambda: table.partition_by(by), [table, by]) +def partition_then_filter(table, by, initial_value): + text, set_text = ui.use_state(initial_value) + partitioned_table = ui.use_memo(lambda: table.partition_by(by), [table, by]) filtered = ui.use_memo( - lambda: paritioned_table.filter(f"{by[0]} = `{text.upper()}`"), - [text, paritioned_table], + lambda: partitioned_table.filter(f"{by[0]} = `{text.upper()}`"), + [text, partitioned_table], ) return [ ui.text_field(value=text, on_change=set_text), @@ -89,10 +89,10 @@ def parition_then_filter(table, by, inital_value): ] -# component using a where on a, then re-paritions on b +# component using a where on a, then re-partitions on b @ui.component -def where_then_parition(table, by, inital_value): - text, set_text = ui.use_state(inital_value) +def where_then_partition(table, by, initial_value): + text, set_text = ui.use_state(initial_value) filtered = ui.use_memo( lambda: table.where(f"{by[0]} = `{text.upper()}`"), [text, table] ) @@ -103,6 +103,6 @@ def where_then_parition(table, by, inital_value): # outputs the same thing, done two different ways depending on how you want the work done -ptf = parition_then_filter(_stocks, ["sym", "exchange"], "DOG") -wtp = where_then_parition(_stocks, ["sym", "exchange"], "DOG") +ptf = partition_then_filter(_stocks, ["sym", "exchange"], "DOG") +wtp = where_then_partition(_stocks, ["sym", "exchange"], "DOG") ``` From 1c7ac5eaf07a12a6239aeec03e6d47fc50a22c71 Mon Sep 17 00:00:00 2001 From: dsmmcken Date: Wed, 24 Apr 2024 12:29:57 -0400 Subject: [PATCH 4/7] blacken more --- plugins/ui/docs/Plotting.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/ui/docs/Plotting.md b/plugins/ui/docs/Plotting.md index 0626cac5f..53e514166 100644 --- a/plugins/ui/docs/Plotting.md +++ b/plugins/ui/docs/Plotting.md @@ -48,7 +48,8 @@ def plot_partitioned_table(table, initial_value): # memoize the partition by so that it only performed once partitioned_table = ui.use_memo(lambda: table.partition_by(["sym"]), [table]) constituent_table = ui.use_memo( - lambda: partitioned_table.get_constituent(text.upper()), [partitioned_table, text] + lambda: partitioned_table.get_constituent(text.upper()), + [partitioned_table, text], ) return [ ui.text_field(value=text, on_change=set_text), From d22e652be45eba4ecf8a2ee6d0486311b13ee151 Mon Sep 17 00:00:00 2001 From: dsmmcken Date: Thu, 25 Apr 2024 17:46:27 -0400 Subject: [PATCH 5/7] review feedback --- plugins/ui/docs/Plotting.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/ui/docs/Plotting.md b/plugins/ui/docs/Plotting.md index 53e514166..4c8b64825 100644 --- a/plugins/ui/docs/Plotting.md +++ b/plugins/ui/docs/Plotting.md @@ -67,7 +67,7 @@ p = plot_partitioned_table(_stocks, "DOG") ## Combining a fitler and a partition by -Deephaven express allows you to plot by a partition and assign unique colors to each key. Sometimes as a user you may also want to filter the data in addition to partitioning it. We've previously referred to this as a "one-click plot by" behaviour in enterprise. This can be done by either filtering the table first and then partitioning it, or partitioning it first and then filtering it. The choice of which to use depends on the size of the table and the number of unique values in the partition key. The first example is more like a traditional "one-click" component, and the second is more like a parameterized query. Both will give you the same result, but the first one may return results faster, whereas the second one may be more memory efficient. +Deephaven Express allows you to plot by a partition and assign unique colors to each key. Sometimes as a user you may also want to filter the data in addition to partitioning it. We've previously referred to this as a "one-click plot by" behaviour in enterprise. This can be done by either filtering the table first and then partitioning it, or partitioning it first and then filtering it. The choice of which to use depends on the size of the table and the number of unique values in the partition key. The first example is more like a traditional "one-click" component, and the second is more like a parameterized query. Both will give you the same result, but the first one may return results faster, whereas the second one may be more memory efficient. ```python import deephaven.plot.express as dx @@ -75,9 +75,11 @@ import deephaven.ui as ui _stocks = dx.data.stocks() -# component using a partition_by[a,b] once, then filter on a @ui.component def partition_then_filter(table, by, initial_value): + """ + Parition the table by both passed columns, then filter it by the value entered by the user + """ text, set_text = ui.use_state(initial_value) partitioned_table = ui.use_memo(lambda: table.partition_by(by), [table, by]) filtered = ui.use_memo( @@ -90,9 +92,11 @@ def partition_then_filter(table, by, initial_value): ] -# component using a where on a, then re-partitions on b @ui.component def where_then_partition(table, by, initial_value): + """ + Filter the table by the value entered by the user, then re-partition it by the second passed column + """ text, set_text = ui.use_state(initial_value) filtered = ui.use_memo( lambda: table.where(f"{by[0]} = `{text.upper()}`"), [text, table] From acea79be813b8e73db68247553ba9038b57b40a6 Mon Sep 17 00:00:00 2001 From: dsmmcken Date: Thu, 25 Apr 2024 17:49:50 -0400 Subject: [PATCH 6/7] bllack --- plugins/ui/docs/Plotting.md | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/ui/docs/Plotting.md b/plugins/ui/docs/Plotting.md index 4c8b64825..8849faf51 100644 --- a/plugins/ui/docs/Plotting.md +++ b/plugins/ui/docs/Plotting.md @@ -75,6 +75,7 @@ import deephaven.ui as ui _stocks = dx.data.stocks() + @ui.component def partition_then_filter(table, by, initial_value): """ From 2285b8bf002694b9a5a82062f48879ec26e8d1e2 Mon Sep 17 00:00:00 2001 From: Don Date: Fri, 26 Apr 2024 16:22:58 -0400 Subject: [PATCH 7/7] Update plugins/ui/docs/Plotting.md Co-authored-by: Joe --- plugins/ui/docs/Plotting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/ui/docs/Plotting.md b/plugins/ui/docs/Plotting.md index 8849faf51..729f68e76 100644 --- a/plugins/ui/docs/Plotting.md +++ b/plugins/ui/docs/Plotting.md @@ -79,7 +79,7 @@ _stocks = dx.data.stocks() @ui.component def partition_then_filter(table, by, initial_value): """ - Parition the table by both passed columns, then filter it by the value entered by the user + Partition the table by both passed columns, then filter it by the value entered by the user """ text, set_text = ui.use_state(initial_value) partitioned_table = ui.use_memo(lambda: table.partition_by(by), [table, by])