-
-
Notifications
You must be signed in to change notification settings - Fork 292
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
Support dependent bound variables for SliderServer #2014
Conversation
Try this Pull Request!Open Julia and type: julia> import Pkg
julia> Pkg.activate(temp=true)
julia> Pkg.add(url="/~https://github.com/lungben/Pluto.jl", rev="dependend_bound_variables")
julia> using Pluto |
You need to apply the value transformation somewhere: expr = :($bond_var = Main.PlutoRunner.transform_bond_value($(QuoteNode(bond_var)), $bond_val)) This API allows widgets to return julia native values by converting them depending on the widget (the second argument to Note that this suggestion causes a problem because the transformation will happen with the new widget: # Cell 1
@bind x Slider(1:10)
# Cell 2
@bind y Slider(0:x:100) I think in this context, the widgets send as indices in the respective slider ranges So the transformation has already been done before the cell is run but its value has been overriden with the new default widget value and we either need to restore that value or transform again: Pluto.jl/src/evaluation/RunBonds.jl Line 44 in 15a94f4
|
Thanks for pointing out the value transformations! It is a general issue for dependent sliders that As far as I know, PlutoSliderServer does not distinguish in its request which variable has been explicitly changed by the user and which is only co-dependent. Thus, we cannot check if the latter still has a valid value with the updated UI element (and reset it to the default if not). Or do I miss something? |
For the valid value problem, there is plan (see JuliaPluto/PlutoSliderServer.jl#39) to integrate AbstractPlutoDingetjes.validate_value inside PlutoSliderServer and prevent invalid value altogether (from calling the API directly). You are right that the request always contains both bonds values, so actually the transformation should be done with respect to the new widget value (not previous as i said). Therefore, you should be okay by just transforming after the cell was run: for bond_var ∈ redefined_external_bound_variables
# set the redefined bound variables to their original value from the request
bond_val = externally_updated_variables[bond_var]
- expr = :($bond_var = $bond_val)
+ expr = :($bond_var = Main.PlutoRunner.transform_value($(QuoteNode(bond_var)), $bond_val))
WorkspaceManager.eval_in_workspace((session, notebook), expr)
end |
In general this functionality seems to be working correctly also for larger / more complex notebooks. If the first bound value (here Maybe we should catch this error and show a more descriptive error to the user? |
Wrapping Edit: I think a more descriptive error message is rather "nice-to-have" and should not prevent merging this PR. The core in this PR should anyhow only be executed in SliderServer requests, where this PR brings a significant improvement. |
Awesome! Very cool that this could be done with such a small PR! We should add tests for this into Pluto.jl itself, right now there are only on PSS. It would be nice if the tests do not depend on PlutoUI, so you can do: begin
struct TransformSlider
range::AbstractRange
end
Base.show(io::IO, m::MIME"text/html", os::TransformSlider) = write(io, "<input type=range value=$(minimum(os.range)) min=$(minimum(os.range)) max=$(maximum(os.range))>")
Bonds.initial_value(os::TransformSlider) = Bonds.transform_value(os, minimum(os.range))
Bonds.possible_values(os::TransformSlider) = os.range
Bonds.transform_value(os::TransformSlider, from_js) = from_js * 2
end which you can use like so: @bind x TransformSlider(1:10)
@bind y TransformSlider(1:x) (copy-paste into Pluto to see what it does) See /~https://github.com/fonsp/Pluto.jl/blob/main/test/Bonds.jl for more examples |
For testing this PR, plain html should be sufficient:
|
From my side, this PR is now ready for merging.
|
Thanks! Can you add a test with a transformed value as in #2014 (comment) ? Since this PR had some discussion and changes related to it, it makes sense to also add a test, right? It should be easy to add, by adding three new cells to the test notebook, (defining |
Done Interestingly, the TransformSlider requires setting of |
Weird! But that's a good find! I made #2056 to track that separately from this PR |
Hey! I noticed that we might be able to clean this up a bit: we now have the old mechanism for setting bond values: In theory, |
As far as I can see, both
|
@fonsp can this PR be merged? |
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.
Thanks @lungben , and apologies for the delay!
I initially thought that this PR would be difficult to review, because I was worried about how it interplays with funky combinations of bonds. But in fact, this PR does not change behaviour when only a single bond is being set, because there is only one cell defining that bond, and that one is excluded from the reactive run (because we use the cells that reference a set bond as the root of the reactive run).
There is one exception: the following notebook behaves differently:
begin
if @isdefined(x)
# cool
end
bond = @bind x html"<input type=range>"
x = 999
bond
end
x
because the first cell both defines and references x
, so it is a cell that references the bound symbol, and it will be part of the reactive run. This mechanism can be used for single-cell interactions like this one.
As it happens, the example above gets stuck in an infinite loop on the current Pluto (long story), and after this PR, it doesn't get stuck, and the x = 999
line is "ignored".
But! The single-cell interaction example that I mentioned above still works! So I take that as a sign that the behaviour is generally as expected, and can even contribute to preventing loops.
TLDR: 👍👍
My first try to implement #1695
This should allow PlutoSliderServer to work with dependent bind values, i.e. a UI element dependent on another UI element.
ToDo: