Description
Bug summary
** Updated after looking into figure.py.
In blocking_input.py (called by ginput()), the following breaks when a figure is created outside of pyplot. In the Figure class init, FigureCanvasBase(self) is called and this sets the canvas. The FigureCanvasBase init sets the figure.canvas.manager attribute to None, so the combination of these two if statements doesn't produce the expected outcome.
blocking_input.py:
if hasattr(self.fig.canvas, "manager"):
# Ensure that the figure is shown, if we are managing it.
self.fig.show()
This causes figure.show() to return an attribute error.
if self.canvas.manager is None:
raise AttributeError(
"Figure.show works only for figures managed by pyplot, "
"normally created by pyplot.figure()")
Code for reproduction
import wx
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
class GraphFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title='Graph Frame')
panel = wx.Panel(self)
my_sizer = wx.BoxSizer(wx.VERTICAL)
# Create figure and canvas, add canvas to sizer.
self._figure = Figure()
my_canvas = FigureCanvas(self, wx.ID_ANY, self._figure)
my_sizer.Add(my_canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
# Add subplot to figure, and plot to axis.
self._axis = self._figure.add_subplot()
self._axis.plot([1,2,3])
# Add a button to trigger ginput.
my_btn = wx.Button(panel, label='Select Point')
my_btn.Bind(wx.EVT_BUTTON, self.on_press)
my_sizer.Add(my_btn, 0, wx.TOP | wx.CENTER, 5)
# Add a button to delete the manager attribute from canvas.
my_other_btn = wx.Button(panel, label='Delete canvas manager')
my_other_btn.Bind(wx.EVT_BUTTON, self.delete_canvas_manager)
my_sizer.Add(my_other_btn, 0, wx.TOP | wx.CENTER, 5)
panel.SetSizer(my_sizer)
panel.Fit()
self.Show()
self.Fit()
def delete_canvas_manager(self, event):
print("Canvas has attribute manager returns: ", hasattr(self._figure.canvas, "manager"))
del self._figure.canvas.__dict__["manager"]
print("Canvas manager attribute deleted.")
print("Canvas has attribute manager returns: ", hasattr(self._figure.canvas, "manager"))
print("Try selecting a point again.")
def on_press(self, event):
try:
selection = self._figure.ginput()
print("User selection ({}, {})".format(selection[0][0], selection[0][1]))
except AttributeError as e:
print("User selection unsuccessful: ", e)
if __name__ == '__main__':
app = wx.App()
frame = GraphFrame()
app.MainLoop()
Actual outcome
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/matplotlib/figure.py", line 3075, in ginput
return blocking_mouse_input(n=n, timeout=timeout,
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/matplotlib/blocking_input.py", line 266, in __call__
super().__call__(n=n, timeout=timeout)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/matplotlib/blocking_input.py", line 88, in __call__
self.fig.show()
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/matplotlib/figure.py", line 2333, in show
raise AttributeError(
AttributeError: Figure.show works only for figures managed by pyplot, normally created by pyplot.figure()
Expected outcome
The expected outcome is for blocking_input to skip the call to show figure. Modifying the attribute check in blocking_input.py to the following gives the expected result. In the example, deleting the manager attribute from the class dict also works.
if hasattr(self.fig.canvas, "manager"):
# Ensure that the figure is shown, if we are managing it.
if self.fig.canvas.manager is not None:
self.fig.show()
Operating system
OS/X
Matplotlib Version
3.4.3
Matplotlib Backend
MacOSX
Python version
3.9.6
Jupyter version
No response
Other libraries
No response
Installation
pip
Conda channel
No response