forked from matplotlib/matplotlib
-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
User Report
[Bug]: Unable to pickle figure with draggable legend
Bug summary:
- Unable to pickle figure with a draggable legend (same for draggable annotations).
Code for reproduction:
import matplotlib.pyplot as plt
import pickle
fig = plt.figure()
ax = fig.add_subplot(111)
time=[0,1,2,3,4]
speed=[40,43,45,47,48]
ax.plot(time,speed,label="speed")
leg=ax.legend()
leg.set_draggable(True) # pickling works after removing this line
pickle.dumps(fig)
plt.show()Actual outcome:
TypeError: cannot pickle 'FigureCanvasQTAgg' object
Expected outcome:
- Pickling successful
Environment:
- OS: Windows 10
- Matplotlib Version: 3.7.0
- Python: 3.10
- Installation: pip
Specification (Research Findings)
Root cause:
- Enabling a draggable legend sets
Legend._draggableto aDraggableLegend(inherits fromDraggableBase). In 3.7.0-era code,DraggableBase.__init__stores a direct reference to the backend canvas (self.canvas = self.ref_artist.figure.canvas) and event connection IDs on that canvas. When theFigureis pickled, the_draggablehelper is pickled as part of the legend, pulling in the backend-specificFigureCanvas(e.g.,FigureCanvasQTAgg), which is not picklable for GUI backends.
Upstream fix:
- Make draggable helpers “canvas-less” in their pickled state by:
- Removing stored canvas attribute and replacing it with a computed property.
- Using
figure._canvas_callbacksand storing lightweight disconnector callables instead of persistent canvas references and connection IDs. - Register motion handler up-front, remove dynamic connect/disconnect that stores state.
Target changes (files/functions):
lib/matplotlib/offsetbox.pyDraggableBase.__init__: removeself.canvasassignment; use acanvasproperty.- Replace
self.cids/_c1withself._disconnectorsbuilt viafigure._canvas_callbacks. - Update
disconnect()to iterateself._disconnectors.
References:
- Upstream PR Make draggable legends picklable. matplotlib/matplotlib#25311: Make draggable legends picklable — Make draggable legends picklable. matplotlib/matplotlib#25311
- Backport commit to v3.7.x: matplotlib@b1342cb
- Related PR Fix disconnection of callbacks when draggable artist is deparented. matplotlib/matplotlib#25442 (disconnect callbacks on deparenting): Fix disconnection of callbacks when draggable artist is deparented. matplotlib/matplotlib#25442
Verification plan (local, no CI):
- With Agg backend, pickle a figure containing a draggable legend and confirm success and that the pickle stream does not contain canvas symbols (via
pickletools). - With Qt5Agg backend (if available), confirm that pickling succeeds after the patch whereas it fails before.
Task linkage: swev-id: matplotlib__matplotlib-25311 (ID: 277)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels