Skip to content

Instantly share code, notes, and snippets.

@MarcSkovMadsen
Last active December 14, 2022 09:07
Show Gist options
  • Save MarcSkovMadsen/756fe0cf392a35c3c826194b831ad1a3 to your computer and use it in GitHub Desktop.
Save MarcSkovMadsen/756fe0cf392a35c3c826194b831ad1a3 to your computer and use it in GitHub Desktop.
Matplotlib interactive extension
import panel as pn
from panel.pane.plot import Matplotlib as _MatplotlibPane
import param
import numpy as np
from matplotlib.figure import Figure
from matplotlib import cm
from matplotlib.backends.backend_agg import FigureCanvasAgg # not needed for mpl >= 3.1
pn.extension()
# Custom implementation of Matplotlib pane for now
# Should go back into Panel Matplotlib pane at some stage
class MatplotlibInteractive(_MatplotlibPane):
_rename = {**_MatplotlibPane._rename, "event": None, "events": None}
_priority = -1
event = param.Parameter()
events = param.List()
def _get_widget(self, fig):
import matplotlib
old_backend = getattr(matplotlib.backends, "backend", "agg")
from ipympl.backend_nbagg import FigureManager, Canvas, is_interactive
from matplotlib._pylab_helpers import Gcf
matplotlib.use(old_backend)
canvas = Canvas(fig)
fig.patch.set_alpha(0)
manager = FigureManager(canvas, 0)
if is_interactive():
fig.canvas.draw_idle()
def closer(event):
canvas.mpl_disconnect(cid)
Gcf.destroy(manager)
cid = canvas.mpl_connect("close_event", closer)
def handle_event(event):
self.event = event
for ev in self.events:
if not ev == "close_event":
fig.canvas.mpl_connect(ev, handle_event)
return manager
## Matplotlib Interactive Example
FONTAWESOME_LINK = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.css"
pn.config.css_files.append(FONTAWESOME_LINK)
pn.extension(sizing_mode="stretch_width")
def get_mpl():
Y, X = np.mgrid[-3:3:100j, -3:3:100j]
U = -1 - X ** 2 + Y
V = 1 + X - Y ** 2
fig0 = Figure(figsize=(8, 6))
ax0 = fig0.subplots()
# FigureCanvasAgg(fig0) # not needed for mpl >= 3.1
strm = ax0.streamplot(X, Y, U, V, color=U, linewidth=2, cmap=cm.autumn)
fig0.colorbar(strm.lines)
return MatplotlibInteractive(
fig0,
dpi=144,
interactive=True,
events=["button_press_event", "button_release_event", "motion_notify_event"],
)
mpl = get_mpl()
pn.template.FastListTemplate(
site = "Awesome Panel", title = "Matplotlib with events",
main = [pn.Column(
mpl,
pn.layout.HSpacer(height=700), # Hack because the Matplotlib Pane does not set its "height" correctly
mpl.param.event,
)
]
).servable()
@MarcSkovMadsen
Copy link
Author

MarcSkovMadsen commented Jun 5, 2021

This example is developed as a short term solution for enabled interactive Matplotlib applications based on events using Panel.

C.f. Panel Feature Request #2349.

This will give us time to discuss the right api and implementation before we add this to back into Panel.

matplotlib-with-events

I will keep a list of things that are "Not working yet" and update it as we go.

Run it via panel serve matplotlib_interactive.py.

Not working yet

  • Pan and zoom for multiple sessions or when reloading the window. C.f. holoviz/panel#2353
  • Matplotlib pane height parameter not working
  • "No handler for the 'history_buttons'" error message type in browser console.

@masip85
Copy link

masip85 commented Dec 13, 2022

Why there is so much white space above? Is what you mean with "height parameter not working"? Is there a way to eliminate white space? I've tried tight layout options unsuccessfully.

@MarcSkovMadsen
Copy link
Author

Thanks for the question. Are you referring to white space in the code or the app shown?

@masip85
Copy link

masip85 commented Dec 14, 2022

referring to white space in the code or the app shown?

The matplotlib figure shows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment