Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 42266b0

Browse files
Merge pull request #4177 from zfoltz/axis_spanning_layout_object_xref_yref_bug
Issue #3755 Bug Fix for: Shapes and Annotations With yref Parameter Not Drawing on Correct Axis
2 parents 58075f4 + f5d2900 commit 42266b0

File tree

3 files changed

+97
-14
lines changed

3 files changed

+97
-14
lines changed

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
88
- Fixed another compatibility issue with Pandas 2.0, just affecting `px.*(line_close=True)` [[#4190](https://github.com/plotly/plotly.py/pull/4190)]
99
- 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)]
1010
- Sanitize JSON output to prevent an XSS vector when graphs are inserted directly into HTML [[#4196](https://github.com/plotly/plotly.py/pull/4196)]
11+
- 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)]
1112
- Remove `use_2to3` setuptools arg, which is invalid in the latest Python and setuptools versions [[#4206](https://github.com/plotly/plotly.py/pull/4206)]
1213
- 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)]
1314

‎packages/python/plotly/plotly/basedatatypes.py‎

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,17 +1559,31 @@ def _add_annotation_like(
15591559
subplot_type=refs[0].subplot_type,
15601560
)
15611561
)
1562-
if len(refs) == 1 and secondary_y:
1563-
raise ValueError(
1564-
"""
1565-
Cannot add {prop_singular} to secondary y-axis of subplot at position ({r}, {c})
1566-
because subplot does not have a secondary y-axis"""
1567-
)
1568-
if secondary_y:
1569-
xaxis, yaxis = refs[1].layout_keys
1562+
1563+
# 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
1564+
if (
1565+
new_obj.yref is None
1566+
or new_obj.yref == "y"
1567+
or "paper" in new_obj.yref
1568+
or "domain" in new_obj.yref
1569+
):
1570+
if len(refs) == 1 and secondary_y:
1571+
raise ValueError(
1572+
"""
1573+
Cannot add {prop_singular} to secondary y-axis of subplot at position ({r}, {c})
1574+
because subplot does not have a secondary y-axis""".format(
1575+
prop_singular=prop_singular, r=row, c=col
1576+
)
1577+
)
1578+
if secondary_y:
1579+
xaxis, yaxis = refs[1].layout_keys
1580+
else:
1581+
xaxis, yaxis = refs[0].layout_keys
1582+
xref, yref = xaxis.replace("axis", ""), yaxis.replace("axis", "")
15701583
else:
1571-
xaxis, yaxis = refs[0].layout_keys
1572-
xref, yref = xaxis.replace("axis", ""), yaxis.replace("axis", "")
1584+
yref = new_obj.yref
1585+
xaxis = refs[0].layout_keys[0]
1586+
xref = xaxis.replace("axis", "")
15731587
# if exclude_empty_subplots is True, check to see if subplot is
15741588
# empty and return if it is
15751589
if exclude_empty_subplots and (
@@ -1591,6 +1605,11 @@ def _add_domain(ax_letter, new_axref):
15911605
new_obj.update(xref=xref, yref=yref)
15921606

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

15951614
return self
15961615

@@ -4034,6 +4053,7 @@ def _process_multiple_axis_spanning_shapes(
40344053
row=row,
40354054
col=col,
40364055
exclude_empty_subplots=exclude_empty_subplots,
4056+
yref=shape_kwargs.get("yref", "y"),
40374057
)
40384058
# update xref and yref for the new shapes and annotations
40394059
for layout_obj, n_layout_objs_before in zip(
@@ -4045,10 +4065,13 @@ def _process_multiple_axis_spanning_shapes(
40454065
):
40464066
# this was called intending to add to a single plot (and
40474067
# self.add_{layout_obj} succeeded)
4048-
# however, in the case of a single plot, xref and yref are not
4049-
# specified, so we specify them here so the following routines can work
4050-
# (they need to append " domain" to xref or yref)
4051-
self.layout[layout_obj][-1].update(xref="x", yref="y")
4068+
# however, in the case of a single plot, xref and yref MAY not be
4069+
# specified, IF they are not specified we specify them here so the following routines can work
4070+
# (they need to append " domain" to xref or yref). If they are specified, we leave them alone.
4071+
if self.layout[layout_obj][-1].xref is None:
4072+
self.layout[layout_obj][-1].update(xref="x")
4073+
if self.layout[layout_obj][-1].yref is None:
4074+
self.layout[layout_obj][-1].update(yref="y")
40524075
new_layout_objs = tuple(
40534076
filter(
40544077
lambda x: x is not None,

‎packages/python/plotly/plotly/tests/test_core/test_update_objects/test_update_annotations.py‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import plotly.graph_objs as go
77
from plotly.subplots import make_subplots
8+
89
import pytest
910

1011

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

353354

355+
def test_supplied_yref_on_single_plot_subplot():
356+
### test a (1,1) subplot figure object
357+
fig = make_subplots(1, 1)
358+
fig.add_trace(go.Scatter(x=[1, 2, 3, 4], y=[1, 2, 2, 1]))
359+
fig.add_trace(go.Scatter(x=[1, 2, 3, 4], y=[4, 3, 2, 1], yaxis="y2"))
360+
fig.update_layout(
361+
yaxis=dict(title="yaxis1 title"),
362+
yaxis2=dict(title="yaxis2 title", overlaying="y", side="right"),
363+
)
364+
# add horizontal line on y2. Secondary_y can be True or False when yref is supplied
365+
fig.add_hline(y=3, yref="y2", secondary_y=True)
366+
assert fig.layout["shapes"][0]["yref"] == "y2"
367+
368+
369+
def test_supplied_yref_on_non_subplot_figure_object():
370+
### test a non-subplot figure object from go.Figure
371+
trace1 = go.Scatter(x=[1, 2, 3, 4], y=[1, 2, 2, 1])
372+
trace2 = go.Scatter(x=[1, 2, 3, 4], y=[4, 3, 2, 1], yaxis="y2")
373+
data = [trace1, trace2]
374+
layout = go.Layout(
375+
yaxis=dict(title="yaxis1 title"),
376+
yaxis2=dict(title="yaxis2 title", overlaying="y", side="right"),
377+
)
378+
fig = go.Figure(data=data, layout=layout)
379+
# add horizontal line on y2. Secondary_y can be True or False when yref is supplied
380+
fig.add_hline(y=3, yref="y2", secondary_y=False)
381+
assert fig.layout["shapes"][0]["yref"] == "y2"
382+
383+
384+
def test_supplied_yref_on_multi_plot_subplot():
385+
### test multiple subploted figure object with subplots.make_subplots
386+
fig = make_subplots(
387+
rows=1,
388+
cols=2,
389+
shared_yaxes=False,
390+
specs=[[{"secondary_y": True}, {"secondary_y": True}]],
391+
)
392+
### Add traces to the first subplot
393+
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[1, 2, 3]), row=1, col=1)
394+
fig.add_trace(
395+
go.Scatter(x=[1, 2, 3], y=[3, 2, 1], yaxis="y2"), row=1, col=1, secondary_y=True
396+
)
397+
### Add traces to the second subplot
398+
fig.add_trace(go.Scatter(x=[1, 2, 3], y=[1, 2, 3], yaxis="y"), row=1, col=2)
399+
fig.add_trace(
400+
go.Scatter(x=[1, 2, 3], y=[1, 1, 2], yaxis="y2"), row=1, col=2, secondary_y=True
401+
)
402+
# add a horizontal line on both subplots on their respective secondary y.
403+
# When using the subplots.make_subplots() method yref parameter should NOT be supplied per docstring instructions.
404+
# Instead secondary_y specs and secondary_y parameter MUST be True to plot on secondary y
405+
fig.add_hline(y=2, row=1, col=1, secondary_y=True)
406+
fig.add_hline(y=1, row=1, col=2, secondary_y=True)
407+
assert fig.layout["shapes"][0]["yref"] == "y2"
408+
assert fig.layout["shapes"][0]["xref"] == "x domain"
409+
assert fig.layout["shapes"][1]["yref"] == "y4"
410+
assert fig.layout["shapes"][1]["xref"] == "x2 domain"
411+
412+
354413
@pytest.fixture
355414
def select_annotations_integer():
356415
fig = make_subplots(2, 3)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /