Closed surchs closed 3 years ago
One solution could be to use the new drag_value
property of the slider. However, this feature is not yet in a released version of dcc. In the example below I use a second slider as "something that both gets and sets the index".
import dash
import dash_html_components as html
from dash_slicer import VolumeSlicer
import dash_core_components as dcc
from dash.dependencies import Input, Output, State, ALL
import imageio
app = dash.Dash(__name__, update_title=None)
vol = imageio.volread("imageio:stent.npz")
slicer = VolumeSlicer(app, vol)
slider = dcc.Slider(id="slider", max=slicer.nslices)
setpos_store = dcc.Store(
id={"context": "app", "scene": slicer.scene_id, "name": "setpos"}
)
app.layout = html.Div([slicer.graph, slicer.slider, slider, setpos_store, *slicer.stores])
@app.callback(
Output("slider", "value"),
Input(slicer.slider.id, "drag_value")
)
def set_slider(index):
return index
@app.callback(
Output(setpos_store.id, "data"),
Input("slider", "value"),
)
def get_slider(value):
return None, None, value
if __name__ == "__main__":
# Note: dev_tools_props_check negatively affects the performance of VolumeSlicer
app.run_server(debug=True, dev_tools_props_check=False)
I was expecting that the pattern matching callback for setpos would break the loop, but it does not. This means we need another way to break the loop.
In the above example the slider can do this, because slider.value
copies to slider.drag_value
- because the system is not aware of this, it breaks the loop. Maybe there is another way to do a similar trick.
I think we could break the loop by splitting the rate limiting logic into two callbacks. I'll have a go later.
It looks like we can do this. With #45 the loop is broken in the slicer itself, so application code does not have to worry about it at all.
This is amazing! Thanks @almarklein, I'll try this out right away on the brain app!
Hey,
I am trying to have a simple app where I show the current slicer index positions in a datatable or similar element and allow for a user to change the slicer values from this datatable (this could also just be Input fields or something like NumericInput from dash-daq). My goal is something like this:![image](https://user-images.githubusercontent.com/1302022/102124003-b5c8c080-3e15-11eb-8a12-9b8f10ae7ecd.png)
Here is the general strategy I am using: Suppose I have three slicers:
then to
Write to slicer position:
Read from slicer position
This is more difficult. I cannot just listen to
Input({"scene": slicer0.scene_id, "context": ALL, "name": "state"}, "data"),
as described in the examples, because that gives me circular dependencies. As per the new devnotes, I tried listening to the slider values instead like so:However, this also results in a circular dependency problem (i.e. the values in the datatable both depend on and affect the slider positions).
A nice workaround proposed by @emmanuelle is to listen to the clickData events from the slicer.graph objects as the trigger, and have the slicer position info as state. This is possible. For example like so:
This works, but the slicer position reported by the
State
here lags one user click event behind the true position. In other words: the coordinates I get in this way are from the position before the current click event. So also not ideal, particularly not if I want to use the coordinates as starting points to let the user make changes. Here is a brief example to show the latter problem:Summary
It's not clear (to me) how to both listen to and change through user input the slicer positions with the same interactive component (e.g. datatable or input field) in an app. It would probably be good to add an example that illustrates this.