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

Horizontal Plotting using widgets #3140

Merged
merged 9 commits into from
Jun 21, 2023
6 changes: 6 additions & 0 deletions doc/user_guide/visualisation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,12 @@

.. _plot.customize_navigator:

.. versionadded:: 2.0.0
``plot_style`` keyword argument to allow for "horizontal" or "vertical" plotting when using the `ipympl` or
`widget` backends. A default value can also be set using

Check warning on line 288 in doc/user_guide/visualisation.rst

View workflow job for this annotation

GitHub Actions / Docs (PR comments)

Explicit markup ends without a blank line; unexpected unindent.
:ref:`HyperSpy plot preferences <configuring-hyperspy-label>`.
jlaehne marked this conversation as resolved.
Show resolved Hide resolved


Customizing the "navigator"
===========================

Expand Down
3 changes: 3 additions & 0 deletions hyperspy/defaults_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ class PlotConfig(t.HasTraits):
# Don't use t.Enum to list all possible matplotlib colormap to
# avoid importing matplotlib and building the list of colormap
# when importing hyperpsy
widget_plot_style = t.Enum(
['horizontal', 'vertical'],
label='Widget plot style: (only with ipympl)')
cmap_navigator = t.Str('gray',
label='Color map navigator',
desc='Set the default color map for the navigator.',
Expand Down
54 changes: 42 additions & 12 deletions hyperspy/drawing/mpl_he.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
import logging

from traits.api import Undefined
import matplotlib as mpl

from hyperspy.drawing import widgets, signal1d, image
from hyperspy.defaults_parser import preferences

import warnings

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -180,17 +181,46 @@ def plot(self, **kwargs):
for key in ['power_spectrum', 'fft_shift']:
if key in kwargs:
self.signal_data_function_kwargs[key] = kwargs.pop(key)
if self.pointer is None:
pointer = self.assign_pointer()
if pointer is not None:
self.pointer = pointer(self.axes_manager)
self.pointer.color = 'red'
self.pointer.connect_navigate()
self.plot_navigator(**kwargs.pop('navigator_kwds', {}))
if pointer is not None:
self.navigator_plot.events.closed.connect(
self.pointer.disconnect, [])
self.plot_signal(**kwargs)
backend = mpl.get_backend()
if not "ipympl" in backend and "plot_style" in kwargs:
warnings.warn("The `plot_style` keyword is only used when the `ipympl` or `widget`"
"plotting backends are used.")
plot_style = kwargs.pop("plot_style", None)
# matplotlib plotting backend
def plot_sig_and_nav(plot_style):
if self.pointer is None:
pointer = self.assign_pointer()
if pointer is not None:
self.pointer = pointer(self.axes_manager)
self.pointer.color = 'red'
self.pointer.connect_navigate()
self.plot_navigator(**kwargs.pop('navigator_kwds', {}))
if pointer is not None:
self.navigator_plot.events.closed.connect(
self.pointer.disconnect, [])
self.plot_signal(**kwargs)
if "ipympl" in backend:
if plot_style not in ["vertical", "horizontal", None]:
raise ValueError("plot_style must be one of ['vertical', 'horizontal', None]")
if plot_style is None:
plot_style = preferences.Plot.widget_plot_style
# If widgets do not already exist, we will `display` them at the end
from ipywidgets.widgets import HBox, VBox
from IPython.display import display
if not self.navigator_plot:
display(self.signal_plot.figure.canvas)
elif plot_style == "horizontal":
display(HBox([self.navigator_plot.figure.canvas,self.signal_plot.figure.canvas]))
else: # plot_style == "vertical":
display(VBox([self.navigator_plot.figure.canvas, self.signal_plot.figure.canvas]))
if "ipympl" in backend:
import matplotlib.pyplot as plt
with plt.ioff():
plot_sig_and_nav(plot_style)
else:
plot_sig_and_nav(plot_style)



def assign_pointer(self):
if self.navigator_data_function is None:
Expand Down
58 changes: 58 additions & 0 deletions hyperspy/tests/drawing/test_horizontal_plotting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copyright 2007-2022 The HyperSpy developers
#
# This file is part of HyperSpy.
#
# HyperSpy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# HyperSpy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with HyperSpy. If not, see <https://www.gnu.org/licenses/#GPL>.
import pytest

import hyperspy.api as hs
import numpy as np
import matplotlib

ipympl = pytest.importorskip("ipympl")
ipywidgets = pytest.importorskip("ipywidgets")

class TestIPYMPL:
def test_horizontal(self, capsys):
matplotlib.use("module://ipympl.backend_nbagg")
s = hs.signals.Signal2D(np.random.random((4, 4, 2, 2)))
s.plot(plot_style="horizontal")
captured = capsys.readouterr()
assert("HBox(children=(Canvas(" in captured.out)

def test_vertical(self,capsys):
matplotlib.use("module://ipympl.backend_nbagg")
s = hs.signals.Signal2D(np.random.random((4, 4, 2, 2)))
s.plot(plot_style="vertical")
captured = capsys.readouterr()

assert("VBox(children=(Canvas(" in captured.out)

def test_only_signal(self,capsys):
matplotlib.use("module://ipympl.backend_nbagg")
s = hs.signals.Signal2D(np.random.random(( 2, 2)))
s.plot()
captured = capsys.readouterr()
assert("Canvas(toolbar=Toolbar(" in captured.out)

def test_warnings(self,):
with pytest.warns(UserWarning):
s = hs.signals.Signal2D(np.random.random(( 4, 2, 2)))
s.plot(plot_style="vertical")

def test_misspelling(self, ):
matplotlib.use("module://ipympl.backend_nbagg")
with pytest.raises(ValueError):
s = hs.signals.Signal2D(np.random.random((4, 2, 2)))
s.plot(plot_style="Vert")
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@

extras_require = {
"learning": ["scikit-learn>=1.0.1"],
"gui-jupyter": ["hyperspy_gui_ipywidgets>=1.1.0"],
"gui-jupyter": ["hyperspy_gui_ipywidgets>=1.1.0", "ipympl"],
# UPDATE BEFORE RELEASE
"gui-traitsui": ["hyperspy_gui_traitsui @ git+https://github.com/hyperspy/hyperspy_gui_traitsui#egg=hyperspy_gui_traitsui"],
#"gui-traitsui": ["hyperspy_gui_traitsui>=1.1.0"],
Expand Down
3 changes: 3 additions & 0 deletions upcoming_changes/3140.new.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Changes to :meth:`~.api.signals.BaseSignal.plot`:
- Allow Horizontal plotting using the `ipympl` or `widget` backends
- Add in gui preferences to set a default to horizontal or vertical plotting