Skip to content

Commit

Permalink
Interactive slicer example (#48)
Browse files Browse the repository at this point in the history
* simple example for interactive position set
- uses input fields to both display and change slider position
- uses three viewers for volume

* add comments, run doc
also unhide the sliders

* some verbose labels in the app

* removed unused imports and short description

* black

* flake

Co-authored-by: Almar Klein <[email protected]>
  • Loading branch information
surchs and almarklein authored Dec 17, 2020
1 parent 495200d commit 75e2f8b
Showing 1 changed file with 117 additions and 0 deletions.
117 changes: 117 additions & 0 deletions examples/set_slicer_position_interactively.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""
This is an example that shows how the slicer position can be both read from and written to
by listening to the "drag_value" of the default slider with an auxiliary slider
"""

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
import imageio


app = dash.Dash(__name__, update_title=None)

vol = imageio.volread("imageio:stent.npz")
slicer0 = VolumeSlicer(app, vol, axis=0, scene_id="brain")
slicer1 = VolumeSlicer(app, vol, axis=1, scene_id="brain")
slicer2 = VolumeSlicer(app, vol, axis=2, scene_id="brain")

setpos_store = dcc.Store(
id={"context": "app", "scene": slicer0.scene_id, "name": "setpos"}
)

# Here we create an auxiliary slider for each slicer and encapsulate it inside a Div
# to be added to the app layout
slicer_list = [setpos_store]
for sidx, slicer in enumerate([slicer0, slicer1, slicer2]):
slider = dcc.Slider(id=f"slider-{sidx}", max=slicer.nslices)
slicer_list.append(
html.Div(
[
html.Pre("slicer graph"),
slicer.graph,
html.Pre("builtin slider"),
slicer.slider,
html.Pre("auxiliary slider"),
slider,
*slicer.stores,
]
)
)

# Create a small CSS grid with Input fields and text labels to both display
# the slicer axis positions and allow the user to interactively change them
nav_table = html.Div(
[
html.Div(""),
html.Div("Voxel position"),
html.Div("X axis"),
dcc.Input(id="x-nav", type="number", placeholder="X value"),
html.Div("Y axis"),
dcc.Input(id="y-nav", type="number", placeholder="Y value"),
html.Div("Z axis"),
dcc.Input(id="z-nav", type="number", placeholder="Z value"),
],
style={"display": "grid", "gridTemplateColumns": "10% 10%"},
)
slicer_list.append(nav_table)

app.layout = html.Div(
style={
"display": "grid",
"gridTemplateColumns": "33% 33% 33%",
},
children=slicer_list,
)


# Take the current value from the main slider and copy it to the
# auxiliary slider
@app.callback(
[
Output("slider-0", "value"),
Output("slider-1", "value"),
Output("slider-2", "value"),
],
[
Input(slicer0.slider.id, "drag_value"),
Input(slicer1.slider.id, "drag_value"),
Input(slicer2.slider.id, "drag_value"),
],
)
def write_to_auxiliary_slider(x_slider, y_slider, z_slider):
return x_slider, y_slider, z_slider


# Write the values of the axiliary slider to the input fields in the navigation table
@app.callback(
[Output("x-nav", "value"), Output("y-nav", "value"), Output("z-nav", "value")],
[
Input("slider-0", "value"),
Input("slider-1", "value"),
Input("slider-2", "value"),
],
)
def write_to_position_table(x_val, y_val, z_val):
return x_val, y_val, z_val


# Listen for a user-triggered change to the value of the Input fields in the navigation table
# and set the position of the slicer accordingly
@app.callback(
Output(setpos_store.id, "data"),
[
Input("x-nav", "value"),
Input("y-nav", "value"),
Input("z-nav", "value"),
],
)
def write_table_values_to_slicer(x_pos, y_pos, z_pos):
return z_pos, y_pos, x_pos


if __name__ == "__main__":
# Note: dev_tools_props_check negatively affects the performance of VolumeSlicer
app.run_server(debug=True, dev_tools_props_check=False)

0 comments on commit 75e2f8b

Please sign in to comment.