diff --git a/packages/python/plotly/codegen/__init__.py b/packages/python/plotly/codegen/__init__.py
index d33b785bf8f..5b739f4c7c9 100644
--- a/packages/python/plotly/codegen/__init__.py
+++ b/packages/python/plotly/codegen/__init__.py
@@ -143,6 +143,12 @@ def perform_codegen():
if node.node_data.get("_isSubplotObj", False)
]
+ layout_array_nodes = [
+ node
+ for node in layout_node.child_compound_datatypes
+ if node.is_array_element and node.has_child("xref") and node.has_child("yref")
+ ]
+
# ### FrameNode ###
compound_frame_nodes = PlotlyNode.get_all_compound_datatype_nodes(
plotly_schema, FrameNode
@@ -207,6 +213,7 @@ def perform_codegen():
layout_validator,
frame_validator,
subplot_nodes,
+ layout_array_nodes,
)
# Write datatype __init__.py files
diff --git a/packages/python/plotly/codegen/figure.py b/packages/python/plotly/codegen/figure.py
index ab1e1c9b748..034145c01f2 100644
--- a/packages/python/plotly/codegen/figure.py
+++ b/packages/python/plotly/codegen/figure.py
@@ -25,6 +25,7 @@ def build_figure_py(
layout_validator,
frame_validator,
subplot_nodes,
+ layout_array_nodes,
):
"""
@@ -47,6 +48,8 @@ def build_figure_py(
FrameValidator instance
subplot_nodes: list of str
List of names of all of the layout subplot properties
+ layout_array_nodes: list of PlotlyNode
+ List of array nodes under layout that can be positioned using xref/yref
Returns
-------
str
@@ -66,8 +69,10 @@ def build_figure_py(
# ### Import base class ###
buffer.write(f"from plotly.{base_package} import {base_classname}\n")
- # ### Import trace graph_obj classes ###
- trace_types_csv = ", ".join([n.name_datatype_class for n in trace_nodes])
+ # ### Import trace graph_obj classes / layout ###
+ trace_types_csv = ", ".join(
+ [n.name_datatype_class for n in trace_nodes] + ["layout as _layout"]
+ )
buffer.write(f"from plotly.graph_objs import ({trace_types_csv})\n")
# Write class definition
@@ -353,6 +358,239 @@ def update_{plural_name}(
return self"""
)
+ # update annotations/shapes/images
+ # --------------------------------
+ for node in layout_array_nodes:
+ singular_name = node.plotly_name
+ plural_name = node.name_property
+
+ if singular_name == "image":
+ # Rename image to layout_image to avoid conflict with an image trace
+ method_prefix = "layout_"
+ else:
+ method_prefix = ""
+
+ buffer.write(
+ f"""
+ def select_{method_prefix}{plural_name}(
+ self, selector=None, row=None, col=None, secondary_y=None
+ ):
+ \"\"\"
+ Select {plural_name} from a particular subplot cell and/or {plural_name}
+ that satisfy custom selection criteria.
+
+ Parameters
+ ----------
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Annotations will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all {plural_name} are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of {plural_name} to select.
+ To select {plural_name} by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ {singular_name} that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all {plural_name} are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select {plural_name} associated with the secondary
+ y-axis of the subplot.
+ * If False, only select {plural_name} associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter {plural_name} based on secondary
+ y-axis.
+
+ To select {plural_name} by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ generator
+ Generator that iterates through all of the {plural_name} that satisfy
+ all of the specified selection criteria
+ \"\"\"
+ return self._select_annotations_like(
+ "{plural_name}", selector=selector, row=row, col=col, secondary_y=secondary_y
+ )
+
+ def for_each_{method_prefix}{singular_name}(
+ self, fn, selector=None, row=None, col=None, secondary_y=None
+ ):
+ \"\"\"
+ Apply a function to all {plural_name} that satisfy the specified selection
+ criteria
+
+ Parameters
+ ----------
+ fn:
+ Function that inputs a single {singular_name} object.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all {plural_name} are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of {plural_name} to select.
+ To select {plural_name} by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ {plural_name} that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all {plural_name} are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select {plural_name} associated with the secondary
+ y-axis of the subplot.
+ * If False, only select {plural_name} associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter {plural_name} based on secondary
+ y-axis.
+
+ To select {plural_name} by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ \"\"\"
+ for obj in self._select_annotations_like(
+ prop='{plural_name}',
+ selector=selector,
+ row=row,
+ col=col,
+ secondary_y=secondary_y,
+ ):
+ fn(obj)
+
+ return self
+
+ def update_{method_prefix}{plural_name}(
+ self,
+ patch,
+ selector=None,
+ row=None,
+ col=None,
+ secondary_y=None,
+ **kwargs
+ ):
+ \"\"\"
+ Perform a property update operation on all {plural_name} that satisfy the
+ specified selection criteria
+
+ Parameters
+ ----------
+ patch: dict or None (default None)
+ Dictionary of property updates to be applied to all {plural_name} that
+ satisfy the selection criteria.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all {plural_name} are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of {plural_name} to select.
+ To select {plural_name} by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ {singular_name} that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all {plural_name} are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select {plural_name} associated with the secondary
+ y-axis of the subplot.
+ * If False, only select {plural_name} associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter {plural_name} based on secondary
+ y-axis.
+
+ To select {plural_name} by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ **kwargs
+ Additional property updates to apply to each selected {singular_name}. If
+ a property is specified in both patch and in **kwargs then the
+ one in **kwargs takes precedence.
+
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ \"\"\"
+ for obj in self._select_annotations_like(
+ prop='{plural_name}',
+ selector=selector,
+ row=row,
+ col=col,
+ secondary_y=secondary_y,
+ ):
+ obj.update(patch, **kwargs)
+
+ return self
+"""
+ )
+ # Add layout array items
+ buffer.write(
+ f"""
+ def add_{method_prefix}{singular_name}(self"""
+ )
+ add_constructor_params(
+ buffer,
+ node.child_datatypes,
+ prepend_extras=["arg"],
+ append_extras=["row", "col", "secondary_y"],
+ )
+
+ prepend_extras = [
+ (
+ "arg",
+ f"instance of {node.name_datatype_class} or dict with "
+ "compatible properties",
+ )
+ ]
+ append_extras = [
+ ("row", f"Subplot row for {singular_name}"),
+ ("col", f"Subplot column for {singular_name}"),
+ ("secondary_y", f"Whether to add {singular_name} to secondary y-axis"),
+ ]
+ add_docstring(
+ buffer,
+ node,
+ header=f"Create and add a new {singular_name} to the figure's layout",
+ prepend_extras=prepend_extras,
+ append_extras=append_extras,
+ return_type=fig_classname,
+ )
+
+ # #### Function body ####
+ buffer.write(
+ f"""
+ new_obj = _layout.{node.name_datatype_class}(arg,
+ """
+ )
+
+ for i, subtype_node in enumerate(node.child_datatypes):
+ subtype_prop_name = subtype_node.name_property
+ buffer.write(
+ f"""
+ {subtype_prop_name}={subtype_prop_name},"""
+ )
+
+ buffer.write("""**kwargs)""")
+
+ buffer.write(
+ f"""
+ return self._add_annotation_like(
+ '{singular_name}',
+ '{plural_name}',
+ new_obj,
+ row=row,
+ col=col,
+ secondary_y=secondary_y,
+ )"""
+ )
+
# Return source string
# --------------------
buffer.write("\n")
@@ -360,7 +598,13 @@ def update_{plural_name}(
def write_figure_classes(
- outdir, trace_node, data_validator, layout_validator, frame_validator, subplot_nodes
+ outdir,
+ trace_node,
+ data_validator,
+ layout_validator,
+ frame_validator,
+ subplot_nodes,
+ layout_array_nodes,
):
"""
Construct source code for the Figure and FigureWidget classes and
@@ -380,9 +624,10 @@ def write_figure_classes(
LayoutValidator instance
frame_validator : CompoundArrayValidator
FrameValidator instance
- subplot_nodes: list of str
+ subplot_nodes: list of PlotlyNode
List of names of all of the layout subplot properties
-
+ layout_array_nodes: list of PlotlyNode
+ List of array nodes under layout that can be positioned using xref/yref
Returns
-------
None
@@ -415,6 +660,7 @@ def write_figure_classes(
layout_validator,
frame_validator,
subplot_nodes,
+ layout_array_nodes,
)
# ### Format and write to file###
diff --git a/packages/python/plotly/codegen/utils.py b/packages/python/plotly/codegen/utils.py
index f60be8391ee..1a689972575 100644
--- a/packages/python/plotly/codegen/utils.py
+++ b/packages/python/plotly/codegen/utils.py
@@ -893,6 +893,12 @@ def child_literals(self) -> List["PlotlyNode"]:
"""
return [n for n in self.children if n.is_literal]
+ def has_child(self, name) -> bool:
+ """
+ Check whether node has child of the specified name
+ """
+ return bool([n for n in self.children if n.plotly_name == name])
+
def get_constructor_params_docstring(self, indent=12):
"""
Return a docstring-style string containing the names and
diff --git a/packages/python/plotly/plotly/basedatatypes.py b/packages/python/plotly/plotly/basedatatypes.py
index a423ae11978..fcbd7f924f2 100644
--- a/packages/python/plotly/plotly/basedatatypes.py
+++ b/packages/python/plotly/plotly/basedatatypes.py
@@ -978,6 +978,119 @@ def _select_layout_subplots_by_prefix(
yield self.layout[k]
+ def _select_annotations_like(
+ self, prop, selector=None, row=None, col=None, secondary_y=None
+ ):
+ """
+ Helper to select annotation-like elements from a layout object array.
+ Compatible with layout.annotations, layout.shapes, and layout.images
+ """
+ xref_to_col = {}
+ yref_to_row = {}
+ yref_to_secondary_y = {}
+ if isinstance(row, int) or isinstance(col, int) or secondary_y is not None:
+ grid_ref = self._validate_get_grid_ref()
+ for r, subplot_row in enumerate(grid_ref):
+ for c, subplot_refs in enumerate(subplot_row):
+ if not subplot_refs:
+ continue
+
+ for i, subplot_ref in enumerate(subplot_refs):
+ if subplot_ref.subplot_type == "xy":
+ is_secondary_y = i == 1
+ xaxis, yaxis = subplot_ref.layout_keys
+ xref = xaxis.replace("axis", "")
+ yref = yaxis.replace("axis", "")
+ xref_to_col[xref] = c + 1
+ yref_to_row[yref] = r + 1
+ yref_to_secondary_y[yref] = is_secondary_y
+
+ for obj in self.layout[prop]:
+ # Filter by row
+ if col is not None:
+ if col == "paper" and obj.xref != "paper":
+ continue
+ elif col != "paper" and xref_to_col.get(obj.xref, None) != col:
+ continue
+
+ # Filter by col
+ if row is not None:
+ if row == "paper" and obj.yref != "paper":
+ continue
+ elif row != "paper" and yref_to_row.get(obj.yref, None) != row:
+ continue
+
+ # Filter by secondary y
+ if (
+ secondary_y is not None
+ and yref_to_secondary_y.get(obj.yref, None) != secondary_y
+ ):
+ continue
+
+ # Filter by selector
+ if not self._selector_matches(obj, selector):
+ continue
+
+ yield obj
+
+ def _add_annotation_like(
+ self, prop_singular, prop_plural, new_obj, row=None, col=None, secondary_y=None
+ ):
+ # Make sure we have both row and col or neither
+ if row is not None and col is None:
+ raise ValueError(
+ "Received row parameter but not col.\n"
+ "row and col must be specified together"
+ )
+ elif col is not None and row is None:
+ raise ValueError(
+ "Received col parameter but not row.\n"
+ "row and col must be specified together"
+ )
+
+ # Get grid_ref if specific row or column requested
+ if row is not None:
+ grid_ref = self._validate_get_grid_ref()
+ refs = grid_ref[row - 1][col - 1]
+
+ if not refs:
+ raise ValueError(
+ "No subplot found at position ({r}, {c})".format(r=row, c=col)
+ )
+
+ if refs[0].subplot_type != "xy":
+ raise ValueError(
+ """
+Cannot add {prop_singular} to subplot at position ({r}, {c}) because subplot
+is of type {subplot_type}.""".format(
+ prop_singular=prop_singular,
+ r=row,
+ c=col,
+ subplot_type=refs[0].subplot_type,
+ )
+ )
+ if len(refs) == 1 and secondary_y:
+ raise ValueError(
+ """
+Cannot add {prop_singular} to secondary y-axis of subplot at position ({r}, {c})
+because subplot does not have a secondary y-axis"""
+ )
+ if secondary_y:
+ xaxis, yaxis = refs[1].layout_keys
+ else:
+ xaxis, yaxis = refs[0].layout_keys
+ xref, yref = xaxis.replace("axis", ""), yaxis.replace("axis", "")
+ new_obj.update(xref=xref, yref=yref)
+
+ if new_obj.xref is None:
+ new_obj.xref = "paper"
+ if new_obj.yref is None:
+ new_obj.yref = "paper"
+
+ self.layout[prop_plural] += (new_obj,)
+
+ return self
+
# Restyle
# -------
def plotly_restyle(self, restyle_data, trace_indexes=None, **kwargs):
@@ -1471,13 +1584,6 @@ def add_trace(self, trace, row=None, col=None, secondary_y=None):
>>> fig.add_trace(go.Scatter(x=[1,2,3], y=[2,1,2]), row=1, col=1)
>>> fig.add_trace(go.Scatter(x=[1,2,3], y=[2,1,2]), row=2, col=1)
"""
- # Validate row/col
- if row is not None and not isinstance(row, int):
- pass
-
- if col is not None and not isinstance(col, int):
- pass
-
# Make sure we have both row and col or neither
if row is not None and col is None:
raise ValueError(
diff --git a/packages/python/plotly/plotly/graph_objs/_figure.py b/packages/python/plotly/plotly/graph_objs/_figure.py
index a3890db0188..e037b75363d 100644
--- a/packages/python/plotly/plotly/graph_objs/_figure.py
+++ b/packages/python/plotly/plotly/graph_objs/_figure.py
@@ -45,6 +45,7 @@
Violin,
Volume,
Waterfall,
+ layout as _layout,
)
@@ -15818,3 +15819,1121 @@ def update_yaxes(
obj.update(patch, **kwargs)
return self
+
+ def select_annotations(self, selector=None, row=None, col=None, secondary_y=None):
+ """
+ Select annotations from a particular subplot cell and/or annotations
+ that satisfy custom selection criteria.
+
+ Parameters
+ ----------
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Annotations will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all annotations are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of annotations to select.
+ To select annotations by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ annotation that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all annotations are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select annotations associated with the secondary
+ y-axis of the subplot.
+ * If False, only select annotations associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter annotations based on secondary
+ y-axis.
+
+ To select annotations by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ generator
+ Generator that iterates through all of the annotations that satisfy
+ all of the specified selection criteria
+ """
+ return self._select_annotations_like(
+ "annotations", selector=selector, row=row, col=col, secondary_y=secondary_y
+ )
+
+ def for_each_annotation(
+ self, fn, selector=None, row=None, col=None, secondary_y=None
+ ):
+ """
+ Apply a function to all annotations that satisfy the specified selection
+ criteria
+
+ Parameters
+ ----------
+ fn:
+ Function that inputs a single annotation object.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all annotations are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of annotations to select.
+ To select annotations by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ annotations that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all annotations are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select annotations associated with the secondary
+ y-axis of the subplot.
+ * If False, only select annotations associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter annotations based on secondary
+ y-axis.
+
+ To select annotations by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ """
+ for obj in self._select_annotations_like(
+ prop="annotations",
+ selector=selector,
+ row=row,
+ col=col,
+ secondary_y=secondary_y,
+ ):
+ fn(obj)
+
+ return self
+
+ def update_annotations(
+ self, patch, selector=None, row=None, col=None, secondary_y=None, **kwargs
+ ):
+ """
+ Perform a property update operation on all annotations that satisfy the
+ specified selection criteria
+
+ Parameters
+ ----------
+ patch: dict or None (default None)
+ Dictionary of property updates to be applied to all annotations that
+ satisfy the selection criteria.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all annotations are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of annotations to select.
+ To select annotations by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ annotation that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all annotations are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select annotations associated with the secondary
+ y-axis of the subplot.
+ * If False, only select annotations associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter annotations based on secondary
+ y-axis.
+
+ To select annotations by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ **kwargs
+ Additional property updates to apply to each selected annotation. If
+ a property is specified in both patch and in **kwargs then the
+ one in **kwargs takes precedence.
+
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ """
+ for obj in self._select_annotations_like(
+ prop="annotations",
+ selector=selector,
+ row=row,
+ col=col,
+ secondary_y=secondary_y,
+ ):
+ obj.update(patch, **kwargs)
+
+ return self
+
+ def add_annotation(
+ self,
+ arg=None,
+ align=None,
+ arrowcolor=None,
+ arrowhead=None,
+ arrowside=None,
+ arrowsize=None,
+ arrowwidth=None,
+ ax=None,
+ axref=None,
+ ay=None,
+ ayref=None,
+ bgcolor=None,
+ bordercolor=None,
+ borderpad=None,
+ borderwidth=None,
+ captureevents=None,
+ clicktoshow=None,
+ font=None,
+ height=None,
+ hoverlabel=None,
+ hovertext=None,
+ name=None,
+ opacity=None,
+ showarrow=None,
+ standoff=None,
+ startarrowhead=None,
+ startarrowsize=None,
+ startstandoff=None,
+ templateitemname=None,
+ text=None,
+ textangle=None,
+ valign=None,
+ visible=None,
+ width=None,
+ x=None,
+ xanchor=None,
+ xclick=None,
+ xref=None,
+ xshift=None,
+ y=None,
+ yanchor=None,
+ yclick=None,
+ yref=None,
+ yshift=None,
+ row=None,
+ col=None,
+ secondary_y=None,
+ **kwargs
+ ):
+ """
+ Create and add a new annotation to the figure's layout
+
+ Parameters
+ ----------
+ arg
+ instance of Annotation or dict with compatible
+ properties
+ align
+ Sets the horizontal alignment of the `text` within the
+ box. Has an effect only if `text` spans more two or
+ more lines (i.e. `text` contains one or more
HTML
+ tags) or if an explicit width is set to override the
+ text width.
+ arrowcolor
+ Sets the color of the annotation arrow.
+ arrowhead
+ Sets the end annotation arrow head style.
+ arrowside
+ Sets the annotation arrow head position.
+ arrowsize
+ Sets the size of the end annotation arrow head,
+ relative to `arrowwidth`. A value of 1 (default) gives
+ a head about 3x as wide as the line.
+ arrowwidth
+ Sets the width (in px) of annotation arrow line.
+ ax
+ Sets the x component of the arrow tail about the arrow
+ head. If `axref` is `pixel`, a positive (negative)
+ component corresponds to an arrow pointing from right
+ to left (left to right). If `axref` is an axis, this is
+ an absolute value on that axis, like `x`, NOT a
+ relative value.
+ axref
+ Indicates in what terms the tail of the annotation
+ (ax,ay) is specified. If `pixel`, `ax` is a relative
+ offset in pixels from `x`. If set to an x axis id
+ (e.g. "x" or "x2"), `ax` is specified in the same
+ terms as that axis. This is useful for trendline
+ annotations which should continue to indicate the
+ correct trend when zoomed.
+ ay
+ Sets the y component of the arrow tail about the arrow
+ head. If `ayref` is `pixel`, a positive (negative)
+ component corresponds to an arrow pointing from bottom
+ to top (top to bottom). If `ayref` is an axis, this is
+ an absolute value on that axis, like `y`, NOT a
+ relative value.
+ ayref
+ Indicates in what terms the tail of the annotation
+ (ax,ay) is specified. If `pixel`, `ay` is a relative
+ offset in pixels from `y`. If set to a y axis id (e.g.
+ "y" or "y2"), `ay` is specified in the same terms as
+ that axis. This is useful for trendline annotations
+ which should continue to indicate the correct trend
+ when zoomed.
+ bgcolor
+ Sets the background color of the annotation.
+ bordercolor
+ Sets the color of the border enclosing the annotation
+ `text`.
+ borderpad
+ Sets the padding (in px) between the `text` and the
+ enclosing border.
+ borderwidth
+ Sets the width (in px) of the border enclosing the
+ annotation `text`.
+ captureevents
+ Determines whether the annotation text box captures
+ mouse move and click events, or allows those events to
+ pass through to data points in the plot that may be
+ behind the annotation. By default `captureevents` is
+ False unless `hovertext` is provided. If you use the
+ event `plotly_clickannotation` without `hovertext` you
+ must explicitly enable `captureevents`.
+ clicktoshow
+ Makes this annotation respond to clicks on the plot. If
+ you click a data point that exactly matches the `x` and
+ `y` values of this annotation, and it is hidden
+ (visible: false), it will appear. In "onoff" mode, you
+ must click the same point again to make it disappear,
+ so if you click multiple points, you can show multiple
+ annotations. In "onout" mode, a click anywhere else in
+ the plot (on another data point or not) will hide this
+ annotation. If you need to show/hide this annotation in
+ response to different `x` or `y` values, you can set
+ `xclick` and/or `yclick`. This is useful for example to
+ label the side of a bar. To label markers though,
+ `standoff` is preferred over `xclick` and `yclick`.
+ font
+ Sets the annotation text font.
+ height
+ Sets an explicit height for the text box. null
+ (default) lets the text set the box height. Taller text
+ will be clipped.
+ hoverlabel
+ plotly.graph_objects.layout.annotation.Hoverlabel
+ instance or dict with compatible properties
+ hovertext
+ Sets text to appear when hovering over this annotation.
+ If omitted or blank, no hover label will appear.
+ name
+ When used in a template, named items are created in the
+ output figure in addition to any items the figure
+ already has in this array. You can modify these items
+ in the output figure by making your own item with
+ `templateitemname` matching this `name` alongside your
+ modifications (including `visible: false` or `enabled:
+ false` to hide it). Has no effect outside of a
+ template.
+ opacity
+ Sets the opacity of the annotation (text + arrow).
+ showarrow
+ Determines whether or not the annotation is drawn with
+ an arrow. If True, `text` is placed near the arrow's
+ tail. If False, `text` lines up with the `x` and `y`
+ provided.
+ standoff
+ Sets a distance, in pixels, to move the end arrowhead
+ away from the position it is pointing at, for example
+ to point at the edge of a marker independent of zoom.
+ Note that this shortens the arrow from the `ax` / `ay`
+ vector, in contrast to `xshift` / `yshift` which moves
+ everything by this amount.
+ startarrowhead
+ Sets the start annotation arrow head style.
+ startarrowsize
+ Sets the size of the start annotation arrow head,
+ relative to `arrowwidth`. A value of 1 (default) gives
+ a head about 3x as wide as the line.
+ startstandoff
+ Sets a distance, in pixels, to move the start arrowhead
+ away from the position it is pointing at, for example
+ to point at the edge of a marker independent of zoom.
+ Note that this shortens the arrow from the `ax` / `ay`
+ vector, in contrast to `xshift` / `yshift` which moves
+ everything by this amount.
+ templateitemname
+ Used to refer to a named item in this array in the
+ template. Named items from the template will be created
+ even without a matching item in the input figure, but
+ you can modify one by making an item with
+ `templateitemname` matching its `name`, alongside your
+ modifications (including `visible: false` or `enabled:
+ false` to hide it). If there is no template or no
+ matching item, this item will be hidden unless you
+ explicitly show it with `visible: true`.
+ text
+ Sets the text associated with this annotation. Plotly
+ uses a subset of HTML tags to do things like newline
+ (
), bold (), italics (), hyperlinks
+ (). Tags , ,
+ are also supported.
+ textangle
+ Sets the angle at which the `text` is drawn with
+ respect to the horizontal.
+ valign
+ Sets the vertical alignment of the `text` within the
+ box. Has an effect only if an explicit height is set to
+ override the text height.
+ visible
+ Determines whether or not this annotation is visible.
+ width
+ Sets an explicit width for the text box. null (default)
+ lets the text set the box width. Wider text will be
+ clipped. There is no automatic wrapping; use
to
+ start a new line.
+ x
+ Sets the annotation's x position. If the axis `type` is
+ "log", then you must take the log of your desired
+ range. If the axis `type` is "date", it should be date
+ strings, like date data, though Date objects and unix
+ milliseconds will be accepted and converted to strings.
+ If the axis `type` is "category", it should be numbers,
+ using the scale where each category is assigned a
+ serial number from zero in the order it appears.
+ xanchor
+ Sets the text box's horizontal position anchor This
+ anchor binds the `x` position to the "left", "center"
+ or "right" of the annotation. For example, if `x` is
+ set to 1, `xref` to "paper" and `xanchor` to "right"
+ then the right-most portion of the annotation lines up
+ with the right-most edge of the plotting area. If
+ "auto", the anchor is equivalent to "center" for data-
+ referenced annotations or if there is an arrow, whereas
+ for paper-referenced with no arrow, the anchor picked
+ corresponds to the closest side.
+ xclick
+ Toggle this annotation when clicking a data point whose
+ `x` value is `xclick` rather than the annotation's `x`
+ value.
+ xref
+ Sets the annotation's x coordinate axis. If set to an x
+ axis id (e.g. "x" or "x2"), the `x` position refers to
+ an x coordinate If set to "paper", the `x` position
+ refers to the distance from the left side of the
+ plotting area in normalized coordinates where 0 (1)
+ corresponds to the left (right) side.
+ xshift
+ Shifts the position of the whole annotation and arrow
+ to the right (positive) or left (negative) by this many
+ pixels.
+ y
+ Sets the annotation's y position. If the axis `type` is
+ "log", then you must take the log of your desired
+ range. If the axis `type` is "date", it should be date
+ strings, like date data, though Date objects and unix
+ milliseconds will be accepted and converted to strings.
+ If the axis `type` is "category", it should be numbers,
+ using the scale where each category is assigned a
+ serial number from zero in the order it appears.
+ yanchor
+ Sets the text box's vertical position anchor This
+ anchor binds the `y` position to the "top", "middle" or
+ "bottom" of the annotation. For example, if `y` is set
+ to 1, `yref` to "paper" and `yanchor` to "top" then the
+ top-most portion of the annotation lines up with the
+ top-most edge of the plotting area. If "auto", the
+ anchor is equivalent to "middle" for data-referenced
+ annotations or if there is an arrow, whereas for paper-
+ referenced with no arrow, the anchor picked corresponds
+ to the closest side.
+ yclick
+ Toggle this annotation when clicking a data point whose
+ `y` value is `yclick` rather than the annotation's `y`
+ value.
+ yref
+ Sets the annotation's y coordinate axis. If set to an y
+ axis id (e.g. "y" or "y2"), the `y` position refers to
+ an y coordinate If set to "paper", the `y` position
+ refers to the distance from the bottom of the plotting
+ area in normalized coordinates where 0 (1) corresponds
+ to the bottom (top).
+ yshift
+ Shifts the position of the whole annotation and arrow
+ up (positive) or down (negative) by this many pixels.
+ row
+ Subplot row for annotation
+ col
+ Subplot column for annotation
+ secondary_y
+ Whether to add annotation to secondary y-axis
+
+ Returns
+ -------
+ Figure
+ """
+ new_obj = _layout.Annotation(
+ arg,
+ align=align,
+ arrowcolor=arrowcolor,
+ arrowhead=arrowhead,
+ arrowside=arrowside,
+ arrowsize=arrowsize,
+ arrowwidth=arrowwidth,
+ ax=ax,
+ axref=axref,
+ ay=ay,
+ ayref=ayref,
+ bgcolor=bgcolor,
+ bordercolor=bordercolor,
+ borderpad=borderpad,
+ borderwidth=borderwidth,
+ captureevents=captureevents,
+ clicktoshow=clicktoshow,
+ font=font,
+ height=height,
+ hoverlabel=hoverlabel,
+ hovertext=hovertext,
+ name=name,
+ opacity=opacity,
+ showarrow=showarrow,
+ standoff=standoff,
+ startarrowhead=startarrowhead,
+ startarrowsize=startarrowsize,
+ startstandoff=startstandoff,
+ templateitemname=templateitemname,
+ text=text,
+ textangle=textangle,
+ valign=valign,
+ visible=visible,
+ width=width,
+ x=x,
+ xanchor=xanchor,
+ xclick=xclick,
+ xref=xref,
+ xshift=xshift,
+ y=y,
+ yanchor=yanchor,
+ yclick=yclick,
+ yref=yref,
+ yshift=yshift,
+ **kwargs
+ )
+ return self._add_annotation_like(
+ "annotation",
+ "annotations",
+ new_obj,
+ row=row,
+ col=col,
+ secondary_y=secondary_y,
+ )
+
+ def select_layout_images(self, selector=None, row=None, col=None, secondary_y=None):
+ """
+ Select images from a particular subplot cell and/or images
+ that satisfy custom selection criteria.
+
+ Parameters
+ ----------
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Annotations will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all images are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of images to select.
+ To select images by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ image that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all images are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select images associated with the secondary
+ y-axis of the subplot.
+ * If False, only select images associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter images based on secondary
+ y-axis.
+
+ To select images by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ generator
+ Generator that iterates through all of the images that satisfy
+ all of the specified selection criteria
+ """
+ return self._select_annotations_like(
+ "images", selector=selector, row=row, col=col, secondary_y=secondary_y
+ )
+
+ def for_each_layout_image(
+ self, fn, selector=None, row=None, col=None, secondary_y=None
+ ):
+ """
+ Apply a function to all images that satisfy the specified selection
+ criteria
+
+ Parameters
+ ----------
+ fn:
+ Function that inputs a single image object.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all images are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of images to select.
+ To select images by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ images that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all images are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select images associated with the secondary
+ y-axis of the subplot.
+ * If False, only select images associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter images based on secondary
+ y-axis.
+
+ To select images by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ """
+ for obj in self._select_annotations_like(
+ prop="images", selector=selector, row=row, col=col, secondary_y=secondary_y
+ ):
+ fn(obj)
+
+ return self
+
+ def update_layout_images(
+ self, patch, selector=None, row=None, col=None, secondary_y=None, **kwargs
+ ):
+ """
+ Perform a property update operation on all images that satisfy the
+ specified selection criteria
+
+ Parameters
+ ----------
+ patch: dict or None (default None)
+ Dictionary of property updates to be applied to all images that
+ satisfy the selection criteria.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all images are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of images to select.
+ To select images by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ image that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all images are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select images associated with the secondary
+ y-axis of the subplot.
+ * If False, only select images associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter images based on secondary
+ y-axis.
+
+ To select images by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ **kwargs
+ Additional property updates to apply to each selected image. If
+ a property is specified in both patch and in **kwargs then the
+ one in **kwargs takes precedence.
+
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ """
+ for obj in self._select_annotations_like(
+ prop="images", selector=selector, row=row, col=col, secondary_y=secondary_y
+ ):
+ obj.update(patch, **kwargs)
+
+ return self
+
+ def add_layout_image(
+ self,
+ arg=None,
+ layer=None,
+ name=None,
+ opacity=None,
+ sizex=None,
+ sizey=None,
+ sizing=None,
+ source=None,
+ templateitemname=None,
+ visible=None,
+ x=None,
+ xanchor=None,
+ xref=None,
+ y=None,
+ yanchor=None,
+ yref=None,
+ row=None,
+ col=None,
+ secondary_y=None,
+ **kwargs
+ ):
+ """
+ Create and add a new image to the figure's layout
+
+ Parameters
+ ----------
+ arg
+ instance of Image or dict with compatible properties
+ layer
+ Specifies whether images are drawn below or above
+ traces. When `xref` and `yref` are both set to `paper`,
+ image is drawn below the entire plot area.
+ name
+ When used in a template, named items are created in the
+ output figure in addition to any items the figure
+ already has in this array. You can modify these items
+ in the output figure by making your own item with
+ `templateitemname` matching this `name` alongside your
+ modifications (including `visible: false` or `enabled:
+ false` to hide it). Has no effect outside of a
+ template.
+ opacity
+ Sets the opacity of the image.
+ sizex
+ Sets the image container size horizontally. The image
+ will be sized based on the `position` value. When
+ `xref` is set to `paper`, units are sized relative to
+ the plot width.
+ sizey
+ Sets the image container size vertically. The image
+ will be sized based on the `position` value. When
+ `yref` is set to `paper`, units are sized relative to
+ the plot height.
+ sizing
+ Specifies which dimension of the image to constrain.
+ source
+ Specifies the URL of the image to be used. The URL must
+ be accessible from the domain where the plot code is
+ run, and can be either relative or absolute.
+ templateitemname
+ Used to refer to a named item in this array in the
+ template. Named items from the template will be created
+ even without a matching item in the input figure, but
+ you can modify one by making an item with
+ `templateitemname` matching its `name`, alongside your
+ modifications (including `visible: false` or `enabled:
+ false` to hide it). If there is no template or no
+ matching item, this item will be hidden unless you
+ explicitly show it with `visible: true`.
+ visible
+ Determines whether or not this image is visible.
+ x
+ Sets the image's x position. When `xref` is set to
+ `paper`, units are sized relative to the plot height.
+ See `xref` for more info
+ xanchor
+ Sets the anchor for the x position
+ xref
+ Sets the images's x coordinate axis. If set to a x axis
+ id (e.g. "x" or "x2"), the `x` position refers to an x
+ data coordinate If set to "paper", the `x` position
+ refers to the distance from the left of plot in
+ normalized coordinates where 0 (1) corresponds to the
+ left (right).
+ y
+ Sets the image's y position. When `yref` is set to
+ `paper`, units are sized relative to the plot height.
+ See `yref` for more info
+ yanchor
+ Sets the anchor for the y position.
+ yref
+ Sets the images's y coordinate axis. If set to a y axis
+ id (e.g. "y" or "y2"), the `y` position refers to a y
+ data coordinate. If set to "paper", the `y` position
+ refers to the distance from the bottom of the plot in
+ normalized coordinates where 0 (1) corresponds to the
+ bottom (top).
+ row
+ Subplot row for image
+ col
+ Subplot column for image
+ secondary_y
+ Whether to add image to secondary y-axis
+
+ Returns
+ -------
+ Figure
+ """
+ new_obj = _layout.Image(
+ arg,
+ layer=layer,
+ name=name,
+ opacity=opacity,
+ sizex=sizex,
+ sizey=sizey,
+ sizing=sizing,
+ source=source,
+ templateitemname=templateitemname,
+ visible=visible,
+ x=x,
+ xanchor=xanchor,
+ xref=xref,
+ y=y,
+ yanchor=yanchor,
+ yref=yref,
+ **kwargs
+ )
+ return self._add_annotation_like(
+ "image", "images", new_obj, row=row, col=col, secondary_y=secondary_y
+ )
+
+ def select_shapes(self, selector=None, row=None, col=None, secondary_y=None):
+ """
+ Select shapes from a particular subplot cell and/or shapes
+ that satisfy custom selection criteria.
+
+ Parameters
+ ----------
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Annotations will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all shapes are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of shapes to select.
+ To select shapes by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ shape that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all shapes are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select shapes associated with the secondary
+ y-axis of the subplot.
+ * If False, only select shapes associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter shapes based on secondary
+ y-axis.
+
+ To select shapes by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ generator
+ Generator that iterates through all of the shapes that satisfy
+ all of the specified selection criteria
+ """
+ return self._select_annotations_like(
+ "shapes", selector=selector, row=row, col=col, secondary_y=secondary_y
+ )
+
+ def for_each_shape(self, fn, selector=None, row=None, col=None, secondary_y=None):
+ """
+ Apply a function to all shapes that satisfy the specified selection
+ criteria
+
+ Parameters
+ ----------
+ fn:
+ Function that inputs a single shape object.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all shapes are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of shapes to select.
+ To select shapes by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ shapes that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all shapes are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select shapes associated with the secondary
+ y-axis of the subplot.
+ * If False, only select shapes associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter shapes based on secondary
+ y-axis.
+
+ To select shapes by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ """
+ for obj in self._select_annotations_like(
+ prop="shapes", selector=selector, row=row, col=col, secondary_y=secondary_y
+ ):
+ fn(obj)
+
+ return self
+
+ def update_shapes(
+ self, patch, selector=None, row=None, col=None, secondary_y=None, **kwargs
+ ):
+ """
+ Perform a property update operation on all shapes that satisfy the
+ specified selection criteria
+
+ Parameters
+ ----------
+ patch: dict or None (default None)
+ Dictionary of property updates to be applied to all shapes that
+ satisfy the selection criteria.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all shapes are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of shapes to select.
+ To select shapes by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ shape that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all shapes are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select shapes associated with the secondary
+ y-axis of the subplot.
+ * If False, only select shapes associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter shapes based on secondary
+ y-axis.
+
+ To select shapes by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ **kwargs
+ Additional property updates to apply to each selected shape. If
+ a property is specified in both patch and in **kwargs then the
+ one in **kwargs takes precedence.
+
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ """
+ for obj in self._select_annotations_like(
+ prop="shapes", selector=selector, row=row, col=col, secondary_y=secondary_y
+ ):
+ obj.update(patch, **kwargs)
+
+ return self
+
+ def add_shape(
+ self,
+ arg=None,
+ fillcolor=None,
+ layer=None,
+ line=None,
+ name=None,
+ opacity=None,
+ path=None,
+ templateitemname=None,
+ type=None,
+ visible=None,
+ x0=None,
+ x1=None,
+ xanchor=None,
+ xref=None,
+ xsizemode=None,
+ y0=None,
+ y1=None,
+ yanchor=None,
+ yref=None,
+ ysizemode=None,
+ row=None,
+ col=None,
+ secondary_y=None,
+ **kwargs
+ ):
+ """
+ Create and add a new shape to the figure's layout
+
+ Parameters
+ ----------
+ arg
+ instance of Shape or dict with compatible properties
+ fillcolor
+ Sets the color filling the shape's interior.
+ layer
+ Specifies whether shapes are drawn below or above
+ traces.
+ line
+ plotly.graph_objects.layout.shape.Line instance or dict
+ with compatible properties
+ name
+ When used in a template, named items are created in the
+ output figure in addition to any items the figure
+ already has in this array. You can modify these items
+ in the output figure by making your own item with
+ `templateitemname` matching this `name` alongside your
+ modifications (including `visible: false` or `enabled:
+ false` to hide it). Has no effect outside of a
+ template.
+ opacity
+ Sets the opacity of the shape.
+ path
+ For `type` "path" - a valid SVG path with the pixel
+ values replaced by data values in
+ `xsizemode`/`ysizemode` being "scaled" and taken
+ unmodified as pixels relative to `xanchor` and
+ `yanchor` in case of "pixel" size mode. There are a few
+ restrictions / quirks only absolute instructions, not
+ relative. So the allowed segments are: M, L, H, V, Q,
+ C, T, S, and Z arcs (A) are not allowed because radius
+ rx and ry are relative. In the future we could consider
+ supporting relative commands, but we would have to
+ decide on how to handle date and log axes. Note that
+ even as is, Q and C Bezier paths that are smooth on
+ linear axes may not be smooth on log, and vice versa.
+ no chained "polybezier" commands - specify the segment
+ type for each one. On category axes, values are numbers
+ scaled to the serial numbers of categories because
+ using the categories themselves there would be no way
+ to describe fractional positions On data axes: because
+ space and T are both normal components of path strings,
+ we can't use either to separate date from time parts.
+ Therefore we'll use underscore for this purpose:
+ 2015-02-21_13:45:56.789
+ templateitemname
+ Used to refer to a named item in this array in the
+ template. Named items from the template will be created
+ even without a matching item in the input figure, but
+ you can modify one by making an item with
+ `templateitemname` matching its `name`, alongside your
+ modifications (including `visible: false` or `enabled:
+ false` to hide it). If there is no template or no
+ matching item, this item will be hidden unless you
+ explicitly show it with `visible: true`.
+ type
+ Specifies the shape type to be drawn. If "line", a line
+ is drawn from (`x0`,`y0`) to (`x1`,`y1`) with respect
+ to the axes' sizing mode. If "circle", a circle is
+ drawn from ((`x0`+`x1`)/2, (`y0`+`y1`)/2)) with radius
+ (|(`x0`+`x1`)/2 - `x0`|, |(`y0`+`y1`)/2 -`y0`)|) with
+ respect to the axes' sizing mode. If "rect", a
+ rectangle is drawn linking (`x0`,`y0`), (`x1`,`y0`),
+ (`x1`,`y1`), (`x0`,`y1`), (`x0`,`y0`) with respect to
+ the axes' sizing mode. If "path", draw a custom SVG
+ path using `path`. with respect to the axes' sizing
+ mode.
+ visible
+ Determines whether or not this shape is visible.
+ x0
+ Sets the shape's starting x position. See `type` and
+ `xsizemode` for more info.
+ x1
+ Sets the shape's end x position. See `type` and
+ `xsizemode` for more info.
+ xanchor
+ Only relevant in conjunction with `xsizemode` set to
+ "pixel". Specifies the anchor point on the x axis to
+ which `x0`, `x1` and x coordinates within `path` are
+ relative to. E.g. useful to attach a pixel sized shape
+ to a certain data value. No effect when `xsizemode` not
+ set to "pixel".
+ xref
+ Sets the shape's x coordinate axis. If set to an x axis
+ id (e.g. "x" or "x2"), the `x` position refers to an x
+ coordinate. If set to "paper", the `x` position refers
+ to the distance from the left side of the plotting area
+ in normalized coordinates where 0 (1) corresponds to
+ the left (right) side. If the axis `type` is "log",
+ then you must take the log of your desired range. If
+ the axis `type` is "date", then you must convert the
+ date to unix time in milliseconds.
+ xsizemode
+ Sets the shapes's sizing mode along the x axis. If set
+ to "scaled", `x0`, `x1` and x coordinates within `path`
+ refer to data values on the x axis or a fraction of the
+ plot area's width (`xref` set to "paper"). If set to
+ "pixel", `xanchor` specifies the x position in terms of
+ data or plot fraction but `x0`, `x1` and x coordinates
+ within `path` are pixels relative to `xanchor`. This
+ way, the shape can have a fixed width while maintaining
+ a position relative to data or plot fraction.
+ y0
+ Sets the shape's starting y position. See `type` and
+ `ysizemode` for more info.
+ y1
+ Sets the shape's end y position. See `type` and
+ `ysizemode` for more info.
+ yanchor
+ Only relevant in conjunction with `ysizemode` set to
+ "pixel". Specifies the anchor point on the y axis to
+ which `y0`, `y1` and y coordinates within `path` are
+ relative to. E.g. useful to attach a pixel sized shape
+ to a certain data value. No effect when `ysizemode` not
+ set to "pixel".
+ yref
+ Sets the annotation's y coordinate axis. If set to an y
+ axis id (e.g. "y" or "y2"), the `y` position refers to
+ an y coordinate If set to "paper", the `y` position
+ refers to the distance from the bottom of the plotting
+ area in normalized coordinates where 0 (1) corresponds
+ to the bottom (top).
+ ysizemode
+ Sets the shapes's sizing mode along the y axis. If set
+ to "scaled", `y0`, `y1` and y coordinates within `path`
+ refer to data values on the y axis or a fraction of the
+ plot area's height (`yref` set to "paper"). If set to
+ "pixel", `yanchor` specifies the y position in terms of
+ data or plot fraction but `y0`, `y1` and y coordinates
+ within `path` are pixels relative to `yanchor`. This
+ way, the shape can have a fixed height while
+ maintaining a position relative to data or plot
+ fraction.
+ row
+ Subplot row for shape
+ col
+ Subplot column for shape
+ secondary_y
+ Whether to add shape to secondary y-axis
+
+ Returns
+ -------
+ Figure
+ """
+ new_obj = _layout.Shape(
+ arg,
+ fillcolor=fillcolor,
+ layer=layer,
+ line=line,
+ name=name,
+ opacity=opacity,
+ path=path,
+ templateitemname=templateitemname,
+ type=type,
+ visible=visible,
+ x0=x0,
+ x1=x1,
+ xanchor=xanchor,
+ xref=xref,
+ xsizemode=xsizemode,
+ y0=y0,
+ y1=y1,
+ yanchor=yanchor,
+ yref=yref,
+ ysizemode=ysizemode,
+ **kwargs
+ )
+ return self._add_annotation_like(
+ "shape", "shapes", new_obj, row=row, col=col, secondary_y=secondary_y
+ )
diff --git a/packages/python/plotly/plotly/graph_objs/_figurewidget.py b/packages/python/plotly/plotly/graph_objs/_figurewidget.py
index af6758944b3..9e878b5c10f 100644
--- a/packages/python/plotly/plotly/graph_objs/_figurewidget.py
+++ b/packages/python/plotly/plotly/graph_objs/_figurewidget.py
@@ -45,6 +45,7 @@
Violin,
Volume,
Waterfall,
+ layout as _layout,
)
@@ -15818,3 +15819,1121 @@ def update_yaxes(
obj.update(patch, **kwargs)
return self
+
+ def select_annotations(self, selector=None, row=None, col=None, secondary_y=None):
+ """
+ Select annotations from a particular subplot cell and/or annotations
+ that satisfy custom selection criteria.
+
+ Parameters
+ ----------
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Annotations will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all annotations are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of annotations to select.
+ To select annotations by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ annotation that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all annotations are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select annotations associated with the secondary
+ y-axis of the subplot.
+ * If False, only select annotations associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter annotations based on secondary
+ y-axis.
+
+ To select annotations by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ generator
+ Generator that iterates through all of the annotations that satisfy
+ all of the specified selection criteria
+ """
+ return self._select_annotations_like(
+ "annotations", selector=selector, row=row, col=col, secondary_y=secondary_y
+ )
+
+ def for_each_annotation(
+ self, fn, selector=None, row=None, col=None, secondary_y=None
+ ):
+ """
+ Apply a function to all annotations that satisfy the specified selection
+ criteria
+
+ Parameters
+ ----------
+ fn:
+ Function that inputs a single annotation object.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all annotations are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of annotations to select.
+ To select annotations by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ annotations that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all annotations are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select annotations associated with the secondary
+ y-axis of the subplot.
+ * If False, only select annotations associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter annotations based on secondary
+ y-axis.
+
+ To select annotations by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ """
+ for obj in self._select_annotations_like(
+ prop="annotations",
+ selector=selector,
+ row=row,
+ col=col,
+ secondary_y=secondary_y,
+ ):
+ fn(obj)
+
+ return self
+
+ def update_annotations(
+ self, patch, selector=None, row=None, col=None, secondary_y=None, **kwargs
+ ):
+ """
+ Perform a property update operation on all annotations that satisfy the
+ specified selection criteria
+
+ Parameters
+ ----------
+ patch: dict or None (default None)
+ Dictionary of property updates to be applied to all annotations that
+ satisfy the selection criteria.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all annotations are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of annotations to select.
+ To select annotations by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ annotation that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all annotations are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select annotations associated with the secondary
+ y-axis of the subplot.
+ * If False, only select annotations associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter annotations based on secondary
+ y-axis.
+
+ To select annotations by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ **kwargs
+ Additional property updates to apply to each selected annotation. If
+ a property is specified in both patch and in **kwargs then the
+ one in **kwargs takes precedence.
+
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ """
+ for obj in self._select_annotations_like(
+ prop="annotations",
+ selector=selector,
+ row=row,
+ col=col,
+ secondary_y=secondary_y,
+ ):
+ obj.update(patch, **kwargs)
+
+ return self
+
+ def add_annotation(
+ self,
+ arg=None,
+ align=None,
+ arrowcolor=None,
+ arrowhead=None,
+ arrowside=None,
+ arrowsize=None,
+ arrowwidth=None,
+ ax=None,
+ axref=None,
+ ay=None,
+ ayref=None,
+ bgcolor=None,
+ bordercolor=None,
+ borderpad=None,
+ borderwidth=None,
+ captureevents=None,
+ clicktoshow=None,
+ font=None,
+ height=None,
+ hoverlabel=None,
+ hovertext=None,
+ name=None,
+ opacity=None,
+ showarrow=None,
+ standoff=None,
+ startarrowhead=None,
+ startarrowsize=None,
+ startstandoff=None,
+ templateitemname=None,
+ text=None,
+ textangle=None,
+ valign=None,
+ visible=None,
+ width=None,
+ x=None,
+ xanchor=None,
+ xclick=None,
+ xref=None,
+ xshift=None,
+ y=None,
+ yanchor=None,
+ yclick=None,
+ yref=None,
+ yshift=None,
+ row=None,
+ col=None,
+ secondary_y=None,
+ **kwargs
+ ):
+ """
+ Create and add a new annotation to the figure's layout
+
+ Parameters
+ ----------
+ arg
+ instance of Annotation or dict with compatible
+ properties
+ align
+ Sets the horizontal alignment of the `text` within the
+ box. Has an effect only if `text` spans more two or
+ more lines (i.e. `text` contains one or more
HTML
+ tags) or if an explicit width is set to override the
+ text width.
+ arrowcolor
+ Sets the color of the annotation arrow.
+ arrowhead
+ Sets the end annotation arrow head style.
+ arrowside
+ Sets the annotation arrow head position.
+ arrowsize
+ Sets the size of the end annotation arrow head,
+ relative to `arrowwidth`. A value of 1 (default) gives
+ a head about 3x as wide as the line.
+ arrowwidth
+ Sets the width (in px) of annotation arrow line.
+ ax
+ Sets the x component of the arrow tail about the arrow
+ head. If `axref` is `pixel`, a positive (negative)
+ component corresponds to an arrow pointing from right
+ to left (left to right). If `axref` is an axis, this is
+ an absolute value on that axis, like `x`, NOT a
+ relative value.
+ axref
+ Indicates in what terms the tail of the annotation
+ (ax,ay) is specified. If `pixel`, `ax` is a relative
+ offset in pixels from `x`. If set to an x axis id
+ (e.g. "x" or "x2"), `ax` is specified in the same
+ terms as that axis. This is useful for trendline
+ annotations which should continue to indicate the
+ correct trend when zoomed.
+ ay
+ Sets the y component of the arrow tail about the arrow
+ head. If `ayref` is `pixel`, a positive (negative)
+ component corresponds to an arrow pointing from bottom
+ to top (top to bottom). If `ayref` is an axis, this is
+ an absolute value on that axis, like `y`, NOT a
+ relative value.
+ ayref
+ Indicates in what terms the tail of the annotation
+ (ax,ay) is specified. If `pixel`, `ay` is a relative
+ offset in pixels from `y`. If set to a y axis id (e.g.
+ "y" or "y2"), `ay` is specified in the same terms as
+ that axis. This is useful for trendline annotations
+ which should continue to indicate the correct trend
+ when zoomed.
+ bgcolor
+ Sets the background color of the annotation.
+ bordercolor
+ Sets the color of the border enclosing the annotation
+ `text`.
+ borderpad
+ Sets the padding (in px) between the `text` and the
+ enclosing border.
+ borderwidth
+ Sets the width (in px) of the border enclosing the
+ annotation `text`.
+ captureevents
+ Determines whether the annotation text box captures
+ mouse move and click events, or allows those events to
+ pass through to data points in the plot that may be
+ behind the annotation. By default `captureevents` is
+ False unless `hovertext` is provided. If you use the
+ event `plotly_clickannotation` without `hovertext` you
+ must explicitly enable `captureevents`.
+ clicktoshow
+ Makes this annotation respond to clicks on the plot. If
+ you click a data point that exactly matches the `x` and
+ `y` values of this annotation, and it is hidden
+ (visible: false), it will appear. In "onoff" mode, you
+ must click the same point again to make it disappear,
+ so if you click multiple points, you can show multiple
+ annotations. In "onout" mode, a click anywhere else in
+ the plot (on another data point or not) will hide this
+ annotation. If you need to show/hide this annotation in
+ response to different `x` or `y` values, you can set
+ `xclick` and/or `yclick`. This is useful for example to
+ label the side of a bar. To label markers though,
+ `standoff` is preferred over `xclick` and `yclick`.
+ font
+ Sets the annotation text font.
+ height
+ Sets an explicit height for the text box. null
+ (default) lets the text set the box height. Taller text
+ will be clipped.
+ hoverlabel
+ plotly.graph_objects.layout.annotation.Hoverlabel
+ instance or dict with compatible properties
+ hovertext
+ Sets text to appear when hovering over this annotation.
+ If omitted or blank, no hover label will appear.
+ name
+ When used in a template, named items are created in the
+ output figure in addition to any items the figure
+ already has in this array. You can modify these items
+ in the output figure by making your own item with
+ `templateitemname` matching this `name` alongside your
+ modifications (including `visible: false` or `enabled:
+ false` to hide it). Has no effect outside of a
+ template.
+ opacity
+ Sets the opacity of the annotation (text + arrow).
+ showarrow
+ Determines whether or not the annotation is drawn with
+ an arrow. If True, `text` is placed near the arrow's
+ tail. If False, `text` lines up with the `x` and `y`
+ provided.
+ standoff
+ Sets a distance, in pixels, to move the end arrowhead
+ away from the position it is pointing at, for example
+ to point at the edge of a marker independent of zoom.
+ Note that this shortens the arrow from the `ax` / `ay`
+ vector, in contrast to `xshift` / `yshift` which moves
+ everything by this amount.
+ startarrowhead
+ Sets the start annotation arrow head style.
+ startarrowsize
+ Sets the size of the start annotation arrow head,
+ relative to `arrowwidth`. A value of 1 (default) gives
+ a head about 3x as wide as the line.
+ startstandoff
+ Sets a distance, in pixels, to move the start arrowhead
+ away from the position it is pointing at, for example
+ to point at the edge of a marker independent of zoom.
+ Note that this shortens the arrow from the `ax` / `ay`
+ vector, in contrast to `xshift` / `yshift` which moves
+ everything by this amount.
+ templateitemname
+ Used to refer to a named item in this array in the
+ template. Named items from the template will be created
+ even without a matching item in the input figure, but
+ you can modify one by making an item with
+ `templateitemname` matching its `name`, alongside your
+ modifications (including `visible: false` or `enabled:
+ false` to hide it). If there is no template or no
+ matching item, this item will be hidden unless you
+ explicitly show it with `visible: true`.
+ text
+ Sets the text associated with this annotation. Plotly
+ uses a subset of HTML tags to do things like newline
+ (
), bold (), italics (), hyperlinks
+ (). Tags , ,
+ are also supported.
+ textangle
+ Sets the angle at which the `text` is drawn with
+ respect to the horizontal.
+ valign
+ Sets the vertical alignment of the `text` within the
+ box. Has an effect only if an explicit height is set to
+ override the text height.
+ visible
+ Determines whether or not this annotation is visible.
+ width
+ Sets an explicit width for the text box. null (default)
+ lets the text set the box width. Wider text will be
+ clipped. There is no automatic wrapping; use
to
+ start a new line.
+ x
+ Sets the annotation's x position. If the axis `type` is
+ "log", then you must take the log of your desired
+ range. If the axis `type` is "date", it should be date
+ strings, like date data, though Date objects and unix
+ milliseconds will be accepted and converted to strings.
+ If the axis `type` is "category", it should be numbers,
+ using the scale where each category is assigned a
+ serial number from zero in the order it appears.
+ xanchor
+ Sets the text box's horizontal position anchor This
+ anchor binds the `x` position to the "left", "center"
+ or "right" of the annotation. For example, if `x` is
+ set to 1, `xref` to "paper" and `xanchor` to "right"
+ then the right-most portion of the annotation lines up
+ with the right-most edge of the plotting area. If
+ "auto", the anchor is equivalent to "center" for data-
+ referenced annotations or if there is an arrow, whereas
+ for paper-referenced with no arrow, the anchor picked
+ corresponds to the closest side.
+ xclick
+ Toggle this annotation when clicking a data point whose
+ `x` value is `xclick` rather than the annotation's `x`
+ value.
+ xref
+ Sets the annotation's x coordinate axis. If set to an x
+ axis id (e.g. "x" or "x2"), the `x` position refers to
+ an x coordinate If set to "paper", the `x` position
+ refers to the distance from the left side of the
+ plotting area in normalized coordinates where 0 (1)
+ corresponds to the left (right) side.
+ xshift
+ Shifts the position of the whole annotation and arrow
+ to the right (positive) or left (negative) by this many
+ pixels.
+ y
+ Sets the annotation's y position. If the axis `type` is
+ "log", then you must take the log of your desired
+ range. If the axis `type` is "date", it should be date
+ strings, like date data, though Date objects and unix
+ milliseconds will be accepted and converted to strings.
+ If the axis `type` is "category", it should be numbers,
+ using the scale where each category is assigned a
+ serial number from zero in the order it appears.
+ yanchor
+ Sets the text box's vertical position anchor This
+ anchor binds the `y` position to the "top", "middle" or
+ "bottom" of the annotation. For example, if `y` is set
+ to 1, `yref` to "paper" and `yanchor` to "top" then the
+ top-most portion of the annotation lines up with the
+ top-most edge of the plotting area. If "auto", the
+ anchor is equivalent to "middle" for data-referenced
+ annotations or if there is an arrow, whereas for paper-
+ referenced with no arrow, the anchor picked corresponds
+ to the closest side.
+ yclick
+ Toggle this annotation when clicking a data point whose
+ `y` value is `yclick` rather than the annotation's `y`
+ value.
+ yref
+ Sets the annotation's y coordinate axis. If set to an y
+ axis id (e.g. "y" or "y2"), the `y` position refers to
+ an y coordinate If set to "paper", the `y` position
+ refers to the distance from the bottom of the plotting
+ area in normalized coordinates where 0 (1) corresponds
+ to the bottom (top).
+ yshift
+ Shifts the position of the whole annotation and arrow
+ up (positive) or down (negative) by this many pixels.
+ row
+ Subplot row for annotation
+ col
+ Subplot column for annotation
+ secondary_y
+ Whether to add annotation to secondary y-axis
+
+ Returns
+ -------
+ FigureWidget
+ """
+ new_obj = _layout.Annotation(
+ arg,
+ align=align,
+ arrowcolor=arrowcolor,
+ arrowhead=arrowhead,
+ arrowside=arrowside,
+ arrowsize=arrowsize,
+ arrowwidth=arrowwidth,
+ ax=ax,
+ axref=axref,
+ ay=ay,
+ ayref=ayref,
+ bgcolor=bgcolor,
+ bordercolor=bordercolor,
+ borderpad=borderpad,
+ borderwidth=borderwidth,
+ captureevents=captureevents,
+ clicktoshow=clicktoshow,
+ font=font,
+ height=height,
+ hoverlabel=hoverlabel,
+ hovertext=hovertext,
+ name=name,
+ opacity=opacity,
+ showarrow=showarrow,
+ standoff=standoff,
+ startarrowhead=startarrowhead,
+ startarrowsize=startarrowsize,
+ startstandoff=startstandoff,
+ templateitemname=templateitemname,
+ text=text,
+ textangle=textangle,
+ valign=valign,
+ visible=visible,
+ width=width,
+ x=x,
+ xanchor=xanchor,
+ xclick=xclick,
+ xref=xref,
+ xshift=xshift,
+ y=y,
+ yanchor=yanchor,
+ yclick=yclick,
+ yref=yref,
+ yshift=yshift,
+ **kwargs
+ )
+ return self._add_annotation_like(
+ "annotation",
+ "annotations",
+ new_obj,
+ row=row,
+ col=col,
+ secondary_y=secondary_y,
+ )
+
+ def select_layout_images(self, selector=None, row=None, col=None, secondary_y=None):
+ """
+ Select images from a particular subplot cell and/or images
+ that satisfy custom selection criteria.
+
+ Parameters
+ ----------
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Annotations will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all images are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of images to select.
+ To select images by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ image that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all images are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select images associated with the secondary
+ y-axis of the subplot.
+ * If False, only select images associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter images based on secondary
+ y-axis.
+
+ To select images by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ generator
+ Generator that iterates through all of the images that satisfy
+ all of the specified selection criteria
+ """
+ return self._select_annotations_like(
+ "images", selector=selector, row=row, col=col, secondary_y=secondary_y
+ )
+
+ def for_each_layout_image(
+ self, fn, selector=None, row=None, col=None, secondary_y=None
+ ):
+ """
+ Apply a function to all images that satisfy the specified selection
+ criteria
+
+ Parameters
+ ----------
+ fn:
+ Function that inputs a single image object.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all images are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of images to select.
+ To select images by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ images that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all images are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select images associated with the secondary
+ y-axis of the subplot.
+ * If False, only select images associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter images based on secondary
+ y-axis.
+
+ To select images by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ """
+ for obj in self._select_annotations_like(
+ prop="images", selector=selector, row=row, col=col, secondary_y=secondary_y
+ ):
+ fn(obj)
+
+ return self
+
+ def update_layout_images(
+ self, patch, selector=None, row=None, col=None, secondary_y=None, **kwargs
+ ):
+ """
+ Perform a property update operation on all images that satisfy the
+ specified selection criteria
+
+ Parameters
+ ----------
+ patch: dict or None (default None)
+ Dictionary of property updates to be applied to all images that
+ satisfy the selection criteria.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all images are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of images to select.
+ To select images by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ image that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all images are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select images associated with the secondary
+ y-axis of the subplot.
+ * If False, only select images associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter images based on secondary
+ y-axis.
+
+ To select images by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ **kwargs
+ Additional property updates to apply to each selected image. If
+ a property is specified in both patch and in **kwargs then the
+ one in **kwargs takes precedence.
+
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ """
+ for obj in self._select_annotations_like(
+ prop="images", selector=selector, row=row, col=col, secondary_y=secondary_y
+ ):
+ obj.update(patch, **kwargs)
+
+ return self
+
+ def add_layout_image(
+ self,
+ arg=None,
+ layer=None,
+ name=None,
+ opacity=None,
+ sizex=None,
+ sizey=None,
+ sizing=None,
+ source=None,
+ templateitemname=None,
+ visible=None,
+ x=None,
+ xanchor=None,
+ xref=None,
+ y=None,
+ yanchor=None,
+ yref=None,
+ row=None,
+ col=None,
+ secondary_y=None,
+ **kwargs
+ ):
+ """
+ Create and add a new image to the figure's layout
+
+ Parameters
+ ----------
+ arg
+ instance of Image or dict with compatible properties
+ layer
+ Specifies whether images are drawn below or above
+ traces. When `xref` and `yref` are both set to `paper`,
+ image is drawn below the entire plot area.
+ name
+ When used in a template, named items are created in the
+ output figure in addition to any items the figure
+ already has in this array. You can modify these items
+ in the output figure by making your own item with
+ `templateitemname` matching this `name` alongside your
+ modifications (including `visible: false` or `enabled:
+ false` to hide it). Has no effect outside of a
+ template.
+ opacity
+ Sets the opacity of the image.
+ sizex
+ Sets the image container size horizontally. The image
+ will be sized based on the `position` value. When
+ `xref` is set to `paper`, units are sized relative to
+ the plot width.
+ sizey
+ Sets the image container size vertically. The image
+ will be sized based on the `position` value. When
+ `yref` is set to `paper`, units are sized relative to
+ the plot height.
+ sizing
+ Specifies which dimension of the image to constrain.
+ source
+ Specifies the URL of the image to be used. The URL must
+ be accessible from the domain where the plot code is
+ run, and can be either relative or absolute.
+ templateitemname
+ Used to refer to a named item in this array in the
+ template. Named items from the template will be created
+ even without a matching item in the input figure, but
+ you can modify one by making an item with
+ `templateitemname` matching its `name`, alongside your
+ modifications (including `visible: false` or `enabled:
+ false` to hide it). If there is no template or no
+ matching item, this item will be hidden unless you
+ explicitly show it with `visible: true`.
+ visible
+ Determines whether or not this image is visible.
+ x
+ Sets the image's x position. When `xref` is set to
+ `paper`, units are sized relative to the plot height.
+ See `xref` for more info
+ xanchor
+ Sets the anchor for the x position
+ xref
+ Sets the images's x coordinate axis. If set to a x axis
+ id (e.g. "x" or "x2"), the `x` position refers to an x
+ data coordinate If set to "paper", the `x` position
+ refers to the distance from the left of plot in
+ normalized coordinates where 0 (1) corresponds to the
+ left (right).
+ y
+ Sets the image's y position. When `yref` is set to
+ `paper`, units are sized relative to the plot height.
+ See `yref` for more info
+ yanchor
+ Sets the anchor for the y position.
+ yref
+ Sets the images's y coordinate axis. If set to a y axis
+ id (e.g. "y" or "y2"), the `y` position refers to a y
+ data coordinate. If set to "paper", the `y` position
+ refers to the distance from the bottom of the plot in
+ normalized coordinates where 0 (1) corresponds to the
+ bottom (top).
+ row
+ Subplot row for image
+ col
+ Subplot column for image
+ secondary_y
+ Whether to add image to secondary y-axis
+
+ Returns
+ -------
+ FigureWidget
+ """
+ new_obj = _layout.Image(
+ arg,
+ layer=layer,
+ name=name,
+ opacity=opacity,
+ sizex=sizex,
+ sizey=sizey,
+ sizing=sizing,
+ source=source,
+ templateitemname=templateitemname,
+ visible=visible,
+ x=x,
+ xanchor=xanchor,
+ xref=xref,
+ y=y,
+ yanchor=yanchor,
+ yref=yref,
+ **kwargs
+ )
+ return self._add_annotation_like(
+ "image", "images", new_obj, row=row, col=col, secondary_y=secondary_y
+ )
+
+ def select_shapes(self, selector=None, row=None, col=None, secondary_y=None):
+ """
+ Select shapes from a particular subplot cell and/or shapes
+ that satisfy custom selection criteria.
+
+ Parameters
+ ----------
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Annotations will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all shapes are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of shapes to select.
+ To select shapes by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ shape that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all shapes are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select shapes associated with the secondary
+ y-axis of the subplot.
+ * If False, only select shapes associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter shapes based on secondary
+ y-axis.
+
+ To select shapes by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ generator
+ Generator that iterates through all of the shapes that satisfy
+ all of the specified selection criteria
+ """
+ return self._select_annotations_like(
+ "shapes", selector=selector, row=row, col=col, secondary_y=secondary_y
+ )
+
+ def for_each_shape(self, fn, selector=None, row=None, col=None, secondary_y=None):
+ """
+ Apply a function to all shapes that satisfy the specified selection
+ criteria
+
+ Parameters
+ ----------
+ fn:
+ Function that inputs a single shape object.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all shapes are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of shapes to select.
+ To select shapes by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ shapes that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all shapes are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select shapes associated with the secondary
+ y-axis of the subplot.
+ * If False, only select shapes associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter shapes based on secondary
+ y-axis.
+
+ To select shapes by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ """
+ for obj in self._select_annotations_like(
+ prop="shapes", selector=selector, row=row, col=col, secondary_y=secondary_y
+ ):
+ fn(obj)
+
+ return self
+
+ def update_shapes(
+ self, patch, selector=None, row=None, col=None, secondary_y=None, **kwargs
+ ):
+ """
+ Perform a property update operation on all shapes that satisfy the
+ specified selection criteria
+
+ Parameters
+ ----------
+ patch: dict or None (default None)
+ Dictionary of property updates to be applied to all shapes that
+ satisfy the selection criteria.
+ selector: dict or None (default None)
+ Dict to use as selection criteria.
+ Traces will be selected if they contain properties corresponding
+ to all of the dictionary's keys, with values that exactly match
+ the supplied values. If None (the default), all shapes are
+ selected.
+ row, col: int or None (default None)
+ Subplot row and column index of shapes to select.
+ To select shapes by row and column, the Figure must have been
+ created using plotly.subplots.make_subplots. To select only those
+ shape that are in paper coordinates, set row and col to the
+ string 'paper'. If None (the default), all shapes are selected.
+ secondary_y: boolean or None (default None)
+ * If True, only select shapes associated with the secondary
+ y-axis of the subplot.
+ * If False, only select shapes associated with the primary
+ y-axis of the subplot.
+ * If None (the default), do not filter shapes based on secondary
+ y-axis.
+
+ To select shapes by secondary y-axis, the Figure must have been
+ created using plotly.subplots.make_subplots. See the docstring
+ for the specs argument to make_subplots for more info on
+ creating subplots with secondary y-axes.
+ **kwargs
+ Additional property updates to apply to each selected shape. If
+ a property is specified in both patch and in **kwargs then the
+ one in **kwargs takes precedence.
+
+ Returns
+ -------
+ self
+ Returns the Figure object that the method was called on
+ """
+ for obj in self._select_annotations_like(
+ prop="shapes", selector=selector, row=row, col=col, secondary_y=secondary_y
+ ):
+ obj.update(patch, **kwargs)
+
+ return self
+
+ def add_shape(
+ self,
+ arg=None,
+ fillcolor=None,
+ layer=None,
+ line=None,
+ name=None,
+ opacity=None,
+ path=None,
+ templateitemname=None,
+ type=None,
+ visible=None,
+ x0=None,
+ x1=None,
+ xanchor=None,
+ xref=None,
+ xsizemode=None,
+ y0=None,
+ y1=None,
+ yanchor=None,
+ yref=None,
+ ysizemode=None,
+ row=None,
+ col=None,
+ secondary_y=None,
+ **kwargs
+ ):
+ """
+ Create and add a new shape to the figure's layout
+
+ Parameters
+ ----------
+ arg
+ instance of Shape or dict with compatible properties
+ fillcolor
+ Sets the color filling the shape's interior.
+ layer
+ Specifies whether shapes are drawn below or above
+ traces.
+ line
+ plotly.graph_objects.layout.shape.Line instance or dict
+ with compatible properties
+ name
+ When used in a template, named items are created in the
+ output figure in addition to any items the figure
+ already has in this array. You can modify these items
+ in the output figure by making your own item with
+ `templateitemname` matching this `name` alongside your
+ modifications (including `visible: false` or `enabled:
+ false` to hide it). Has no effect outside of a
+ template.
+ opacity
+ Sets the opacity of the shape.
+ path
+ For `type` "path" - a valid SVG path with the pixel
+ values replaced by data values in
+ `xsizemode`/`ysizemode` being "scaled" and taken
+ unmodified as pixels relative to `xanchor` and
+ `yanchor` in case of "pixel" size mode. There are a few
+ restrictions / quirks only absolute instructions, not
+ relative. So the allowed segments are: M, L, H, V, Q,
+ C, T, S, and Z arcs (A) are not allowed because radius
+ rx and ry are relative. In the future we could consider
+ supporting relative commands, but we would have to
+ decide on how to handle date and log axes. Note that
+ even as is, Q and C Bezier paths that are smooth on
+ linear axes may not be smooth on log, and vice versa.
+ no chained "polybezier" commands - specify the segment
+ type for each one. On category axes, values are numbers
+ scaled to the serial numbers of categories because
+ using the categories themselves there would be no way
+ to describe fractional positions On data axes: because
+ space and T are both normal components of path strings,
+ we can't use either to separate date from time parts.
+ Therefore we'll use underscore for this purpose:
+ 2015-02-21_13:45:56.789
+ templateitemname
+ Used to refer to a named item in this array in the
+ template. Named items from the template will be created
+ even without a matching item in the input figure, but
+ you can modify one by making an item with
+ `templateitemname` matching its `name`, alongside your
+ modifications (including `visible: false` or `enabled:
+ false` to hide it). If there is no template or no
+ matching item, this item will be hidden unless you
+ explicitly show it with `visible: true`.
+ type
+ Specifies the shape type to be drawn. If "line", a line
+ is drawn from (`x0`,`y0`) to (`x1`,`y1`) with respect
+ to the axes' sizing mode. If "circle", a circle is
+ drawn from ((`x0`+`x1`)/2, (`y0`+`y1`)/2)) with radius
+ (|(`x0`+`x1`)/2 - `x0`|, |(`y0`+`y1`)/2 -`y0`)|) with
+ respect to the axes' sizing mode. If "rect", a
+ rectangle is drawn linking (`x0`,`y0`), (`x1`,`y0`),
+ (`x1`,`y1`), (`x0`,`y1`), (`x0`,`y0`) with respect to
+ the axes' sizing mode. If "path", draw a custom SVG
+ path using `path`. with respect to the axes' sizing
+ mode.
+ visible
+ Determines whether or not this shape is visible.
+ x0
+ Sets the shape's starting x position. See `type` and
+ `xsizemode` for more info.
+ x1
+ Sets the shape's end x position. See `type` and
+ `xsizemode` for more info.
+ xanchor
+ Only relevant in conjunction with `xsizemode` set to
+ "pixel". Specifies the anchor point on the x axis to
+ which `x0`, `x1` and x coordinates within `path` are
+ relative to. E.g. useful to attach a pixel sized shape
+ to a certain data value. No effect when `xsizemode` not
+ set to "pixel".
+ xref
+ Sets the shape's x coordinate axis. If set to an x axis
+ id (e.g. "x" or "x2"), the `x` position refers to an x
+ coordinate. If set to "paper", the `x` position refers
+ to the distance from the left side of the plotting area
+ in normalized coordinates where 0 (1) corresponds to
+ the left (right) side. If the axis `type` is "log",
+ then you must take the log of your desired range. If
+ the axis `type` is "date", then you must convert the
+ date to unix time in milliseconds.
+ xsizemode
+ Sets the shapes's sizing mode along the x axis. If set
+ to "scaled", `x0`, `x1` and x coordinates within `path`
+ refer to data values on the x axis or a fraction of the
+ plot area's width (`xref` set to "paper"). If set to
+ "pixel", `xanchor` specifies the x position in terms of
+ data or plot fraction but `x0`, `x1` and x coordinates
+ within `path` are pixels relative to `xanchor`. This
+ way, the shape can have a fixed width while maintaining
+ a position relative to data or plot fraction.
+ y0
+ Sets the shape's starting y position. See `type` and
+ `ysizemode` for more info.
+ y1
+ Sets the shape's end y position. See `type` and
+ `ysizemode` for more info.
+ yanchor
+ Only relevant in conjunction with `ysizemode` set to
+ "pixel". Specifies the anchor point on the y axis to
+ which `y0`, `y1` and y coordinates within `path` are
+ relative to. E.g. useful to attach a pixel sized shape
+ to a certain data value. No effect when `ysizemode` not
+ set to "pixel".
+ yref
+ Sets the annotation's y coordinate axis. If set to an y
+ axis id (e.g. "y" or "y2"), the `y` position refers to
+ an y coordinate If set to "paper", the `y` position
+ refers to the distance from the bottom of the plotting
+ area in normalized coordinates where 0 (1) corresponds
+ to the bottom (top).
+ ysizemode
+ Sets the shapes's sizing mode along the y axis. If set
+ to "scaled", `y0`, `y1` and y coordinates within `path`
+ refer to data values on the y axis or a fraction of the
+ plot area's height (`yref` set to "paper"). If set to
+ "pixel", `yanchor` specifies the y position in terms of
+ data or plot fraction but `y0`, `y1` and y coordinates
+ within `path` are pixels relative to `yanchor`. This
+ way, the shape can have a fixed height while
+ maintaining a position relative to data or plot
+ fraction.
+ row
+ Subplot row for shape
+ col
+ Subplot column for shape
+ secondary_y
+ Whether to add shape to secondary y-axis
+
+ Returns
+ -------
+ FigureWidget
+ """
+ new_obj = _layout.Shape(
+ arg,
+ fillcolor=fillcolor,
+ layer=layer,
+ line=line,
+ name=name,
+ opacity=opacity,
+ path=path,
+ templateitemname=templateitemname,
+ type=type,
+ visible=visible,
+ x0=x0,
+ x1=x1,
+ xanchor=xanchor,
+ xref=xref,
+ xsizemode=xsizemode,
+ y0=y0,
+ y1=y1,
+ yanchor=yanchor,
+ yref=yref,
+ ysizemode=ysizemode,
+ **kwargs
+ )
+ return self._add_annotation_like(
+ "shape", "shapes", new_obj, row=row, col=col, secondary_y=secondary_y
+ )
diff --git a/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_annotations.py b/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_annotations.py
new file mode 100644
index 00000000000..beb02e48006
--- /dev/null
+++ b/packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_annotations.py
@@ -0,0 +1,252 @@
+from __future__ import absolute_import
+
+import types
+from unittest import TestCase
+
+import plotly.graph_objs as go
+from plotly.subplots import make_subplots
+
+
+class TestSelectForEachUpdateAnnotations(TestCase):
+ def setUp(self):
+ self.fig = make_subplots(
+ rows=2, cols=2, specs=[[{}, {"secondary_y": True}], [{}, {"type": "polar"}]]
+ )
+
+ def assert_selected(
+ self, prop, inds, selector=None, row=None, col=None, secondary_y=None
+ ):
+ # ## Test select_*
+ # Get select_ method
+ prefix = "layout_" if prop == "images" else ""
+ fn = getattr(self.fig, "select_" + prefix + prop)
+
+ # Perform selection
+ res = fn(selector=selector, row=row, col=col, secondary_y=secondary_y)
+ self.assertIsInstance(res, types.GeneratorType)
+ objs = list(res)
+
+ # Check length of selected objects
+ self.assertEqual(len(objs), len(inds))
+
+ # Check individual annotations
+ for i, obj in zip(inds, objs):
+ self.assertEqual(self.fig.layout[prop][i], obj)
+
+ # ## Test for_each_*
+ objs = []
+ fn = getattr(self.fig, "for_each_" + prefix + prop[:-1])
+ fn(
+ lambda v: objs.append(v),
+ selector=selector,
+ row=row,
+ col=col,
+ secondary_y=secondary_y,
+ )
+ self.assertEqual(len(objs), len(inds))
+ for i, obj in zip(inds, objs):
+ self.assertEqual(self.fig.layout[prop][i], obj)
+
+ def assert_update(
+ self, prop, inds, patch, selector=None, row=None, col=None, secondary_y=None
+ ):
+ # Copy figure and perform update
+ prefix = "layout_" if prop == "images" else ""
+ fig_orig = go.Figure(self.fig)
+ fig = go.Figure(self.fig)
+ fn = getattr(fig, "update_" + prefix + prop)
+ fn(patch, selector=selector, row=row, col=col, secondary_y=secondary_y)
+
+ # Get original up updated object lis
+ objs_orig = fig_orig.layout[prop]
+ objs = fig.layout[prop]
+
+ for i, (obj, obj_orig) in enumerate(zip(objs, objs_orig)):
+ if i in inds:
+ # Check that object changed from original
+ self.assertNotEqual(obj, obj_orig)
+
+ # Apply update to original and check that they match now
+ obj_orig.update(patch)
+ self.assertEqual(obj, obj_orig)
+ else:
+ # Check object unchanged
+ self.assertEqual(obj, obj_orig)
+
+ def test_add_annotation_no_grid(self):
+ # Paper annotation
+ fig = go.Figure()
+ fig.add_annotation(text="A")
+ annot = fig.layout.annotations[-1]
+ self.assertEqual(annot.text, "A")
+ self.assertEqual(annot.xref, "paper")
+ self.assertEqual(annot.yref, "paper")
+
+ # Not valid to add annotation by row/col
+ with self.assertRaisesRegexp(Exception, "make_subplots"):
+ fig.add_annotation(text="B", row=1, col=1)
+
+ def test_add_annotations(self):
+ # Paper annotation
+ self.fig.add_annotation(text="A")
+ annot = self.fig.layout.annotations[-1]
+ self.assertEqual(annot.text, "A")
+ self.assertEqual(annot.xref, "paper")
+ self.assertEqual(annot.yref, "paper")
+
+ # (1, 1) annotation
+ self.fig.add_annotation(text="B", row=1, col=1)
+ annot = self.fig.layout.annotations[-1]
+ self.assertEqual(annot.text, "B")
+ self.assertEqual(annot.xref, "x")
+ self.assertEqual(annot.yref, "y")
+
+ # (1, 2) annotation, primary y-axis
+ self.fig.add_annotation(text="C1", row=1, col=2)
+ annot = self.fig.layout.annotations[-1]
+ self.assertEqual(annot.text, "C1")
+ self.assertEqual(annot.xref, "x2")
+ self.assertEqual(annot.yref, "y2")
+
+ # (1, 2) annotation, secondary y-axis
+ self.fig.add_annotation(text="C2", row=1, col=2, secondary_y=True)
+ annot = self.fig.layout.annotations[-1]
+ self.assertEqual(annot.text, "C2")
+ self.assertEqual(annot.xref, "x2")
+ self.assertEqual(annot.yref, "y3")
+
+ # (2, 1) annotation
+ self.fig.add_annotation(text="D", row=2, col=1)
+ annot = self.fig.layout.annotations[-1]
+ self.assertEqual(annot.text, "D")
+ self.assertEqual(annot.xref, "x3")
+ self.assertEqual(annot.yref, "y4")
+
+ # Try to add to (2, 2), which not a valid
+ with self.assertRaisesRegexp(ValueError, "of type polar"):
+ self.fig.add_annotation(text="D", row=2, col=2)
+
+ def test_select_annotations_no_grid(self):
+ (
+ self.fig.add_annotation(text="A1", arrowcolor="red")
+ .add_annotation(text="A2", arrowcolor="blue")
+ .add_annotation(text="A3", arrowcolor="blue")
+ )
+ self.assert_selected("annotations", [0, 1, 2])
+ self.assert_selected("annotations", [0], selector=dict(arrowcolor="red"))
+ self.assert_selected("annotations", [1, 2], selector=dict(arrowcolor="blue"))
+
+ def test_select_annotations(self):
+ (
+ self.fig.add_annotation(text="A1", arrowcolor="red")
+ .add_annotation(text="A2", arrowcolor="blue")
+ .add_annotation(text="B", arrowcolor="red", row=1, col=1)
+ .add_annotation(text="C1", row=1, col=2)
+ .add_annotation(text="C2", row=1, col=2, secondary_y=True)
+ .add_annotation(text="D", arrowcolor="blue", row=2, col=1)
+ )
+
+ # Test selections
+ self.assert_selected("annotations", [0, 1, 2, 3, 4, 5])
+ self.assert_selected("annotations", [0, 2], selector=dict(arrowcolor="red"))
+ self.assert_selected("annotations", [2, 3, 4], row=1)
+ self.assert_selected("annotations", [2], selector=dict(arrowcolor="red"), row=1)
+ self.assert_selected("annotations", [0, 1], row="paper", col="paper")
+ self.assert_selected("annotations", [4], secondary_y=True)
+
+ def test_select_shapes(self):
+ (
+ self.fig.add_shape(opacity=0.1, fillcolor="red")
+ .add_shape(opacity=0.2, fillcolor="blue")
+ .add_shape(opacity=0.3, fillcolor="red", row=1, col=1)
+ .add_shape(opacity=0.4, row=1, col=2)
+ .add_shape(opacity=0.5, row=1, col=2, secondary_y=True)
+ .add_shape(opacity=0.6, fillcolor="blue", row=2, col=1)
+ )
+
+ # Test selections
+ self.assert_selected("shapes", [0, 1, 2, 3, 4, 5])
+ self.assert_selected("shapes", [0, 2], selector=dict(fillcolor="red"))
+ self.assert_selected("shapes", [2, 3, 4], row=1)
+ self.assert_selected("shapes", [2], selector=dict(fillcolor="red"), row=1)
+ self.assert_selected("shapes", [0, 1], row="paper", col="paper")
+ self.assert_selected("shapes", [4], secondary_y=True)
+
+ def test_select_images(self):
+ (
+ self.fig.add_layout_image(opacity=0.1, source="red")
+ .add_layout_image(opacity=0.2, source="blue")
+ .add_layout_image(opacity=0.3, source="red", row=1, col=1)
+ .add_layout_image(opacity=0.4, row=1, col=2)
+ .add_layout_image(opacity=0.5, row=1, col=2, secondary_y=True)
+ .add_layout_image(opacity=0.6, source="blue", row=2, col=1)
+ )
+
+ # Test selections
+ self.assert_selected("images", [0, 1, 2, 3, 4, 5])
+ self.assert_selected("images", [0, 2], selector=dict(source="red"))
+ self.assert_selected("images", [2, 3, 4], row=1)
+ self.assert_selected("images", [2], selector=dict(source="red"), row=1)
+ self.assert_selected("images", [0, 1], row="paper", col="paper")
+ self.assert_selected("images", [4], secondary_y=True)
+
+ def test_update_annotations(self):
+ (
+ self.fig.add_annotation(text="A1", arrowcolor="red")
+ .add_annotation(text="A2", arrowcolor="blue")
+ .add_annotation(text="B", arrowcolor="red", row=1, col=1)
+ .add_annotation(text="C1", row=1, col=2)
+ .add_annotation(text="C2", row=1, col=2, secondary_y=True)
+ .add_annotation(text="D", arrowcolor="blue", row=2, col=1)
+ )
+
+ self.assert_update(
+ "annotations", [0, 1, 2, 3, 4, 5], patch=dict(showarrow=False)
+ )
+ self.assert_update(
+ "annotations",
+ [1, 5],
+ patch=dict(showarrow=False),
+ selector=dict(arrowcolor="blue"),
+ )
+ self.assert_update("annotations", [2, 3, 4], patch=dict(showarrow=False), row=1)
+ self.assert_update("annotations", [2, 5], patch=dict(showarrow=False), col=1)
+ self.assert_update(
+ "annotations", [4], patch=dict(showarrow=False), secondary_y=True
+ )
+
+ def test_update_shapes(self):
+ (
+ self.fig.add_shape(opacity=0.1, fillcolor="red")
+ .add_shape(opacity=0.2, fillcolor="blue")
+ .add_shape(opacity=0.3, fillcolor="red", row=1, col=1)
+ .add_shape(opacity=0.4, row=1, col=2)
+ .add_shape(opacity=0.5, row=1, col=2, secondary_y=True)
+ .add_shape(opacity=0.6, fillcolor="blue", row=2, col=1)
+ )
+
+ self.assert_update("shapes", [0, 1, 2, 3, 4, 5], patch=dict(opacity=0))
+ self.assert_update(
+ "shapes", [1, 5], patch=dict(opacity=0), selector=dict(fillcolor="blue")
+ )
+ self.assert_update("shapes", [2, 3, 4], patch=dict(opacity=0), row=1)
+ self.assert_update("shapes", [2, 5], patch=dict(opacity=0), col=1)
+ self.assert_update("shapes", [4], patch=dict(opacity=0), secondary_y=True)
+
+ def test_update_images(self):
+ (
+ self.fig.add_layout_image(opacity=0.1, source="red")
+ .add_layout_image(opacity=0.2, source="blue")
+ .add_layout_image(opacity=0.3, source="red", row=1, col=1)
+ .add_layout_image(opacity=0.4, row=1, col=2)
+ .add_layout_image(opacity=0.5, row=1, col=2, secondary_y=True)
+ .add_layout_image(opacity=0.6, source="blue", row=2, col=1)
+ )
+
+ self.assert_update("images", [0, 1, 2, 3, 4, 5], patch=dict(opacity=0))
+ self.assert_update(
+ "images", [1, 5], patch=dict(opacity=0), selector=dict(source="blue")
+ )
+ self.assert_update("images", [2, 3, 4], patch=dict(opacity=0), row=1)
+ self.assert_update("images", [2, 5], patch=dict(opacity=0), col=1)
+ self.assert_update("images", [4], patch=dict(opacity=0), secondary_y=True)