From 820521efdd3179c93abb53d695ebc93b972c76b0 Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Thu, 23 Apr 2020 22:43:59 +0200 Subject: [PATCH 1/4] documentation on shape drawing --- doc/python/configuration-options.md | 29 +++++++++- doc/python/images.md | 57 ++++++++++++++++++- doc/python/imshow.md | 51 ++++++++++++++++- doc/python/shapes.md | 86 ++++++++++++++++++++++++++++- 4 files changed, 216 insertions(+), 7 deletions(-) diff --git a/doc/python/configuration-options.md b/doc/python/configuration-options.md index b5b174dfc8a..c8f46b7caaa 100644 --- a/doc/python/configuration-options.md +++ b/doc/python/configuration-options.md @@ -5,8 +5,8 @@ jupyter: text_representation: extension: .md format_name: markdown - format_version: '1.1' - jupytext_version: 1.1.1 + format_version: '1.2' + jupytext_version: 1.3.0 kernelspec: display_name: Python 3 language: python @@ -20,7 +20,7 @@ jupyter: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.7.0 + version: 3.7.3 plotly: description: How to set the configuration options of figures using the Plotly Python graphing library. @@ -236,6 +236,29 @@ fig.show(config={ }) ``` +### Add optional shape-drawing buttons to modebar + +Some modebar buttons of Cartesian plots are optional and have to be added explictly, using the `modeBarButtonsToAdd` config attribute. These buttons are used for drawing or erasing shapes. See [the tutorial on shapes and shape drawing](python/shapes#drawing-shapes-on-cartesian-plots) for more details. + +```python +import plotly.graph_objects as go +import plotly.express as px +df = px.data.iris() +fig = px.scatter(df, x='petal_width', y='sepal_length', color='species') +fig.update_layout( + dragmode='drawopenpath', + newshape_line_color='cyan', + title_text='Draw a path to separate versicolor and virginica' +) +fig.show(config={'modeBarButtonsToAdd':['drawline', + 'drawopenpath', + 'drawclosedpath', + 'drawcircle', + 'drawrect', + 'eraseshape' + ]}) +``` + ### Double-Click Delay Sets the maximum delay between two consecutive clicks to be interpreted as a double-click in milliseconds. This is the time interval between first mousedown and second mouseup. The default timing is 300 ms (less than half a second). This setting propagates to all on-subplot double clicks (except for `geo` and `mapbox`). diff --git a/doc/python/images.md b/doc/python/images.md index 108d88022ad..9f523fed2a8 100644 --- a/doc/python/images.md +++ b/doc/python/images.md @@ -6,7 +6,7 @@ jupyter: extension: .md format_name: markdown format_version: '1.2' - jupytext_version: 1.3.2 + jupytext_version: 1.3.0 kernelspec: display_name: Python 3 language: python @@ -305,5 +305,60 @@ fig.update_layout( fig.show(config={'doubleClick': 'reset'}) ``` +### Annotating layout image with shapes + +_introduced in plotly 4.7_ + +It can be useful to add shapes to a layout image, for highlighting an object, drawing bounding boxes as part of a machine learning training set, or identifying seeds for a segmentation algorithm. + +In order to enable shape drawing, you need to +- define a dragmode corresponding to a drawing tool (`'drawline'`,`'drawopenpath'`, `'drawclosedpath'`, `'drawcircle'`, or `'drawrect'`) +- add modebar buttons corresponding to the drawing tools you wish to use. + +The style of new shapes is specified by the `newshape` layout attribute. Shapes can be selected and modified after they have been drawn. More details and examples are given in the [tutorial on shapes](/python/shapes#drawing-shapes-on-cartesian-plots). + +Drawing or modifying a shape triggers a `relayout` event, which [can be captured by a callback inside a Dash application](https://dash.plotly.com/interactive-graphing). + +```python +import plotly.graph_objects as go +fig = go.Figure() +# Add image +img_width = 1600 +img_height = 900 +scale_factor = 0.5 +fig.add_layout_image( + dict( + x=0, + sizex=img_width, + y=0, + sizey=img_height, + xref="x", + yref="y", + opacity=1.0, + layer="below", + source="https://raw.githubusercontent.com/michaelbabyn/plot_data/master/bridge.jpg") +) +fig.update_xaxes(showgrid=False, range=(0, img_width)) +fig.update_yaxes(showgrid=False, scaleanchor='x', range=(img_height, 0)) +# Line shape added programatically +fig.add_shape( + type='line', xref='x', yref='y', + x0=650, x1=1080, y0=380, y1=180, line_color='cyan' +) +# Set dragmode and newshape properties; add modebar buttons +fig.update_layout( + dragmode='drawrect', + newshape=dict(line_color='cyan'), + title_text='Drag to add annotations - use modebar to change drawing tool' +) +fig.show(config={'modeBarButtonsToAdd':['drawline', + 'drawopenpath', + 'drawclosedpath', + 'drawcircle', + 'drawrect', + 'eraseshape' + ]}) +``` + #### Reference See https://plotly.com/python/reference/#layout-images for more information and chart attribute options! diff --git a/doc/python/imshow.md b/doc/python/imshow.md index 41914363dc9..cc5b8175789 100644 --- a/doc/python/imshow.md +++ b/doc/python/imshow.md @@ -6,7 +6,7 @@ jupyter: extension: .md format_name: markdown format_version: '1.2' - jupytext_version: 1.3.1 + jupytext_version: 1.3.0 kernelspec: display_name: Python 3 language: python @@ -20,7 +20,7 @@ jupyter: name: python nbconvert_exporter: python pygments_lexer: ipython3 - version: 3.6.8 + version: 3.7.3 plotly: description: How to display image data in Python with Plotly. display_as: scientific @@ -260,6 +260,53 @@ imshow. See the [plotly and datashader tutorial](/python/datashader/) for examples on how to use plotly and datashader. +### Annotating image traces with shapes + +_introduced in plotly 4.7_ + +It can be useful to add shapes to an image trace, for highlighting an object, drawing bounding boxes as part of a machine learning training set, or identifying seeds for a segmentation algorithm. + +In order to enable shape drawing, you need to +- define a dragmode corresponding to a drawing tool (`'drawline'`,`'drawopenpath'`, `'drawclosedpath'`, `'drawcircle'`, or `'drawrect'`) +- add modebar buttons corresponding to the drawing tools you wish to use. + +The style of new shapes is specified by the `newshape` layout attribute. Shapes can be selected and modified after they have been drawn. More details and examples are given in the [tutorial on shapes](/python/shapes#drawing-shapes-on-cartesian-plots). + +Drawing or modifying a shape triggers a `relayout` event, which [can be captured by a callback inside a Dash application](https://dash.plotly.com/interactive-graphing). + +```python +import plotly.express as px +from skimage import data +img = data.chelsea() +fig = px.imshow(img) +fig.add_annotation( + x=0.5, + y=0.9, + text="Drag and draw annotations", + xref="paper", + yref="paper", + showarrow=False, + font_size=20, font_color='cyan') +# Shape defined programatically +fig.add_shape( + type='rect', + x0=230, x1=290, y0=230, y1=280, + xref='x', yref='y', + line_color='cyan' +) +# Define dragmode, newshape parameters, amd add modebar buttons +fig.update_layout( + dragmode='drawrect', + newshape=dict(line_color='cyan')) +fig.show(config={'modeBarButtonsToAdd':['drawline', + 'drawopenpath', + 'drawclosedpath', + 'drawcircle', + 'drawrect', + 'eraseshape' + ]}) +``` + #### Reference See https://plotly.com/python/reference/#image for more information and chart attribute options! diff --git a/doc/python/shapes.md b/doc/python/shapes.md index 2b5301b4865..cd66e095dfd 100644 --- a/doc/python/shapes.md +++ b/doc/python/shapes.md @@ -6,7 +6,7 @@ jupyter: extension: .md format_name: markdown format_version: '1.2' - jupytext_version: 1.3.2 + jupytext_version: 1.3.0 kernelspec: display_name: Python 3 language: python @@ -716,5 +716,89 @@ fig.update_layout( fig.show() ``` +### Drawing shapes on Cartesian plots + +_introduced in plotly 4.7_ + +You can create layout shapes programatically, but you can also draw shapes manually by setting the `dragmode` to one of the shape-drawing modes: `'drawline'`,`'drawopenpath'`, `'drawclosedpath'`, `'drawcircle'`, or `'drawrect'`. If you need to switch between different shape-drawing or other dragmodes (panning, selecting, etc.), modebar buttons can be added in the `config` to select the dragmode. + +This shape-drawing feature is particularly interesting for annotating graphs, in particular [image traces](/python/imshow) or [layout images](/python/images). + +Once you have drawn shapes, you can select and modify an existing shape by clicking on its boundary (note the arrow pointer). Its fillcolor turns to pink to highlight the activated shape and then you can +- drag and resize it for lines, rectangles and circles/ellipses +- drag and move individual vertices for closed paths +- move individual vertices for open paths. + +An activated shape is deleted by cliking on the `eraseshape` button. + +Drawing or modifying a shape triggers a `relayout` event, which [can be captured by a callback inside a Dash application](https://dash.plotly.com/interactive-graphing). + +```python +import plotly.graph_objects as go +fig = go.Figure() +text="Click and drag here
to draw a rectangle

or select another shape
in the modebar" +fig.add_annotation( + x=0.5, + y=0.5, + text=text, + xref="paper", + yref="paper", + showarrow=False, + font_size=20 +) +# shape defined programatically +fig.add_shape(editable=True, + x0=-1, x1=0, y0=2, y1=3, + xref='x1', yref='y1') +# define dragmode and add modebar buttons +fig.update_layout(dragmode='drawrect') +fig.show(config={'modeBarButtonsToAdd':['drawline', + 'drawopenpath', + 'drawclosedpath', + 'drawcircle', + 'drawrect', + 'eraseshape' + ]}) +``` + +### Style of user-drawn shapes + +The layout `newshape` attribute controls the visual appearance of new shapes drawn by the user. `newshape` attributes have the same names as layout shapes. + +```python +import plotly.graph_objects as go +fig = go.Figure() +text="Click and drag
to draw a rectangle

or select another shape
in the modebar" +fig.add_annotation( + x=0.5, + y=0.5, + text=text, + xref="paper", + yref="paper", + showarrow=False, + font_size=20 +) +# shape defined programatically +fig.add_shape(line_color='yellow', + fillcolor='turquoise', + opacity=0.8, + editable=True, + x0=0, x1=1, y0=2, y1=3, + xref='x1', yref='y1' +) +fig.update_layout(dragmode='drawrect', + # style of new shapes + newshape=dict(line_color='yellow', + fillcolor='turquoise', + opacity=0.8)) +fig.show(config={'modeBarButtonsToAdd':['drawline', + 'drawopenpath', + 'drawclosedpath', + 'drawcircle', + 'drawrect', + 'eraseshape' + ]}) +``` + ### Reference See https://plotly.com/python/reference/#layout-shapes for more information and chart attribute options! From 70e37fb070683ac6e4acb3fe2dc324a0a6ad8c5a Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Thu, 23 Apr 2020 22:48:44 +0200 Subject: [PATCH 2/4] skeleton of changelog addition --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab4811baae3..a0b37aada51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Dev version +### Updated + +- Updated Plotly.js to version 1.54.0. The main feature of the Plotly.js is the + possibility to draw layout shapes, using custom dragmodes and corresponding + modebar buttons. + ### Added - The `hover_data` parameter of `px` functions can now be a dictionary. This From 65d0428e08ca8d54e5296f7ef11309da1a8796ba Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Fri, 24 Apr 2020 12:24:08 +0200 Subject: [PATCH 3/4] more explanations --- doc/python/shapes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/python/shapes.md b/doc/python/shapes.md index cd66e095dfd..51d2ec46ec1 100644 --- a/doc/python/shapes.md +++ b/doc/python/shapes.md @@ -720,7 +720,7 @@ fig.show() _introduced in plotly 4.7_ -You can create layout shapes programatically, but you can also draw shapes manually by setting the `dragmode` to one of the shape-drawing modes: `'drawline'`,`'drawopenpath'`, `'drawclosedpath'`, `'drawcircle'`, or `'drawrect'`. If you need to switch between different shape-drawing or other dragmodes (panning, selecting, etc.), modebar buttons can be added in the `config` to select the dragmode. +You can create layout shapes programatically, but you can also draw shapes manually by setting the `dragmode` to one of the shape-drawing modes: `'drawline'`,`'drawopenpath'`, `'drawclosedpath'`, `'drawcircle'`, or `'drawrect'`. If you need to switch between different shape-drawing or other dragmodes (panning, selecting, etc.), modebar buttons can be added in the `config` to select the dragmode. If you switch to a different dragmode such as pan or zoom, you will need to select the drawing tool in the modebar to go back to shape drawing. This shape-drawing feature is particularly interesting for annotating graphs, in particular [image traces](/python/imshow) or [layout images](/python/images). From 8b9ae38f38df3a135d0b871146c4609be6709d4c Mon Sep 17 00:00:00 2001 From: Emmanuelle Gouillart Date: Thu, 30 Apr 2020 16:27:51 +0200 Subject: [PATCH 4/4] addressed review --- doc/python/images.md | 5 ++--- doc/python/shapes.md | 8 +++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/python/images.md b/doc/python/images.md index 9f523fed2a8..5d63c0ebb9c 100644 --- a/doc/python/images.md +++ b/doc/python/images.md @@ -313,7 +313,7 @@ It can be useful to add shapes to a layout image, for highlighting an object, dr In order to enable shape drawing, you need to - define a dragmode corresponding to a drawing tool (`'drawline'`,`'drawopenpath'`, `'drawclosedpath'`, `'drawcircle'`, or `'drawrect'`) -- add modebar buttons corresponding to the drawing tools you wish to use. +- add [modebar buttons](/python/configuration-options#add-optional-shapedrawing-buttons-to-modebar) corresponding to the drawing tools you wish to use. The style of new shapes is specified by the `newshape` layout attribute. Shapes can be selected and modified after they have been drawn. More details and examples are given in the [tutorial on shapes](/python/shapes#drawing-shapes-on-cartesian-plots). @@ -327,7 +327,6 @@ img_width = 1600 img_height = 900 scale_factor = 0.5 fig.add_layout_image( - dict( x=0, sizex=img_width, y=0, @@ -336,7 +335,7 @@ fig.add_layout_image( yref="y", opacity=1.0, layer="below", - source="https://raw.githubusercontent.com/michaelbabyn/plot_data/master/bridge.jpg") + source="https://raw.githubusercontent.com/michaelbabyn/plot_data/master/bridge.jpg" ) fig.update_xaxes(showgrid=False, range=(0, img_width)) fig.update_yaxes(showgrid=False, scaleanchor='x', range=(img_height, 0)) diff --git a/doc/python/shapes.md b/doc/python/shapes.md index 51d2ec46ec1..6338506e741 100644 --- a/doc/python/shapes.md +++ b/doc/python/shapes.md @@ -720,7 +720,7 @@ fig.show() _introduced in plotly 4.7_ -You can create layout shapes programatically, but you can also draw shapes manually by setting the `dragmode` to one of the shape-drawing modes: `'drawline'`,`'drawopenpath'`, `'drawclosedpath'`, `'drawcircle'`, or `'drawrect'`. If you need to switch between different shape-drawing or other dragmodes (panning, selecting, etc.), modebar buttons can be added in the `config` to select the dragmode. If you switch to a different dragmode such as pan or zoom, you will need to select the drawing tool in the modebar to go back to shape drawing. +You can create layout shapes programatically, but you can also draw shapes manually by setting the `dragmode` to one of the shape-drawing modes: `'drawline'`,`'drawopenpath'`, `'drawclosedpath'`, `'drawcircle'`, or `'drawrect'`. If you need to switch between different shape-drawing or other dragmodes (panning, selecting, etc.), [modebar buttons can be added](/python/configuration-options#add-optional-shapedrawing-buttons-to-modebar) in the `config` to select the dragmode. If you switch to a different dragmode such as pan or zoom, you will need to select the drawing tool in the modebar to go back to shape drawing. This shape-drawing feature is particularly interesting for annotating graphs, in particular [image traces](/python/imshow) or [layout images](/python/images). @@ -765,6 +765,8 @@ fig.show(config={'modeBarButtonsToAdd':['drawline', The layout `newshape` attribute controls the visual appearance of new shapes drawn by the user. `newshape` attributes have the same names as layout shapes. +_Note on shape opacity_: having a new shape's opacity > 0.5 makes it possible to activate a shape by clicking inside the shape (for opacity <= 0.5 you have to click on the border of the shape), but you cannot start a new shape within an existing shape (which is possible for an opacity <= 0.5). + ```python import plotly.graph_objects as go fig = go.Figure() @@ -781,7 +783,7 @@ fig.add_annotation( # shape defined programatically fig.add_shape(line_color='yellow', fillcolor='turquoise', - opacity=0.8, + opacity=0.4, editable=True, x0=0, x1=1, y0=2, y1=3, xref='x1', yref='y1' @@ -790,7 +792,7 @@ fig.update_layout(dragmode='drawrect', # style of new shapes newshape=dict(line_color='yellow', fillcolor='turquoise', - opacity=0.8)) + opacity=0.5)) fig.show(config={'modeBarButtonsToAdd':['drawline', 'drawopenpath', 'drawclosedpath',