diff --git a/docs/masks.rst b/docs/masks.rst index 01cf4f99b..a0229623e 100644 --- a/docs/masks.rst +++ b/docs/masks.rst @@ -321,6 +321,6 @@ and averaged flux, which is updated live in the text field of the plot as well. ellipse = EllipsePixelRegion(center=PixCoord(x=126, y=1031), width=8, height=4, angle=-0*u.deg, visual={'color': 'yellow'}) - selector = ellipse.as_mpl_selector(ax, callback=update_sel, use_data_coordinates=True) + selector = ellipse.as_mpl_selector(ax, callback=update_sel) hdulist.close() diff --git a/regions/shapes/ellipse.py b/regions/shapes/ellipse.py index c2ddbbf50..8fa2f3cc0 100644 --- a/regions/shapes/ellipse.py +++ b/regions/shapes/ellipse.py @@ -241,6 +241,10 @@ def as_mpl_selector(self, ax, active=True, sync=True, callback=None, region is updated. This only has an effect if ``sync`` is `True`. If a callback is set, it is called for the first time once the selector has been created. + drag_from_anywhere : bool, optional + If `True`, the selector can be moved by clicking anywhere within + its bounds, else only at the central anchor + (only available with matplotlib 3.5 upwards; default: `False`). **kwargs : dict Additional keyword arguments that are passed to `matplotlib.widgets.EllipseSelector`. @@ -258,14 +262,13 @@ def as_mpl_selector(self, ax, active=True, sync=True, callback=None, ``selector.set_active(True)`` or ``selector.set_active(False)``. """ from matplotlib.widgets import EllipseSelector + from .utils import MPL_VERSION if hasattr(self, '_mpl_selector'): - raise Exception('Cannot attach more than one selector to a ' - 'region.') + raise AttributeError('Cannot attach more than one selector to a region.') if self.angle.value != 0: - raise NotImplementedError('Cannot create matplotlib selector for ' - 'rotated ellipse.') + raise NotImplementedError('Cannot create matplotlib selector for rotated ellipse.') if sync: sync_callback = self._update_from_mpl_selector @@ -273,12 +276,18 @@ def as_mpl_selector(self, ax, active=True, sync=True, callback=None, def sync_callback(*args, **kwargs): pass - self._mpl_selector = EllipseSelector( - ax, sync_callback, interactive=True, - rectprops={'edgecolor': self.visual.get('color', 'black'), - 'facecolor': 'none', - 'linewidth': self.visual.get('linewidth', 1), - 'linestyle': self.visual.get('linestyle', 'solid')}) + rectprops = {'edgecolor': self.visual.get('color', 'black'), + 'facecolor': 'none', + 'linewidth': self.visual.get('linewidth', 1), + 'linestyle': self.visual.get('linestyle', 'solid')} + rectprops.update(kwargs.pop('props', dict())) + # `rectprops` renamed `props` in mpl 3.5 and deprecated for 3.7. + if MPL_VERSION < 35: + kwargs.update({'rectprops': rectprops}) + else: + kwargs.update({'props': rectprops}) + + self._mpl_selector = EllipseSelector(ax, sync_callback, interactive=True, **kwargs) self._mpl_selector.extents = (self.center.x - self.width / 2, self.center.x + self.width / 2, diff --git a/regions/shapes/rectangle.py b/regions/shapes/rectangle.py index 697cdb9b2..6ea634992 100644 --- a/regions/shapes/rectangle.py +++ b/regions/shapes/rectangle.py @@ -234,6 +234,10 @@ def as_mpl_selector(self, ax, active=True, sync=True, callback=None, region is updated. This only has an effect if ``sync`` is `True`. If a callback is set, it is called for the first time once the selector has been created. + drag_from_anywhere : bool, optional + If `True`, the selector can be moved by clicking anywhere within + its bounds, else only at the central anchor + (only available with matplotlib 3.5 upwards; default: `False`). **kwargs : dict Additional keyword arguments are passed to `matplotlib.widgets.RectangleSelector`. @@ -251,10 +255,10 @@ def as_mpl_selector(self, ax, active=True, sync=True, callback=None, ``selector.set_active(True)`` or ``selector.set_active(False)``. """ from matplotlib.widgets import RectangleSelector + from .utils import MPL_VERSION if hasattr(self, '_mpl_selector'): - raise Exception('Cannot attach more than one selector to a ' - 'region.') + raise AttributeError('Cannot attach more than one selector to a region.') if self.angle.value != 0: raise NotImplementedError('Cannot create matplotlib selector for ' @@ -266,12 +270,18 @@ def as_mpl_selector(self, ax, active=True, sync=True, callback=None, def sync_callback(*args, **kwargs): pass - self._mpl_selector = RectangleSelector( - ax, sync_callback, interactive=True, - rectprops={'edgecolor': self.visual.get('color', 'black'), - 'facecolor': 'none', - 'linewidth': self.visual.get('linewidth', 1), - 'linestyle': self.visual.get('linestyle', 'solid')}) + rectprops = {'edgecolor': self.visual.get('color', 'black'), + 'facecolor': 'none', + 'linewidth': self.visual.get('linewidth', 1), + 'linestyle': self.visual.get('linestyle', 'solid')} + rectprops.update(kwargs.pop('props', dict())) + # `rectprops` renamed `props` in mpl 3.5 and deprecated for 3.7. + if MPL_VERSION < 35: + kwargs.update({'rectprops': rectprops}) + else: + kwargs.update({'props': rectprops}) + + self._mpl_selector = RectangleSelector(ax, sync_callback, interactive=True, **kwargs) self._mpl_selector.extents = (self.center.x - self.width / 2, self.center.x + self.width / 2, diff --git a/regions/shapes/tests/test_circle.py b/regions/shapes/tests/test_circle.py index 14a634bca..b159c2dcb 100644 --- a/regions/shapes/tests/test_circle.py +++ b/regions/shapes/tests/test_circle.py @@ -14,7 +14,7 @@ from ...tests.helpers import make_simple_wcs from ..circle import CirclePixelRegion, CircleSkyRegion from .test_common import BaseTestPixelRegion, BaseTestSkyRegion -from .utils import HAS_MATPLOTLIB # noqa +from ..utils import HAS_MATPLOTLIB # noqa @pytest.fixture(scope='session', name='wcs') diff --git a/regions/shapes/tests/test_ellipse.py b/regions/shapes/tests/test_ellipse.py index 64f173619..eb8fc07f0 100644 --- a/regions/shapes/tests/test_ellipse.py +++ b/regions/shapes/tests/test_ellipse.py @@ -15,7 +15,7 @@ from ...tests.helpers import make_simple_wcs from ..ellipse import EllipsePixelRegion, EllipseSkyRegion from .test_common import BaseTestPixelRegion, BaseTestSkyRegion -from .utils import HAS_MATPLOTLIB # noqa +from ..utils import HAS_MATPLOTLIB, MPL_VERSION # noqa @pytest.fixture(scope='session', name='wcs') @@ -100,13 +100,12 @@ def test_rotate(self): assert_allclose(reg.center.xy, (1, 4)) assert_allclose(reg.angle.to_value("deg"), 95) - # TODO: Is this MatplotlibDeprecationWarning something to worry about? - @pytest.mark.filterwarnings(r"ignore:The 'rectprops' parameter of " - r"__init__\(\) has been renamed 'props'") + @pytest.mark.skipif(MPL_VERSION < 33, reason='requires `do_event`') @pytest.mark.parametrize('sync', (False, True)) def test_as_mpl_selector(self, sync): plt = pytest.importorskip('matplotlib.pyplot') + from matplotlib.testing.widgets import do_event data = np.random.random((16, 16)) mask = np.zeros_like(data) @@ -115,39 +114,23 @@ def test_as_mpl_selector(self, sync): ax.imshow(data) def update_mask(reg): - mask[:] = reg.to_mask(mode='subpixels', - subpixels=10).to_image(data.shape) + mask[:] = reg.to_mask(mode='subpixels', subpixels=10).to_image(data.shape) # For now this will only work with unrotated ellipses. Once this # works with rotated ellipses, the following exception check can # be removed as well as the ``angle=0 * u.deg`` in the call to # copy() below. with pytest.raises(NotImplementedError, - match=('Cannot create matplotlib selector for ' - 'rotated ellipse.')): + match=('Cannot create matplotlib selector for rotated ellipse.')): self.reg.as_mpl_selector(ax) region = self.reg.copy(angle=0 * u.deg) selector = region.as_mpl_selector(ax, callback=update_mask, sync=sync) # noqa - from matplotlib.backend_bases import MouseEvent, MouseButton - - x, y = ax.transData.transform([[7.3, 4.4]])[0] - ax.figure.canvas.callbacks.process('button_press_event', - MouseEvent('button_press_event', - ax.figure.canvas, x, y, - button=MouseButton.LEFT)) - x, y = ax.transData.transform([[9.3, 5.4]])[0] - ax.figure.canvas.callbacks.process('motion_notify_event', - MouseEvent('button_press_event', - ax.figure.canvas, x, y, - button=MouseButton.LEFT)) - x, y = ax.transData.transform([[9.3, 5.4]])[0] - ax.figure.canvas.callbacks.process('button_release_event', - MouseEvent('button_press_event', - ax.figure.canvas, x, y, - button=MouseButton.LEFT)) + do_event(selector, 'press', xdata=7.3, ydata=4.4, button=1) + do_event(selector, 'onmove', xdata=9.3, ydata=5.4, button=1) + do_event(selector, 'release', xdata=9.3, ydata=5.4, button=1) ax.figure.canvas.draw() @@ -159,9 +142,7 @@ def update_mask(reg): assert_allclose(region.height, 1) assert_quantity_allclose(region.angle, 0 * u.deg) - assert_equal(mask, - region.to_mask(mode='subpixels', - subpixels=10).to_image(data.shape)) + assert_equal(mask, region.to_mask(mode='subpixels', subpixels=10).to_image(data.shape)) else: @@ -173,10 +154,110 @@ def update_mask(reg): assert_equal(mask, 0) - with pytest.raises(Exception, match=('Cannot attach more than one ' - 'selector to a region.')): + with pytest.raises(AttributeError, match=('Cannot attach more than one selector to a reg')): region.as_mpl_selector(ax) + @pytest.mark.skipif(MPL_VERSION < 33, reason='requires `do_event`') + @pytest.mark.parametrize('anywhere', (False, True)) + def test_mpl_selector_drag(self, anywhere): + """Test dragging of entire region from central handle and anywhere.""" + + plt = pytest.importorskip('matplotlib.pyplot') + from matplotlib.testing.widgets import do_event + + data = np.random.random((16, 16)) + mask = np.zeros_like(data) + + ax = plt.subplot(1, 1, 1) + ax.imshow(data) + + def update_mask(reg): + mask[:] = reg.to_mask(mode='subpixels', subpixels=10).to_image(data.shape) + + region = self.reg.copy(angle=0 * u.deg) + + if anywhere and MPL_VERSION < 35: + pytest.skip('Requires `drag_from_anywhere` kwarg') + elif MPL_VERSION < 35: + selector = region.as_mpl_selector(ax, callback=update_mask) + else: + selector = region.as_mpl_selector(ax, callback=update_mask, drag_from_anywhere=anywhere) + assert selector.drag_from_anywhere is anywhere + assert region._mpl_selector.drag_from_anywhere is anywhere + + # click_and_drag(selector, start=(3, 4), end=(3.5, 4.5)) + do_event(selector, 'press', xdata=3, ydata=4, button=1) + do_event(selector, 'onmove', xdata=3.5, ydata=4.5, button=1) + do_event(selector, 'release', xdata=3.5, ydata=4.5, button=1) + + ax.figure.canvas.draw() + + assert_allclose(region.center.x, 3.5) + assert_allclose(region.center.y, 4.5) + assert_allclose(region.width, 4) + assert_allclose(region.height, 3) + + do_event(selector, 'press', xdata=3.25, ydata=4.25, button=1) + do_event(selector, 'onmove', xdata=4.25, ydata=5.25, button=1) + do_event(selector, 'release', xdata=4.25, ydata=5.25, button=1) + + ax.figure.canvas.draw() + + # For drag_from_anywhere=False this will have created a new 1x1 rectangle. + if anywhere: + assert_allclose(region.center.x, 4.5) + assert_allclose(region.center.y, 5.5) + assert_allclose(region.width, 4) + assert_allclose(region.height, 3) + else: + assert_allclose(region.center.x, 4.5) + assert_allclose(region.center.y, 5.5) + + assert_equal(mask, region.to_mask(mode='subpixels', subpixels=10).to_image(data.shape)) + + @pytest.mark.parametrize('userargs', + ({'useblit': True}, + {'grab_range': 20, 'minspanx': 5, 'minspany': 4}, + {'props': {'facecolor': 'blue', 'linewidth': 2}}, + {'twit': 'gumby'})) + def test_mpl_selector_kwargs(self, userargs): + """Test that additional kwargs are passed to selector.""" + + plt = pytest.importorskip('matplotlib.pyplot') + + data = np.random.random((16, 16)) + mask = np.zeros_like(data) + + ax = plt.subplot(1, 1, 1) + ax.imshow(data) + + def update_mask(reg): + mask[:] = reg.to_mask(mode='subpixels', subpixels=10).to_image(data.shape) + + region = self.reg.copy(angle=0 * u.deg) + region.visual = {'color': 'red'} + + if MPL_VERSION < 35 and 'grab_range' in userargs: + userargs['maxdist'] = userargs.pop('grab_range') + + if 'twit' in userargs: + with pytest.raises(TypeError, match=(r'__init__.. got an unexpected keyword argument')): + selector = region.as_mpl_selector(ax, callback=update_mask, **userargs) + else: + selector = region.as_mpl_selector(ax, callback=update_mask, **userargs) + assert region._mpl_selector.artists[0].get_edgecolor() == (1, 0, 0, 1) + + if 'props' in userargs: + assert region._mpl_selector.artists[0].get_facecolor() == (0, 0, 1, 1) + assert region._mpl_selector.artists[0].get_linewidth() == 2 + else: + assert region._mpl_selector.artists[0].get_facecolor() == (0, 0, 0, 0) + assert region._mpl_selector.artists[0].get_linewidth() == 1 + + for key, val in userargs.items(): + assert getattr(region._mpl_selector, key) == val + assert getattr(selector, key) == val + class TestEllipseSkyRegion(BaseTestSkyRegion): meta = RegionMeta({'text': 'test'}) diff --git a/regions/shapes/tests/test_line.py b/regions/shapes/tests/test_line.py index 6db4221ce..ac7eba4c7 100644 --- a/regions/shapes/tests/test_line.py +++ b/regions/shapes/tests/test_line.py @@ -15,7 +15,7 @@ from ...tests.helpers import make_simple_wcs from ..line import LinePixelRegion, LineSkyRegion from .test_common import BaseTestPixelRegion, BaseTestSkyRegion -from .utils import HAS_MATPLOTLIB # noqa +from ..utils import HAS_MATPLOTLIB # noqa @pytest.fixture(scope='session', name='wcs') diff --git a/regions/shapes/tests/test_point.py b/regions/shapes/tests/test_point.py index d431c9c90..90fe90740 100644 --- a/regions/shapes/tests/test_point.py +++ b/regions/shapes/tests/test_point.py @@ -14,7 +14,7 @@ from ...tests.helpers import make_simple_wcs from ..point import PointPixelRegion, PointSkyRegion from .test_common import BaseTestPixelRegion, BaseTestSkyRegion -from .utils import HAS_MATPLOTLIB # noqa +from ..utils import HAS_MATPLOTLIB # noqa @pytest.fixture(scope='session', name='wcs') diff --git a/regions/shapes/tests/test_polygon.py b/regions/shapes/tests/test_polygon.py index 3088f8f78..44902d012 100644 --- a/regions/shapes/tests/test_polygon.py +++ b/regions/shapes/tests/test_polygon.py @@ -15,7 +15,7 @@ from ..polygon import (PolygonPixelRegion, RegularPolygonPixelRegion, PolygonSkyRegion) from .test_common import BaseTestPixelRegion, BaseTestSkyRegion -from .utils import HAS_MATPLOTLIB # noqa +from ..utils import HAS_MATPLOTLIB # noqa @pytest.fixture(scope='session', name='wcs') diff --git a/regions/shapes/tests/test_rectangle.py b/regions/shapes/tests/test_rectangle.py index 46d5cb5f8..93de1a4ea 100644 --- a/regions/shapes/tests/test_rectangle.py +++ b/regions/shapes/tests/test_rectangle.py @@ -15,7 +15,7 @@ from ...tests.helpers import make_simple_wcs from ..rectangle import RectanglePixelRegion, RectangleSkyRegion from .test_common import BaseTestPixelRegion, BaseTestSkyRegion -from .utils import HAS_MATPLOTLIB # noqa +from ..utils import HAS_MATPLOTLIB, MPL_VERSION # noqa @pytest.fixture(scope='session', name='wcs') @@ -103,12 +103,11 @@ def test_rotate(self): assert_allclose(reg.center.xy, (1, 4)) assert_allclose(reg.angle.to_value('deg'), 95) - # TODO: Is this MatplotlibDeprecationWarning something to worry about? - @pytest.mark.filterwarnings(r"ignore:The 'rectprops' parameter of " - r"__init__\(\) has been renamed 'props'") + @pytest.mark.skipif(MPL_VERSION < 33, reason='requires `do_event`') @pytest.mark.parametrize('sync', (False, True)) def test_as_mpl_selector(self, sync): plt = pytest.importorskip('matplotlib.pyplot') + from matplotlib.testing.widgets import do_event data = np.random.random((16, 16)) mask = np.zeros_like(data) @@ -117,39 +116,23 @@ def test_as_mpl_selector(self, sync): ax.imshow(data) def update_mask(reg): - mask[:] = reg.to_mask( - mode='subpixels', subpixels=10).to_image(data.shape) + mask[:] = reg.to_mask(mode='subpixels', subpixels=10).to_image(data.shape) # For now this will only work with unrotated rectangles. Once # this works with rotated rectangles, the following exception # check can be removed as well as the ``angle=0 * u.deg`` in the # call to copy() below. with pytest.raises(NotImplementedError, - match=('Cannot create matplotlib selector for ' - 'rotated rectangle.')): + match=('Cannot create matplotlib selector for rotated rectangle.')): self.reg.as_mpl_selector(ax) region = self.reg.copy(angle=0 * u.deg) selector = region.as_mpl_selector(ax, callback=update_mask, sync=sync) # noqa - from matplotlib.backend_bases import MouseEvent, MouseButton - - x, y = ax.transData.transform([[7.3, 4.4]])[0] - ax.figure.canvas.callbacks.process('button_press_event', - MouseEvent('button_press_event', - ax.figure.canvas, x, y, - button=MouseButton.LEFT)) - x, y = ax.transData.transform([[9.3, 5.4]])[0] - ax.figure.canvas.callbacks.process('motion_notify_event', - MouseEvent('button_press_event', - ax.figure.canvas, x, y, - button=MouseButton.LEFT)) - x, y = ax.transData.transform([[9.3, 5.4]])[0] - ax.figure.canvas.callbacks.process('button_release_event', - MouseEvent('button_press_event', - ax.figure.canvas, x, y, - button=MouseButton.LEFT)) + do_event(selector, 'press', xdata=7.3, ydata=4.4, button=1) + do_event(selector, 'onmove', xdata=9.3, ydata=5.4, button=1) + do_event(selector, 'release', xdata=9.3, ydata=5.4, button=1) ax.figure.canvas.draw() @@ -160,8 +143,7 @@ def update_mask(reg): assert_allclose(region.height, 1) assert_quantity_allclose(region.angle, 0 * u.deg) - assert_equal(mask, region.to_mask( - mode='subpixels', subpixels=10).to_image(data.shape)) + assert_equal(mask, region.to_mask(mode='subpixels', subpixels=10).to_image(data.shape)) else: assert_allclose(region.center.x, 3) @@ -172,10 +154,109 @@ def update_mask(reg): assert_equal(mask, 0) - with pytest.raises(Exception, match=('Cannot attach more than one ' - 'selector to a region.')): + with pytest.raises(AttributeError, match=('Cannot attach more than one selector to a reg')): region.as_mpl_selector(ax) + @pytest.mark.skipif(MPL_VERSION < 33, reason='requires `do_event`') + @pytest.mark.parametrize('anywhere', (False, True)) + def test_mpl_selector_drag(self, anywhere): + """Test dragging of entire region from central handle and anywhere.""" + + plt = pytest.importorskip('matplotlib.pyplot') + from matplotlib.testing.widgets import do_event # click_and_drag # MPL_VERSION >= 36 + + data = np.random.random((16, 16)) + mask = np.zeros_like(data) + + ax = plt.subplot(1, 1, 1) + ax.imshow(data) + + def update_mask(reg): + mask[:] = reg.to_mask(mode='subpixels', subpixels=10).to_image(data.shape) + + region = self.reg.copy(angle=0 * u.deg) + + if anywhere and MPL_VERSION < 35: + pytest.skip('Requires `drag_from_anywhere` kwarg') + elif MPL_VERSION < 35: + selector = region.as_mpl_selector(ax, callback=update_mask) + else: + selector = region.as_mpl_selector(ax, callback=update_mask, drag_from_anywhere=anywhere) + assert selector.drag_from_anywhere is anywhere + assert region._mpl_selector.drag_from_anywhere is anywhere + + # click_and_drag(selector, start=(3, 4), end=(3.5, 4.5)) + do_event(selector, 'press', xdata=3, ydata=4, button=1) + do_event(selector, 'onmove', xdata=3.5, ydata=4.5, button=1) + do_event(selector, 'release', xdata=3.5, ydata=4.5, button=1) + + ax.figure.canvas.draw() + + assert_allclose(region.center.x, 3.5) + assert_allclose(region.center.y, 4.5) + assert_allclose(region.width, 4) + assert_allclose(region.height, 3) + + do_event(selector, 'press', xdata=3.25, ydata=4.25, button=1) + do_event(selector, 'onmove', xdata=4.25, ydata=5.25, button=1) + do_event(selector, 'release', xdata=4.25, ydata=5.25, button=1) + + ax.figure.canvas.draw() + + # For drag_from_anywhere=False this will have created a new 1x1 rectangle. + if anywhere: + assert_allclose(region.center.x, 4.5) + assert_allclose(region.center.y, 5.5) + assert_allclose(region.width, 4) + assert_allclose(region.height, 3) + else: + assert_allclose(region.center.x, 4.5) + assert_allclose(region.center.y, 5.5) + + assert_equal(mask, region.to_mask(mode='subpixels', subpixels=10).to_image(data.shape)) + + @pytest.mark.parametrize('userargs', + ({'useblit': True}, + {'grab_range': 20, 'minspanx': 5, 'minspany': 4}, + {'props': {'facecolor': 'blue', 'linewidth': 2}}, + {'twit': 'gumby'})) + def test_mpl_selector_kwargs(self, userargs): + """Test that additional kwargs are passed to selector.""" + + plt = pytest.importorskip('matplotlib.pyplot') + + data = np.random.random((16, 16)) + mask = np.zeros_like(data) + + ax = plt.subplot(1, 1, 1) + ax.imshow(data) + + def update_mask(reg): + mask[:] = reg.to_mask(mode='subpixels', subpixels=10).to_image(data.shape) + + region = self.reg.copy(angle=0 * u.deg) + + if MPL_VERSION < 35 and 'grab_range' in userargs: + userargs['maxdist'] = userargs.pop('grab_range') + + if 'twit' in userargs: + with pytest.raises(TypeError, match=(r'__init__.. got an unexpected keyword argument')): + selector = region.as_mpl_selector(ax, callback=update_mask, **userargs) + else: + selector = region.as_mpl_selector(ax, callback=update_mask, **userargs) + assert region._mpl_selector.artists[0].get_edgecolor() == (0, 0, 1, 1) + + if 'props' in userargs: + assert region._mpl_selector.artists[0].get_facecolor() == (0, 0, 1, 1) + assert region._mpl_selector.artists[0].get_linewidth() == 2 + else: + assert region._mpl_selector.artists[0].get_facecolor() == (0, 0, 0, 0) + assert region._mpl_selector.artists[0].get_linewidth() == 1 + + for key, val in userargs.items(): + assert getattr(region._mpl_selector, key) == val + assert getattr(selector, key) == val + def test_rectangular_pixel_region_bbox(): # odd sizes diff --git a/regions/shapes/tests/utils.py b/regions/shapes/tests/utils.py deleted file mode 100644 index f879f6251..000000000 --- a/regions/shapes/tests/utils.py +++ /dev/null @@ -1,7 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -try: - import matplotlib # noqa - HAS_MATPLOTLIB = True -except ImportError: - HAS_MATPLOTLIB = False diff --git a/regions/shapes/utils.py b/regions/shapes/utils.py new file mode 100644 index 000000000..1732ae465 --- /dev/null +++ b/regions/shapes/utils.py @@ -0,0 +1,13 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +try: + import matplotlib # noqa + HAS_MATPLOTLIB = True + MPL_VERSION = getattr(matplotlib, '__version__', None) + if MPL_VERSION is None: + MPL_VERSION = matplotlib._version.version + MPL_VERSION = MPL_VERSION.split('.') + MPL_VERSION = 10 * int(MPL_VERSION[0]) + int(MPL_VERSION[1]) +except ImportError: + HAS_MATPLOTLIB = False + MPL_VERSION = 0