Skip to content

Commit 745fc07

Browse files
authored
Add support for extra traces (#40)
* Add support for additional traces, plus example to demo it * update readme * forgot to add example * each contour a different color * change the wording in the docstring a bit
1 parent b73e3b9 commit 745fc07

File tree

4 files changed

+92
-9
lines changed

4 files changed

+92
-9
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ can be a list of such colors, defining a colormap.
107107

108108
**property `VolumeSlicer.axis`** (`int`): The axis to slice.
109109

110+
**property `VolumeSlicer.extra_traces`**: A `dcc.Store` that can be used as an output to define
111+
additional traces to be shown in this slicer. The data must be
112+
a list of dictionaries, with each dict representing a raw trace
113+
object.
114+
110115
**property `VolumeSlicer.graph`**: The `dcc.Graph` for this slicer. Use `graph.figure` to access the
111116
Plotly Figure object.
112117

dash_slicer/slicer.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,15 @@ def state(self):
271271
"""
272272
return self._state
273273

274+
@property
275+
def extra_traces(self):
276+
"""A `dcc.Store` that can be used as an output to define
277+
additional traces to be shown in this slicer. The data must be
278+
a list of dictionaries, with each dict representing a raw trace
279+
object.
280+
"""
281+
return self._extra_traces
282+
274283
@property
275284
def overlay_data(self):
276285
"""A `dcc.Store` containing the overlay data. The form of this
@@ -453,6 +462,9 @@ def _create_dash_components(self):
453462
# Store indicator traces for the slicer.
454463
self._indicator_traces = Store(id=self._subid("indicator-traces"), data=[])
455464

465+
# Store user traces for the slider.
466+
self._extra_traces = Store(id=self._subid("extra-traces"), data=[])
467+
456468
# A timer to apply a rate-limit between slider.value and index.data
457469
self._timer = Interval(id=self._subid("timer"), interval=100, disabled=True)
458470

@@ -469,6 +481,7 @@ def _create_dash_components(self):
469481
self._server_data,
470482
self._img_traces,
471483
self._indicator_traces,
484+
self._extra_traces,
472485
self._timer,
473486
self._state,
474487
self._setpos,
@@ -820,11 +833,12 @@ def _create_client_callbacks(self):
820833

821834
app.clientside_callback(
822835
"""
823-
function update_figure(img_traces, indicators, info, ori_figure) {
836+
function update_figure(img_traces, indicator_traces, extra_traces, info, ori_figure) {
824837
// Collect traces
825838
let traces = [];
826839
for (let trace of img_traces) { traces.push(trace); }
827-
for (let trace of indicators) { if (trace.line.color) traces.push(trace); }
840+
for (let trace of extra_traces) { traces.push(trace); }
841+
for (let trace of indicator_traces) { if (trace.line.color) traces.push(trace); }
828842
// Update figure
829843
let figure = {...ori_figure};
830844
figure.data = traces;
@@ -835,6 +849,7 @@ def _create_client_callbacks(self):
835849
[
836850
Input(self._img_traces.id, "data"),
837851
Input(self._indicator_traces.id, "data"),
852+
Input(self._extra_traces.id, "data"),
838853
],
839854
[State(self._info.id, "data"), State(self._graph.id, "figure")],
840855
)

examples/threshold_contour.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""
2+
An example demonstrating adding traces.
3+
4+
This shows a volume with contours overlaid on top. The `extra_traces`
5+
property is used to add scatter traces that represent the contours.
6+
"""
7+
8+
import plotly
9+
import dash
10+
import dash_html_components as html
11+
import dash_core_components as dcc
12+
from dash.dependencies import Input, Output
13+
from dash_slicer import VolumeSlicer
14+
import imageio
15+
from skimage import measure
16+
17+
18+
app = dash.Dash(__name__, update_title=None)
19+
server = app.server
20+
21+
vol = imageio.volread("imageio:stent.npz")
22+
mi, ma = vol.min(), vol.max()
23+
slicer = VolumeSlicer(app, vol)
24+
25+
26+
app.layout = html.Div(
27+
[
28+
slicer.graph,
29+
slicer.slider,
30+
dcc.Slider(
31+
id="level-slider",
32+
min=mi,
33+
max=ma,
34+
step=1,
35+
value=mi + 0.2 * (ma - mi),
36+
),
37+
*slicer.stores,
38+
]
39+
)
40+
41+
42+
@app.callback(
43+
Output(slicer.extra_traces.id, "data"),
44+
[Input("level-slider", "value"), Input(slicer.state.id, "data")],
45+
)
46+
def apply_levels(level, state):
47+
if not state:
48+
return dash.no_update
49+
slice = vol[state["index"]]
50+
contours = measure.find_contours(slice, level)
51+
# Create a trace for each contour, each a different color
52+
traces = []
53+
for i, contour in enumerate(contours):
54+
colors = plotly.colors.qualitative.D3
55+
color = colors[i % len(colors)]
56+
traces.append(
57+
{
58+
"type": "scatter",
59+
"mode": "lines",
60+
"line": {"color": color, "width": 3},
61+
"x": contour[:, 1],
62+
"y": contour[:, 0],
63+
}
64+
)
65+
return traces
66+
67+
68+
if __name__ == "__main__":
69+
# Note: dev_tools_props_check negatively affects the performance of VolumeSlicer
70+
app.run_server(debug=True, dev_tools_props_check=False)

examples/threshold_overlay.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,6 @@
2222
mi, ma = vol.min(), vol.max()
2323
slicer = VolumeSlicer(app, vol)
2424

25-
slicer.graph.config.update(
26-
modeBarButtonsToAdd=[
27-
"drawclosedpath",
28-
"eraseshape",
29-
]
30-
)
31-
3225

3326
app.layout = html.Div(
3427
[

0 commit comments

Comments
 (0)