Short answer
Capturing mouse clicks on a non-interactive Matplotlib figure is not possible – that's what the interactive backends are for. If you want to avoid switching back and forth between non-interactive and interactive backends, maybe try the reverse approach: Rather than trying to get interactivity from non-interactive plots, use an interactive backend by default, and disable interactivity where it is not necessary.
Detailed answer
What Matplotlib says
Regarding interactivity, Matplotlib's documentation explicitly states (emphasis by me):
To get interactive figures in the 'classic' notebook or Jupyter lab, use the ipympl backend (must be installed separately) which uses the ipywidget framework.
And further down:
The default backend in notebooks, the inline backend, is not [interactive]. backend_inline renders the figure once and inserts a static image into the notebook when the cell is executed. Because the images are static, they cannot be panned / zoomed, take user input, or be updated from other cells.
I guess that should make the situation pretty clear.
Interactivity with ipywidgets
As you noted, you can interact with (static) Matplotlib figures using ipywidgets. What happens there, however, is the following: The widgets (e.g. the slider that you show) are interactive, while the figure is still not. So "interactivity" in this context means interacting with a widget that then triggers the re-rendering of a static image. This use case and setup is fundamentally different from trying to interactively capture inputs from a static image.
Proposed approach
What I would suggest is:
- Install ipympl, as it is meant to be used for your purpose.
- If you want to avoid switching back and forth between backends, set your interactive backend once for your notebook, and disable interactive features in plots where you don't need them. Following Matplotlib's "comprehensive ipympl example", the
display() function can be used for this purpose.
Altogether, this could look as follows in code:
%matplotlib widget
# Alternatively: %matplotlib ipympl
import matplotlib.pyplot as plt
import numpy as np
# Provide some dummy data
x = np.linspace(-10, 10, num=10000)
y1 = x ** 2
y2 = x ** 3
# Plot `y1` in an interactive plot
def on_click(event):
plt.plot(event.xdata, event.ydata, "ro")
plt.connect("button_press_event", on_click)
plt.plot(x, y1)
# Plot `y2` in a 'static' plot
with plt.ioff():
plt.figure() # Create new figure for 2nd plot
plt.plot(x, y2)
display(plt.gcf()) # Display without interactivity
The resulting notebook would look as follows:

Semi-off-topic: "interactive mode"
You might have noticed that display() is used in connection with ioff() for the static figure here. And although ioff() is documented as the function to, quote, disable interactive mode, it is not the one that is responsible for disabling click capturing etc. here. In this context, "interactive" refers to yet another concept, which is explained with the isinteractive() function; namely,
… whether plots are updated after every plotting command. The interactive mode is mainly useful if you build plots from the command line and want to see the effect of each command while you are building the figure.
In the given example, we don't want plotting commands to have immediate effects on the output, because this would mean that already the figure() and plot() calls would render the figure (with all its interaction capabilities in our original sense!), rather than only rendering it (as a static image) when we call display(). Moreover, we would get two outputs of our figure: one (interactive) plot because of the figure() and plot() calls, one (static) plot because of the display() call. To suppress the first one, we use an ioff() context.
interact()alone, you are only interacting via your mouse with the widget and so that is the mouse-responsive component, i.e., user interface. You want the mouse-responsive element in your idea to be the plot though, and I think you cannot use inline for that because it makes it non-responsive, i.e., there is no user interface for the plot. My understanding is the way the standard Matplotlib backend interacts with Jupyter notebooks, there's no way to detect clicks on it with inline.backend_inlinerenders the figure once and inserts a static image into the notebook when the cell is executed. Because the images are static, they cannot be panned / zoomed, take user input, or be updated from other cells. So you will need a different backend. Does this answer your question?ipywidgetone can change the Matplotlib plot interactively without changing backend. And what I need is possible e.g. withPlotlyplotly.com/python/click-events. Is the same really impossible with Matplotlib? I am looking for an autoritative answer from someone expert with the Matplotlib and ipywidget internals.