From 736c0ffd29db02c7aa3cf66c92437a28b9aa24d3 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 11 Dec 2020 15:44:11 +0100 Subject: [PATCH 1/5] Add support for additional traces, plus example to demo it --- dash_slicer/slicer.py | 19 +++++++++++++++++-- examples/threshold_overlay.py | 7 ------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/dash_slicer/slicer.py b/dash_slicer/slicer.py index 6300bb8..66d4bee 100644 --- a/dash_slicer/slicer.py +++ b/dash_slicer/slicer.py @@ -238,6 +238,15 @@ def state(self): """ return self._state + @property + def extra_traces(self): + """A `dcc.Store` that can be used as an output to define + additional traces to be shown in this slicer. The data must be + a list of dictionaries, with each dict representing a raw trace + object. + """ + return self._extra_traces + @property def overlay_data(self): """A `dcc.Store` containing the overlay data. The form of this @@ -420,6 +429,9 @@ def _create_dash_components(self): # Store indicator traces for the slicer. self._indicator_traces = Store(id=self._subid("indicator-traces"), data=[]) + # Store user traces for the slider. + self._extra_traces = Store(id=self._subid("extra-traces"), data=[]) + # A timer to apply a rate-limit between slider.value and index.data self._timer = Interval(id=self._subid("timer"), interval=100, disabled=True) @@ -436,6 +448,7 @@ def _create_dash_components(self): self._server_data, self._img_traces, self._indicator_traces, + self._extra_traces, self._timer, self._state, self._setpos, @@ -787,11 +800,12 @@ def _create_client_callbacks(self): app.clientside_callback( """ - function update_figure(img_traces, indicators, info, ori_figure) { + function update_figure(img_traces, indicator_traces, extra_traces, info, ori_figure) { // Collect traces let traces = []; for (let trace of img_traces) { traces.push(trace); } - for (let trace of indicators) { if (trace.line.color) traces.push(trace); } + for (let trace of extra_traces) { traces.push(trace); } + for (let trace of indicator_traces) { if (trace.line.color) traces.push(trace); } // Update figure let figure = {...ori_figure}; figure.data = traces; @@ -802,6 +816,7 @@ def _create_client_callbacks(self): [ Input(self._img_traces.id, "data"), Input(self._indicator_traces.id, "data"), + Input(self._extra_traces.id, "data"), ], [State(self._info.id, "data"), State(self._graph.id, "figure")], ) diff --git a/examples/threshold_overlay.py b/examples/threshold_overlay.py index 997039f..5938950 100644 --- a/examples/threshold_overlay.py +++ b/examples/threshold_overlay.py @@ -22,13 +22,6 @@ mi, ma = vol.min(), vol.max() slicer = VolumeSlicer(app, vol) -slicer.graph.config.update( - modeBarButtonsToAdd=[ - "drawclosedpath", - "eraseshape", - ] -) - app.layout = html.Div( [ From b9e78f1dc155baaffd49bcd60e31c94a3098e303 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 11 Dec 2020 15:44:31 +0100 Subject: [PATCH 2/5] update readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index e61b1dc..06861e1 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,11 @@ can be a list of such colors, defining a colormap. **property `VolumeSlicer.axis`** (`int`): The axis to slice. +**property `VolumeSlicer.extra_traces`**: A `dcc.Store` that can be used as an output to define +additional traces to be shown in this slicer. The data must be +a list of dictionaries, with each dict representing a raw trace +object. + **property `VolumeSlicer.graph`**: The `dcc.Graph` for this slicer. Use `graph.figure` to access the Plotly Figure object. From 054c5d430bebde7fa46edb82e4cf182081613634 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 14 Dec 2020 14:08:21 +0100 Subject: [PATCH 3/5] forgot to add example --- examples/threshold_contour.py | 66 +++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 examples/threshold_contour.py diff --git a/examples/threshold_contour.py b/examples/threshold_contour.py new file mode 100644 index 0000000..f5cd88b --- /dev/null +++ b/examples/threshold_contour.py @@ -0,0 +1,66 @@ +""" +An example demonstrating adding traces. + +This shows a volume with a contour overlaid on top. The `extra_traces` +property is used to add scatter traces that represent the contour. +""" + +import dash +import dash_html_components as html +import dash_core_components as dcc +from dash.dependencies import Input, Output +from dash_slicer import VolumeSlicer +import imageio +from skimage import measure + + +app = dash.Dash(__name__, update_title=None) +server = app.server + +vol = imageio.volread("imageio:stent.npz") +mi, ma = vol.min(), vol.max() +slicer = VolumeSlicer(app, vol) + + +app.layout = html.Div( + [ + slicer.graph, + slicer.slider, + dcc.Slider( + id="level-slider", + min=mi, + max=ma, + step=1, + value=mi + 0.2 * (ma - mi), + ), + *slicer.stores, + ] +) + + +@app.callback( + Output(slicer.extra_traces.id, "data"), + [Input("level-slider", "value"), Input(slicer.state.id, "data")], +) +def apply_levels(level, state): + if not state: + return dash.no_update + slice = vol[state["index"]] + contours = measure.find_contours(slice, level) + traces = [] + for contour in contours: + traces.append( + { + "type": "scatter", + "mode": "lines", + "line": {"color": "cyan", "width": 3}, + "x": contour[:, 1], + "y": contour[:, 0], + } + ) + return traces + + +if __name__ == "__main__": + # Note: dev_tools_props_check negatively affects the performance of VolumeSlicer + app.run_server(debug=True, dev_tools_props_check=False) From 016697aeb146740e9fb56ec7ade62c3a2d8d270a Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 14 Dec 2020 14:56:03 +0100 Subject: [PATCH 4/5] each contour a different color --- examples/threshold_contour.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/threshold_contour.py b/examples/threshold_contour.py index f5cd88b..7ace14c 100644 --- a/examples/threshold_contour.py +++ b/examples/threshold_contour.py @@ -5,6 +5,7 @@ property is used to add scatter traces that represent the contour. """ +import plotly import dash import dash_html_components as html import dash_core_components as dcc @@ -48,12 +49,15 @@ def apply_levels(level, state): slice = vol[state["index"]] contours = measure.find_contours(slice, level) traces = [] - for contour in contours: + for i, contour in enumerate(contours): + # Create a trace for each contour, each a different color + colors = plotly.colors.qualitative.D3 + color = colors[i % len(colors)] traces.append( { "type": "scatter", "mode": "lines", - "line": {"color": "cyan", "width": 3}, + "line": {"color": color, "width": 3}, "x": contour[:, 1], "y": contour[:, 0], } From b91edb510a0a936d157db3bb55d8039c99571600 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 14 Dec 2020 14:58:14 +0100 Subject: [PATCH 5/5] change the wording in the docstring a bit --- examples/threshold_contour.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/threshold_contour.py b/examples/threshold_contour.py index 7ace14c..ffda504 100644 --- a/examples/threshold_contour.py +++ b/examples/threshold_contour.py @@ -1,8 +1,8 @@ """ An example demonstrating adding traces. -This shows a volume with a contour overlaid on top. The `extra_traces` -property is used to add scatter traces that represent the contour. +This shows a volume with contours overlaid on top. The `extra_traces` +property is used to add scatter traces that represent the contours. """ import plotly @@ -48,9 +48,9 @@ def apply_levels(level, state): return dash.no_update slice = vol[state["index"]] contours = measure.find_contours(slice, level) + # Create a trace for each contour, each a different color traces = [] for i, contour in enumerate(contours): - # Create a trace for each contour, each a different color colors = plotly.colors.qualitative.D3 color = colors[i % len(colors)] traces.append(