-
Notifications
You must be signed in to change notification settings - Fork 261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: run_button #1514
feat: run_button #1514
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# Run Button | ||
|
||
```{eval-rst} | ||
.. marimo-embed:: | ||
@app.cell | ||
def __(): | ||
b = mo.ui.run_button() | ||
b | ||
return | ||
|
||
@app.cell | ||
def __(): | ||
s = mo.ui.slider(1, 10) | ||
s | ||
return | ||
|
||
|
||
@app.cell | ||
def __(): | ||
mo.stop(b.value, "Click `run` to submit the slider's value") | ||
|
||
s.value | ||
return | ||
``` | ||
|
||
```{eval-rst} | ||
.. autoclass:: marimo.ui.run_button | ||
:members: | ||
|
||
.. autoclasstoc:: marimo._plugins.ui._impl.run_button.run_button | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -379,6 +379,10 @@ def _update(self, value: S) -> None: | |
if self._on_change is not None: | ||
self._on_change(self._value) | ||
|
||
def _on_update_completion(self) -> None: | ||
"""Callback to run after the kernel has processed a value update.""" | ||
return | ||
Comment on lines
+382
to
+384
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method acts as a post execution hook; the runtime calls it after setting UI element values. |
||
|
||
def _clone(self) -> UIElement[S, T]: | ||
"""Clone a UIElement, returning one with a different id | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# Copyright 2024 Marimo. All rights reserved. | ||
from __future__ import annotations | ||
|
||
from typing import Any, Callable, Final, Literal, Optional | ||
|
||
from marimo._output.rich_help import mddoc | ||
from marimo._plugins.ui._core.ui_element import UIElement | ||
from marimo._plugins.ui._impl.input import button | ||
from marimo._runtime.context.types import ContextNotInitializedError | ||
|
||
|
||
@mddoc | ||
class run_button(UIElement[Any, Any]): | ||
""" | ||
A button that can be used to trigger computation. | ||
|
||
**Example.** | ||
|
||
```python | ||
# a button that when clicked will have its value set to True; | ||
# any cells referencing that button will automatically run. | ||
button = mo.ui.button() | ||
akshayka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
button | ||
``` | ||
|
||
```python | ||
slider = mo.ui.slider(1, 10) | ||
slider | ||
``` | ||
|
||
```python | ||
# if the button hasn't been clicked, don't run. | ||
mo.stop(not button.value) | ||
|
||
slider.value | ||
``` | ||
|
||
When clicked, `run_button`'s value is set to `True`, and any cells | ||
referencing it are run. After those cells are run, `run_button`'s | ||
value will automatically be set back to `False` as long as automatic | ||
execution is enabled. | ||
|
||
**Attributes.** | ||
|
||
- `value`: the value of the button; `True` when clicked, and reset to | ||
`False` after cells referencing the button finish running (when | ||
automatic execution is enabled). | ||
|
||
**Initialization Args.** | ||
|
||
- `kind`: 'neutral', 'success', 'warn', or 'danger' | ||
- `disabled`: whether the button is disabled | ||
- `tooltip`: a tooltip to display for the button | ||
- `label`: text label for the element | ||
- `on_change`: optional callback to run when this element's value changes | ||
- `full_width`: whether the input should take up the full width of its | ||
container | ||
""" | ||
|
||
_name: Final[str] = "marimo-button" | ||
|
||
def __init__( | ||
self, | ||
kind: Literal["neutral", "success", "warn", "danger"] = "neutral", | ||
disabled: bool = False, | ||
tooltip: Optional[str] = None, | ||
*, | ||
label: str = "click to run", | ||
on_change: Optional[Callable[[Any], None]] = None, | ||
full_width: bool = False, | ||
) -> None: | ||
self._initial_value = False | ||
super().__init__( | ||
# We reuse the button plugin on the frontend, since the UI | ||
# is the same. | ||
component_name=button._name, | ||
# frontend's value is a counter | ||
initial_value=0, | ||
label=label, | ||
args={ | ||
"kind": kind, | ||
"disabled": disabled, | ||
"tooltip": tooltip, | ||
"full-width": full_width, | ||
}, | ||
on_change=on_change, | ||
) | ||
|
||
def _convert_value(self, value: Any) -> Any: | ||
if value == 0: | ||
# frontend's value == 0 only during initialization; first value | ||
# frontend will send is 1 | ||
return False | ||
else: | ||
return True | ||
|
||
def _on_update_completion(self) -> None: | ||
from marimo._runtime.context import get_context | ||
from marimo._runtime.context.kernel_context import KernelRuntimeContext | ||
|
||
try: | ||
ctx = get_context() | ||
except ContextNotInitializedError: | ||
self._value = False | ||
return | ||
|
||
if isinstance(ctx, KernelRuntimeContext) and ctx.lazy: | ||
# Resetting to False in lazy kernels makes the button pointless, | ||
# since its value hasn't been read by downstream cells on update | ||
# completion. | ||
# | ||
# The right thing to do would be to somehow set to False after | ||
# all cells that were marked stale because of the update were run, | ||
# but that's too complicated. | ||
return | ||
Comment on lines
+106
to
+114
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Special case for lazy kernels, since we don't when descendants will run |
||
else: | ||
self._value = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be
mo.stop(not b.value)
.not sure if this hides to much, but could put a stop on the button too?
or maybe
b.gate()
,b.clicked()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops yes it should.
Something like that could be nice — maybe
b.gate()
... might add in a follow-up