Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #3755 Bug Fix for: Shapes and Annotations With yref Parameter Not Drawing on Correct Axis #4177

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cc94803
fixed bug for issue #3755 when the figure object contains subplots
zfoltz Apr 21, 2023
ebb3808
fixed bug for issue #3755 when the figure object does not contain sub…
zfoltz Apr 21, 2023
d0522f5
incorrect use of 'is' changed to '=='
zfoltz Apr 21, 2023
24ad4c7
added some conditionals to help find bug in subplot shapes
zfoltz Apr 22, 2023
8bb24b9
fixed bug which was causing shapes to be all drawn on the same axis
zfoltz Apr 26, 2023
1d1db35
removed some no longer needed files that were being used to find the …
zfoltz Apr 26, 2023
d95d8ee
added tests
zfoltz Apr 26, 2023
f92dc6e
added comments and cleaned up code
zfoltz Apr 26, 2023
9f06509
more comments
zfoltz Apr 27, 2023
746b227
removed import plotly express from test
zfoltz Apr 27, 2023
336a880
Merge branch 'master' into axis_spanning_layout_object_xref_yref_bug
alexcjohnson May 5, 2023
9f8c92d
comment tweaks and remove commented-out code
alexcjohnson May 5, 2023
8b2d4bf
Fixed issued caused by a paper or domain, yref
zfoltz May 10, 2023
260e3a3
Merge branch 'axis_spanning_layout_object_xref_yref_bug' of https://g…
zfoltz May 10, 2023
ea8c795
Merge branch 'plotly:master' into axis_spanning_layout_object_xref_yr…
zfoltz May 10, 2023
a9bfd76
Merge branch 'axis_spanning_layout_object_xref_yref_bug' of https://g…
zfoltz May 10, 2023
872e3d5
Added changelog entry
zfoltz May 10, 2023
7678812
fixed issue with annotation_text of an axis_spanning_shape being disp…
zfoltz May 12, 2023
cc7e200
Merge branch 'axis_spanning_layout_object_xref_yref_bug' of https://g…
zfoltz May 12, 2023
c2860de
Added check to catch if no yref was supplied
zfoltz May 12, 2023
a0288c5
Merge branch 'master' into axis_spanning_layout_object_xref_yref_bug
zfoltz May 12, 2023
a8fa9dd
Apply suggestions from code review
alexcjohnson May 24, 2023
f5d2900
Merge branch 'master' into axis_spanning_layout_object_xref_yref_bug
alexcjohnson Jun 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Fixed another compatibility issue with Pandas 2.0, just affecting `px.*(line_close=True)` [[#4190](https://github.com/plotly/plotly.py/pull/4190)]
- Added some rounding to the `make_subplots` function to handle situations where the user-input specs cause the domain to exceed 1 by small amounts [[#4153](https://github.com/plotly/plotly.py/pull/4153)]
- Sanitize JSON output to prevent an XSS vector when graphs are inserted directly into HTML [[#4196](https://github.com/plotly/plotly.py/pull/4196)]
- Fixed issue with shapes and annotations plotting on the wrong y axis when supplied with a specific axis in the `yref` parameter [[#4177](https://github.com/plotly/plotly.py/pull/4177)]
- Remove `use_2to3` setuptools arg, which is invalid in the latest Python and setuptools versions [[#4206](https://github.com/plotly/plotly.py/pull/4206)]
- Fix [#4066](https://github.com/plotly/plotly.py/issues/4066) JupyterLab v4 giving tiny default graph height [[#4227](https://github.com/plotly/plotly.py/pull/4227)]

Expand Down
51 changes: 37 additions & 14 deletions packages/python/plotly/plotly/basedatatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1559,17 +1559,31 @@ def _add_annotation_like(
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

# If the new_object was created with a yref specified that did not include paper or domain, the specified yref should be used otherwise assign the xref and yref from the layout_keys
if (
new_obj.yref is None
or new_obj.yref == "y"
or "paper" in new_obj.yref
or "domain" in new_obj.yref
):
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""".format(
prop_singular=prop_singular, r=row, c=col
)
)
if secondary_y:
xaxis, yaxis = refs[1].layout_keys
else:
xaxis, yaxis = refs[0].layout_keys
xref, yref = xaxis.replace("axis", ""), yaxis.replace("axis", "")
else:
xaxis, yaxis = refs[0].layout_keys
xref, yref = xaxis.replace("axis", ""), yaxis.replace("axis", "")
yref = new_obj.yref
xaxis = refs[0].layout_keys[0]
xref = xaxis.replace("axis", "")
# if exclude_empty_subplots is True, check to see if subplot is
# empty and return if it is
if exclude_empty_subplots and (
Expand All @@ -1591,6 +1605,11 @@ def _add_domain(ax_letter, new_axref):
new_obj.update(xref=xref, yref=yref)

self.layout[prop_plural] += (new_obj,)
# The 'new_obj.xref' and 'new_obj.yref' parameters need to be reset otherwise it
# will appear as if user supplied yref params when looping through subplots and
# will force annotation to be on the axis of the last drawn annotation
# i.e. they all end up on the same axis.
new_obj.update(xref=None, yref=None)

return self

Expand Down Expand Up @@ -4034,6 +4053,7 @@ def _process_multiple_axis_spanning_shapes(
row=row,
col=col,
exclude_empty_subplots=exclude_empty_subplots,
yref=shape_kwargs.get("yref", "y"),
)
# update xref and yref for the new shapes and annotations
for layout_obj, n_layout_objs_before in zip(
Expand All @@ -4045,10 +4065,13 @@ def _process_multiple_axis_spanning_shapes(
):
# this was called intending to add to a single plot (and
# self.add_{layout_obj} succeeded)
# however, in the case of a single plot, xref and yref are not
# specified, so we specify them here so the following routines can work
# (they need to append " domain" to xref or yref)
self.layout[layout_obj][-1].update(xref="x", yref="y")
# however, in the case of a single plot, xref and yref MAY not be
# specified, IF they are not specified we specify them here so the following routines can work
# (they need to append " domain" to xref or yref). If they are specified, we leave them alone.
if self.layout[layout_obj][-1].xref is None:
self.layout[layout_obj][-1].update(xref="x")
if self.layout[layout_obj][-1].yref is None:
self.layout[layout_obj][-1].update(yref="y")
new_layout_objs = tuple(
filter(
lambda x: x is not None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import plotly.graph_objs as go
from plotly.subplots import make_subplots

import pytest


Expand Down Expand Up @@ -351,6 +352,64 @@ def test_no_exclude_empty_subplots():
assert fig.layout[k][3]["xref"] == "x4" and fig.layout[k][3]["yref"] == "y4"


def test_supplied_yref_on_single_plot_subplot():
### test a (1,1) subplot figure object
fig = make_subplots(1, 1)
fig.add_trace(go.Scatter(x=[1, 2, 3, 4], y=[1, 2, 2, 1]))
fig.add_trace(go.Scatter(x=[1, 2, 3, 4], y=[4, 3, 2, 1], yaxis="y2"))
fig.update_layout(
yaxis=dict(title="yaxis1 title"),
yaxis2=dict(title="yaxis2 title", overlaying="y", side="right"),
)
# add horizontal line on y2. Secondary_y can be True or False when yref is supplied
fig.add_hline(y=3, yref="y2", secondary_y=True)
assert fig.layout["shapes"][0]["yref"] == "y2"


def test_supplied_yref_on_non_subplot_figure_object():
### test a non-subplot figure object from go.Figure
trace1 = go.Scatter(x=[1, 2, 3, 4], y=[1, 2, 2, 1])
trace2 = go.Scatter(x=[1, 2, 3, 4], y=[4, 3, 2, 1], yaxis="y2")
data = [trace1, trace2]
layout = go.Layout(
yaxis=dict(title="yaxis1 title"),
yaxis2=dict(title="yaxis2 title", overlaying="y", side="right"),
)
fig = go.Figure(data=data, layout=layout)
# add horizontal line on y2. Secondary_y can be True or False when yref is supplied
fig.add_hline(y=3, yref="y2", secondary_y=False)
assert fig.layout["shapes"][0]["yref"] == "y2"


def test_supplied_yref_on_multi_plot_subplot():
### test multiple subploted figure object with subplots.make_subplots
fig = make_subplots(
rows=1,
cols=2,
shared_yaxes=False,
specs=[[{"secondary_y": True}, {"secondary_y": True}]],
)
### Add traces to the first subplot
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[1, 2, 3]), row=1, col=1)
fig.add_trace(
go.Scatter(x=[1, 2, 3], y=[3, 2, 1], yaxis="y2"), row=1, col=1, secondary_y=True
)
### Add traces to the second subplot
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[1, 2, 3], yaxis="y"), row=1, col=2)
fig.add_trace(
go.Scatter(x=[1, 2, 3], y=[1, 1, 2], yaxis="y2"), row=1, col=2, secondary_y=True
)
# add a horizontal line on both subplots on their respective secondary y.
# When using the subplots.make_subplots() method yref parameter should NOT be supplied per docstring instructions.
# Instead secondary_y specs and secondary_y parameter MUST be True to plot on secondary y
fig.add_hline(y=2, row=1, col=1, secondary_y=True)
fig.add_hline(y=1, row=1, col=2, secondary_y=True)
assert fig.layout["shapes"][0]["yref"] == "y2"
assert fig.layout["shapes"][0]["xref"] == "x domain"
assert fig.layout["shapes"][1]["yref"] == "y4"
assert fig.layout["shapes"][1]["xref"] == "x2 domain"


@pytest.fixture
def select_annotations_integer():
fig = make_subplots(2, 3)
Expand Down