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)