Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: blocking_input #21548

Closed
misc-coder opened this issue Nov 5, 2021 · 2 comments · Fixed by #21553
Closed

[Bug]: blocking_input #21548

misc-coder opened this issue Nov 5, 2021 · 2 comments · Fixed by #21553
Labels
status: needs clarification Issues that need more information to resolve.
Milestone

Comments

@misc-coder
Copy link

misc-coder commented Nov 5, 2021

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

@QuLogic
Copy link
Member

QuLogic commented Nov 6, 2021

Please provide a complete example.

@QuLogic QuLogic added the status: needs clarification Issues that need more information to resolve. label Nov 6, 2021
@misc-coder
Copy link
Author

The linked pull request will close this issue 👍🏻

@QuLogic QuLogic added this to the v3.5.0 milestone Nov 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs clarification Issues that need more information to resolve.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants