Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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/).
- Add support for converting matplotlib reference lines (`axhline`, `axvline`, and horizontal/vertical `axline`) as well as axes-coordinate lines into Plotly layout shapes in `mpl_to_plotly`, including support for date axes [[#5719](https://github.com/plotly/plotly.py/pull/5719)], with thanks to @robertoffmoura for the contribution!

### Fixed
- Fix `add_hline` and `add_vline` dropping lines on empty subplots when the target is unambiguous (the grid has a single subplot, or both `row` and `col` are given) [[#5136](https://github.com/plotly/plotly.py/issues/5136)]
- Fix `mpl_to_plotly` tick marker mirroring, axis positioning, and trace visibility for twinned and overlaid subplots (such as `twinx` and `twiny`) [[#5310](https://github.com/plotly/plotly.py/pull/5310)], with thanks to @robertoffmoura for the contribution!
- Fix `mpl_to_plotly` losing matplotlib axis line colors by exporting the color of each axis's displayed spine to the plotly x and y axis `linecolor` [[#5311](https://github.com/plotly/plotly.py/pull/5311)], with thanks to @robertoffmoura for the contribution!
- Fix concurrent first access to lazily initialized graph object properties, which could raise `ValueError("Invalid value")` [[#5691](https://github.com/plotly/plotly.py/pull/5691)], with thanks to @hb1915 for the contribution!
Expand Down
20 changes: 18 additions & 2 deletions plotly/basedatatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4050,6 +4050,20 @@ def _process_multiple_axis_spanning_shapes(
self.layout[layout_obj][:n_layout_objs_before] + new_layout_objs
)

def _axis_spanning_line_target_is_explicit(self, row, col):
"""
Returns True when the subplot targeted by add_hline/add_vline is
unambiguous: the grid has a single subplot, or both row and col were
given explicitly. In these cases the line is kept even if the
subplot does not contain any data yet.
"""
if not self._has_subplots():
return False
rows, cols = self._get_subplot_rows_columns()
if len(rows) * len(cols) == 1:
return True
return all(rc is not None and rc != "all" for rc in (row, col))

def add_vline(
self,
x,
Expand All @@ -4064,7 +4078,8 @@ def add_vline(
row,
col,
"vline",
exclude_empty_subplots=exclude_empty_subplots,
exclude_empty_subplots=exclude_empty_subplots
and not self._axis_spanning_line_target_is_explicit(row, col),
annotation=annotation,
**kwargs,
)
Expand Down Expand Up @@ -4092,7 +4107,8 @@ def add_hline(
row,
col,
"hline",
exclude_empty_subplots=exclude_empty_subplots,
exclude_empty_subplots=exclude_empty_subplots
and not self._axis_spanning_line_target_is_explicit(row, col),
annotation=annotation,
**kwargs,
)
Expand Down
35 changes: 35 additions & 0 deletions tests/test_core/test_update_objects/test_update_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,38 @@ def test_select_annotations_integer(select_annotations_integer):
assert (len(anns) == 1) and anns[0]["text"] == "B"
with pytest.raises(IndexError):
fig.select_annotations(row=2, col=2, selector=3)


@pytest.mark.parametrize("method", ["add_hline", "add_vline"])
def test_axis_spanning_line_kept_on_single_empty_subplot(method):
fig = make_subplots(rows=1, cols=1)
getattr(fig, method)(1)
assert len(fig.layout.shapes) == 1


@pytest.mark.parametrize("method", ["add_hline", "add_vline"])
def test_axis_spanning_line_kept_on_explicit_empty_subplot(method):
fig = make_subplots(rows=2, cols=2)
getattr(fig, method)(1, row=2, col=1)
assert len(fig.layout.shapes) == 1
shape = fig.layout.shapes[0]
assert (shape.xref, shape.yref) in [("x3", "y3 domain"), ("x3 domain", "y3")]


@pytest.mark.parametrize("method", ["add_hline", "add_vline"])
def test_axis_spanning_line_excludes_unspecified_empty_subplots(method):
fig = make_subplots(rows=2, cols=2)
getattr(fig, method)(1)
getattr(fig, method)(1, row=1)
assert len(fig.layout.shapes) == 0

fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2]), row=1, col=2)
getattr(fig, method)(1)
assert len(fig.layout.shapes) == 1


@pytest.mark.parametrize("method", ["add_hrect", "add_vrect"])
def test_axis_spanning_rect_still_excluded_on_explicit_empty_subplot(method):
fig = make_subplots(rows=2, cols=2)
getattr(fig, method)(0, 1, row=2, col=1)
assert len(fig.layout.shapes) == 0