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

Fix display of interactive Matplotlib #6450

Merged
merged 2 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/reference/panes/Matplotlib.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@
"cf = ax.contourf(x + dx/2., y + dy/2., z)\n",
"fig.colorbar(cf, ax=ax)\n",
"\n",
"pn.pane.Matplotlib(fig, interactive=True, dpi=72)"
"pn.pane.Matplotlib(fig, interactive=True)"
]
},
{
Expand Down
42 changes: 28 additions & 14 deletions panel/pane/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import sys

from contextlib import contextmanager
from functools import partial
from io import BytesIO
from typing import (
TYPE_CHECKING, Any, ClassVar, Dict, Mapping, Optional,
Expand All @@ -19,7 +20,7 @@
)
from bokeh.themes import Theme

from ..io import remove_root
from ..io import remove_root, state
from ..io.notebook import push
from ..util import escape
from ..viewable import Layoutable
Expand Down Expand Up @@ -250,6 +251,8 @@ class Matplotlib(Image, IPyWidget):
'interactive', 'object', 'dpi', 'tight', 'high_dpi'
]

_num = 0

@classmethod
def applies(cls, obj: Any) -> float | bool | None:
if 'matplotlib' not in sys.modules:
Expand All @@ -269,22 +272,21 @@ def _get_widget(self, fig):
import matplotlib.backends
old_backend = getattr(matplotlib.backends, 'backend', 'agg')

from ipympl.backend_nbagg import Canvas, FigureManager, is_interactive
from ipympl.backend_nbagg import Canvas, FigureManager
from matplotlib._pylab_helpers import Gcf

matplotlib.use(old_backend)

def closer(event):
Gcf.destroy(0)
canvas.mpl_disconnect(cid)
Gcf.destroy(manager)

canvas = Canvas(fig)
fig.patch.set_alpha(0)
manager = FigureManager(canvas, 0)

if is_interactive():
fig.canvas.draw_idle()

canvas.mpl_connect('close_event', closer)
manager = FigureManager(canvas, self._num)
self._num += 1
cid = canvas.mpl_connect('close_event', closer)
state.onload(partial(self._initialize_canvas, manager.canvas))
return manager

@property
Expand All @@ -303,7 +305,6 @@ def filetype(self):
def _transform_object(self, obj: Any) -> Dict[str, Any]:
return self._img_type._transform_object(self, obj)


def _imgshape(self, data):
try:
return self._img_type._imgshape(data)
Expand All @@ -315,6 +316,11 @@ def _format_html(
):
return self._img_type._format_html(self, src, width, height)

def _transform_object(self, obj: Any) -> Dict[str, Any]:
if self.interactive:
return {}
return self._img_type._transform_object(self, obj)

def _get_model(
self, doc: Document, root: Optional[Model] = None,
parent: Optional[Model] = None, comm: Optional[Comm] = None
Expand All @@ -324,27 +330,35 @@ def _get_model(
self.object.set_dpi(self.dpi)
manager = self._get_widget(self.object)
properties = self._get_properties(doc)
del properties['text']
model = self._get_ipywidget(
manager.canvas, doc, root, comm, **properties
)
manager.canvas.draw()
root = root or model
self._models[root.ref['id']] = (model, parent)
self._managers[root.ref['id']] = manager
return model

def _initialize_canvas(self, canvas):
canvas._device_pixel_ratio = 2 if self.high_dpi else 1
canvas._handle_message(None, {'type': 'initialized'}, None)

def _update(self, ref: str, model: Model) -> None:
if not self.interactive:
model.update(**self._get_properties(model.document))
props = self._get_properties(model.document)
model.update(**props)
return
manager = self._managers[ref]
if self.object is not manager.canvas.figure:
self.object.set_dpi(self.dpi)
self.object.patch.set_alpha(0)
manager.canvas.figure = self.object
self.object.set_canvas(manager.canvas)
event = {'width': manager.canvas._width,
'height': manager.canvas._height}
if hasattr(manager.canvas, '_size'):
cw, ch = manager.canvas._size
elif hasattr(manager.canvas, '_width'):
cw, ch = manager.canvas._width, manager.canvas._height
event = {'width': cw, 'height': ch}
manager.canvas.handle_resize(event)
manager.canvas.draw_idle()

Expand Down
Loading