Repository: matplotlib/matplotlib

Issue:
[Bug]: Stackplot in SubFigure raises when drawing Legend
### Bug summary

When using subfigures, calling `fig.legend()` (on the main/root figure) after having drawn a stackplot (`ax.stackplot`) results in the following error:
```
RuntimeError: Can not put single artist in more than one figure
```

### Code for reproduction

I copied the code from the [stackplot example from the docs](https://matplotlib.org/stable/gallery/lines_bars_and_markers/stackplot_demo.html#stackplots), then repeated it in a loop:

```Python
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as mticker

year = [1950, 1960, 1970, 1980, 1990, 2000, 2010, 2018]
population_by_continent = {
    "Africa": [0.228, 0.284, 0.365, 0.477, 0.631, 0.814, 1.044, 1.275],
    "the Americas": [0.340, 0.425, 0.519, 0.619, 0.727, 0.840, 0.943, 1.006],
    "Asia": [1.394, 1.686, 2.120, 2.625, 3.202, 3.714, 4.169, 4.560],
    "Europe": [0.220, 0.253, 0.276, 0.295, 0.310, 0.303, 0.294, 0.293],
    "Oceania": [0.012, 0.015, 0.019, 0.022, 0.026, 0.031, 0.036, 0.039],
}

fig = plt.figure()
subfigs = fig.subfigures(nrows=1, ncols=2)

for _fig in subfigs:
    axs = _fig.subplots(nrows=2, ncols=1, squeeze=False)[0]

    for ax in axs:
        ax.stackplot(
            year,
            population_by_continent.values(),
            labels=population_by_continent.keys(),
            alpha=0.8,
        )
        ax.legend(loc="upper left", reverse=True)
        ax.set_title("World population")
        ax.set_xlabel("Year")
        ax.set_ylabel("Number of people (billions)")
        # add tick at every 200 million people
        ax.yaxis.set_minor_locator(mticker.MultipleLocator(0.2))

fig.legend(loc="upper right", bbox_to_anchor=(1.1, 1)) # error!
```

### Actual outcome

An exception is raised

### Expected outcome

A legend is drawn. If in the provided example `ax.stackplot` is replaced by another plotting function (i.e. `ax.plot`, `ax.scatter`...) the error does not happen.

I have also tried:
- `fig.legend(handles, labels)`: Passing a list of artist/labels to `.legend()` causes the same error
- `subfigures[0].legend()`: works, but obviously only prints artists present in said subfigure, not others.


### Additional information

_No response_

### Operating system

_No response_

### Matplotlib Version

3.10.0

### Matplotlib Backend

_No response_

### Python version

_No response_

### Jupyter version

_No response_

### Installation

None


Discussion:
In the meantime, worked around it by just proxying all artists:

```py
def proxy_handles(all_handles: list[Artist]):
    proxy_handles = []
    for handle in all_handles:
        label = handle.get_label()
        if isinstance(handle, PolyCollection):
            # PolyCollection (from stackplot/fill_between)
            color = handle.get_facecolor()[0]
        elif isinstance(handle, Line2D):
            color = handle.get_color()

        proxy = mpatches.Patch(color=color, label=label)  # type: ignore
        proxy_handles.append(proxy)
    return proxy_handles

proxied = proxy_handles(line_set)
    fig.legend(
        proxied,
        label_set,
        loc="upper right",
        bbox_to_anchor=(1.13, 0.85),
        fancybox=True,
    )
```
I suspect the issue is that in https://github.com/matplotlib/matplotlib/blob/c44ae00ac0d384d8a514883a993fb0c83cc3d9fa/lib/matplotlib/legend_handler.py#L88-L90

both calls are trying to set the figure, but I have not confirmed/actually debugged.
Here's a much smaller reproducer:

```python
import matplotlib.pyplot as plt

fig = plt.figure()
subfigs = fig.subfigures(nrows=1, ncols=2)

for _fig in subfigs:
    ax = _fig.subplots(nrows=1, ncols=1)
    ax.stackplot([3, 4], [[1, 2]], labels=['a'])

fig.legend()
plt.show()
```



Patch:
diff --git a/lib/matplotlib/legend_handler.py b/lib/matplotlib/legend_handler.py
index 263945b050d0..65a78891b17f 100644
--- a/lib/matplotlib/legend_handler.py
+++ b/lib/matplotlib/legend_handler.py
@@ -799,7 +799,6 @@ def get_first(prop_array):
         legend_handle.set_linewidth(get_first(orig_handle.get_linewidths()))
         legend_handle.set_linestyle(get_first(orig_handle.get_linestyles()))
         legend_handle.set_transform(get_first(orig_handle.get_transforms()))
-        legend_handle.set_figure(orig_handle.get_figure())
         # Alpha is already taken into account by the color attributes.
 
     def create_artists(self, legend, orig_handle,


Test patch:
diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py
index bba9f8648799..c96173e340f7 100644
--- a/lib/matplotlib/tests/test_axes.py
+++ b/lib/matplotlib/tests/test_axes.py
@@ -3420,6 +3420,20 @@ def test_stackplot_hatching(fig_ref, fig_test):
     ax_ref.set_ylim(0, 70)
 
 
+def test_stackplot_subfig_legend():
+    # Smoke test for https://github.com/matplotlib/matplotlib/issues/30158
+
+    fig = plt.figure()
+    subfigs = fig.subfigures(nrows=1, ncols=2)
+
+    for _fig in subfigs:
+        ax = _fig.subplots(nrows=1, ncols=1)
+        ax.stackplot([3, 4], [[1, 2]], labels=['a'])
+
+    fig.legend()
+    fig.draw_without_rendering()
+
+
 def _bxp_test_helper(
         stats_kwargs={}, transform_stats=lambda s: s, bxp_kwargs={}):
     np.random.seed(937)