From 65c1d4eec674103a59d69e3fe21762df44c236de Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 27 Dec 2014 10:52:27 -0600 Subject: [PATCH 01/17] Update viewer for PyQt5 compatibility Conflicts: skimage/viewer/tests/test_plugins.py skimage/viewer/tests/test_tools.py --- skimage/__init__.py | 8 ++++- skimage/viewer/__init__.py | 14 +++------ skimage/viewer/canvastools/base.py | 5 +-- skimage/viewer/canvastools/linetool.py | 5 +-- skimage/viewer/canvastools/painttool.py | 11 +++---- skimage/viewer/canvastools/recttool.py | 6 +--- skimage/viewer/plugins/base.py | 16 +++------- skimage/viewer/plugins/color_histogram.py | 5 +-- skimage/viewer/plugins/plotplugin.py | 2 +- skimage/viewer/qt.py | 8 +++++ skimage/viewer/qt/QtCore.py | 22 ------------- skimage/viewer/qt/QtGui.py | 11 ------- skimage/viewer/qt/README.rst | 5 --- skimage/viewer/qt/__init__.py | 22 ------------- skimage/viewer/tests/test_plugins.py | 33 +++++++++++++------- skimage/viewer/tests/test_tools.py | 30 ++++++++++++++++++ skimage/viewer/tests/test_utils.py | 20 ++++++------ skimage/viewer/tests/test_viewer.py | 15 +++++---- skimage/viewer/tests/test_widgets.py | 27 +++++++++------- skimage/viewer/utils/core.py | 31 ++++++------------ skimage/viewer/utils/dialogs.py | 2 +- skimage/viewer/viewers/core.py | 32 ++++++++----------- skimage/viewer/widgets/core.py | 38 ++++++++++------------- skimage/viewer/widgets/history.py | 4 +-- 24 files changed, 161 insertions(+), 211 deletions(-) create mode 100644 skimage/viewer/qt.py delete mode 100644 skimage/viewer/qt/QtCore.py delete mode 100644 skimage/viewer/qt/QtGui.py delete mode 100644 skimage/viewer/qt/README.rst delete mode 100644 skimage/viewer/qt/__init__.py diff --git a/skimage/__init__.py b/skimage/__init__.py index fb38f87a..031ae075 100644 --- a/skimage/__init__.py +++ b/skimage/__init__.py @@ -60,7 +60,13 @@ import os.path as _osp import imp as _imp import functools as _functools import warnings as _warnings -from skimage._shared.utils import deprecated as _deprecated + +import matplotlib as _mpl +try: + _imp.find_module('PyQt5') + _mpl.use('Qt5Agg') +except ImportError: + _mpl.use('Qt4Agg') pkg_dir = _osp.abspath(_osp.dirname(__file__)) data_dir = _osp.join(pkg_dir, 'data') diff --git a/skimage/viewer/__init__.py b/skimage/viewer/__init__.py index 4bf70a93..365e00a2 100644 --- a/skimage/viewer/__init__.py +++ b/skimage/viewer/__init__.py @@ -1,11 +1,7 @@ -from warnings import warn -from skimage._shared.version_requirements import is_installed +try: + from .qt import QtGui as _QtGui +except ImportError as e: + raise + raise ImportError('Viewer requires Qt') from .viewers import ImageViewer, CollectionViewer -from .qt import qt_api - -viewer_available = not qt_api is None and is_installed('matplotlib') -if not viewer_available: - warn('Viewer requires matplotlib and Qt') - -del qt_api, is_installed, warn diff --git a/skimage/viewer/canvastools/base.py b/skimage/viewer/canvastools/base.py index 197800c0..4b1d0c00 100644 --- a/skimage/viewer/canvastools/base.py +++ b/skimage/viewer/canvastools/base.py @@ -1,8 +1,5 @@ import numpy as np -try: - from matplotlib import lines -except ImportError: - pass +from matplotlib import lines __all__ = ['CanvasToolBase', 'ToolHandles'] diff --git a/skimage/viewer/canvastools/linetool.py b/skimage/viewer/canvastools/linetool.py index f18a1915..58b05bc4 100644 --- a/skimage/viewer/canvastools/linetool.py +++ b/skimage/viewer/canvastools/linetool.py @@ -1,9 +1,6 @@ import numpy as np -try: - from matplotlib import lines -except ImportError: - pass +from matplotlib import lines from skimage.viewer.canvastools.base import CanvasToolBase, ToolHandles diff --git a/skimage/viewer/canvastools/painttool.py b/skimage/viewer/canvastools/painttool.py index 953f6ebe..9f4bb8ec 100644 --- a/skimage/viewer/canvastools/painttool.py +++ b/skimage/viewer/canvastools/painttool.py @@ -1,11 +1,8 @@ import numpy as np -try: - import matplotlib.pyplot as plt - import matplotlib.colors as mcolors - LABELS_CMAP = mcolors.ListedColormap(['white', 'red', 'dodgerblue', 'gold', - 'greenyellow', 'blueviolet']) -except ImportError: - pass +import matplotlib.pyplot as plt +import matplotlib.colors as mcolors +LABELS_CMAP = mcolors.ListedColormap(['white', 'red', 'dodgerblue', 'gold', + 'greenyellow', 'blueviolet']) from skimage.viewer.canvastools.base import CanvasToolBase diff --git a/skimage/viewer/canvastools/recttool.py b/skimage/viewer/canvastools/recttool.py index 4acccf8a..4520584e 100644 --- a/skimage/viewer/canvastools/recttool.py +++ b/skimage/viewer/canvastools/recttool.py @@ -1,8 +1,4 @@ -try: - from matplotlib.widgets import RectangleSelector -except ImportError: - RectangleSelector = object - +from matplotlib.widgets import RectangleSelector from skimage.viewer.canvastools.base import CanvasToolBase from skimage.viewer.canvastools.base import ToolHandles diff --git a/skimage/viewer/plugins/base.py b/skimage/viewer/plugins/base.py index def0bfd9..7b0418b2 100644 --- a/skimage/viewer/plugins/base.py +++ b/skimage/viewer/plugins/base.py @@ -4,19 +4,11 @@ Base class for Plugins that interact with ImageViewer. from warnings import warn import numpy as np - -from ..qt import QtGui, qt_api -from ..qt.QtCore import Qt, Signal +from ..qt import QtWidgets, QtCore, Signal from ..utils import RequiredAttr, init_qtapp -from skimage._shared.testing import doctest_skip_parser - -if qt_api is not None: - has_qt = True -else: - has_qt = False -class Plugin(QtGui.QDialog): +class Plugin(QtWidgets.QDialog): """Base class for plugins that interact with an ImageViewer. A plugin connects an image filter (or another function) to an image viewer. @@ -101,7 +93,7 @@ class Plugin(QtGui.QDialog): "then the `image_filter` argument is ignored.") self.setWindowTitle(self.name) - self.layout = QtGui.QGridLayout(self) + self.layout = QtWidgets.QGridLayout(self) self.resize(width, height) self.row = 0 @@ -124,7 +116,7 @@ class Plugin(QtGui.QDialog): the image matches the filtered value specified by attached widgets. """ self.setParent(image_viewer) - self.setWindowFlags(Qt.Dialog) + self.setWindowFlags(QtCore.Qt.Dialog) self.image_viewer = image_viewer self.image_viewer.plugins.append(self) diff --git a/skimage/viewer/plugins/color_histogram.py b/skimage/viewer/plugins/color_histogram.py index 52c71786..d6c6ddfc 100644 --- a/skimage/viewer/plugins/color_histogram.py +++ b/skimage/viewer/plugins/color_histogram.py @@ -1,8 +1,5 @@ import numpy as np -try: - import matplotlib.pyplot as plt -except ImportError: - pass +import matplotlib.pyplot as plt from skimage import color from skimage import exposure from .plotplugin import PlotPlugin diff --git a/skimage/viewer/plugins/plotplugin.py b/skimage/viewer/plugins/plotplugin.py index 5de6be7b..f995efa8 100644 --- a/skimage/viewer/plugins/plotplugin.py +++ b/skimage/viewer/plugins/plotplugin.py @@ -1,6 +1,6 @@ import numpy as np -from ..qt import QtGui +from ..qt import QtGui from ..utils import new_plot from .base import Plugin diff --git a/skimage/viewer/qt.py b/skimage/viewer/qt.py new file mode 100644 index 00000000..bc827f9a --- /dev/null +++ b/skimage/viewer/qt.py @@ -0,0 +1,8 @@ +try: + from matplotlib.backends.qt_compat import QtGui, QtCore, QtWidgets +except ImportError: + from matplotlib.backends.qt4_compat import QtGui, QtCore + QtWidgets = QtGui + +Qt = QtCore.Qt +Signal = QtCore.Signal diff --git a/skimage/viewer/qt/QtCore.py b/skimage/viewer/qt/QtCore.py deleted file mode 100644 index 19b92a53..00000000 --- a/skimage/viewer/qt/QtCore.py +++ /dev/null @@ -1,22 +0,0 @@ -from . import qt_api - -if qt_api == 'pyside': - from PySide.QtCore import * -elif qt_api == 'pyqt': - from PyQt4.QtCore import * - # Use pyside names for signals and slots - Signal = pyqtSignal - Slot = pyqtSlot -else: - # Mock objects for buildbot (which doesn't have Qt, but imports viewer). - class Qt(object): - TopDockWidgetArea = None - BottomDockWidgetArea = None - LeftDockWidgetArea = None - RightDockWidgetArea = None - - def Signal(*args, **kwargs): - pass - - def Slot(*args, **kwargs): - pass diff --git a/skimage/viewer/qt/QtGui.py b/skimage/viewer/qt/QtGui.py deleted file mode 100644 index 12e9837f..00000000 --- a/skimage/viewer/qt/QtGui.py +++ /dev/null @@ -1,11 +0,0 @@ -from . import qt_api - -if qt_api == 'pyside': - from PySide.QtGui import * -elif qt_api == 'pyqt': - from PyQt4.QtGui import * -else: - # Mock objects - QMainWindow = object - QDialog = object - QWidget = object diff --git a/skimage/viewer/qt/README.rst b/skimage/viewer/qt/README.rst deleted file mode 100644 index 993ae1a5..00000000 --- a/skimage/viewer/qt/README.rst +++ /dev/null @@ -1,5 +0,0 @@ -This qt subpackage provides a wrapper to allow use of either PySide or PyQt4. -In addition, if neither package is available, some mock objects are created to -prevent errors in the TravisCI build. Only the objects used in the global -namespace need to be mocked (e.g., a Qt object that gets subclassed is used -in the global namespace). diff --git a/skimage/viewer/qt/__init__.py b/skimage/viewer/qt/__init__.py deleted file mode 100644 index 2fc4e946..00000000 --- a/skimage/viewer/qt/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import warnings - -qt_api = os.environ.get('QT_API') - -if qt_api is None: - try: - import PyQt4 - qt_api = 'pyqt' - except ImportError: - try: - import PySide - qt_api = 'pyside' - except ImportError: - qt_api = None - # Note that we don't want to raise an error because that would - # cause the TravisCI build to fail. - warnings.warn("Could not import PyQt4: ImageViewer not available!") - - -if qt_api is not None: - os.environ['QT_API'] = qt_api diff --git a/skimage/viewer/tests/test_plugins.py b/skimage/viewer/tests/test_plugins.py index 528df8f4..b5cf7ab9 100644 --- a/skimage/viewer/tests/test_plugins.py +++ b/skimage/viewer/tests/test_plugins.py @@ -4,15 +4,26 @@ import skimage import skimage.data as data from skimage.filters.rank import median from skimage.morphology import disk -from skimage.viewer import ImageViewer, viewer_available +try: + from skimage.viewer import ImageViewer + from skimage.viewer.plugins.base import Plugin + from skimage.viewer.widgets import Slider + from skimage.viewer.plugins import ( + LineProfile, Measure, CannyPlugin, LabelPainter, Crop, ColorHistogram, + PlotPlugin) +except ImportError: + ImageViewer is None from numpy.testing import assert_equal, assert_allclose, assert_almost_equal from numpy.testing.decorators import skipif +<<<<<<< HEAD from skimage.viewer.plugins import ( LineProfile, Measure, CannyPlugin, LabelPainter, Crop, ColorHistogram, PlotPlugin) from skimage.viewer.plugins.base import Plugin from skimage.viewer.widgets import Slider from skimage._shared._warnings import expected_warnings +======= +>>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility def setup_line_profile(image, limits='image'): @@ -22,7 +33,7 @@ def setup_line_profile(image, limits='image'): return plugin -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_line_profile(): """ Test a line profile using an ndim=2 image""" plugin = setup_line_profile(data.camera()) @@ -36,7 +47,7 @@ def test_line_profile(): assert_allclose(scan_data.mean(), 0.2812, rtol=1e-3) -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_line_profile_rgb(): """ Test a line profile using an ndim=3 image""" plugin = setup_line_profile(data.chelsea(), limits=None) @@ -51,7 +62,7 @@ def test_line_profile_rgb(): assert_allclose(scan_data.mean(), 0.4359, rtol=1e-3) -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_line_profile_dynamic(): """Test a line profile updating after an image transform""" image = data.coins()[:-50, :] # shave some off to make the line lower @@ -77,7 +88,7 @@ def test_line_profile_dynamic(): assert_almost_equal(np.max(line) - np.min(line), 0.639, 1) -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_measure(): image = data.camera() viewer = ImageViewer(image) @@ -89,7 +100,7 @@ def test_measure(): assert_equal(str(m._angle.text[:5]), '135.0') -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_canny(): image = data.camera() viewer = ImageViewer(image) @@ -102,7 +113,7 @@ def test_canny(): assert edges.sum() == 2852 -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_label_painter(): image = data.camera() moon = data.moon() @@ -120,7 +131,7 @@ def test_label_painter(): assert_equal(lp.paint_tool.shape, moon.shape) -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_crop(): image = data.camera() viewer = ImageViewer(image) @@ -131,7 +142,7 @@ def test_crop(): assert_equal(viewer.image.shape, (101, 101)) -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_color_histogram(): image = skimage.img_as_float(data.load('color.png')) viewer = ImageViewer(image) @@ -143,7 +154,7 @@ def test_color_histogram(): assert_almost_equal(viewer.image.std(), 0.325, 3) -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_plot_plugin(): viewer = ImageViewer(data.moon()) plugin = PlotPlugin(image_filter=lambda x: x) @@ -155,7 +166,7 @@ def test_plot_plugin(): viewer.close() -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_plugin(): img = skimage.img_as_float(data.moon()) viewer = ImageViewer(img) diff --git a/skimage/viewer/tests/test_tools.py b/skimage/viewer/tests/test_tools.py index 6b923063..6ea25940 100644 --- a/skimage/viewer/tests/test_tools.py +++ b/skimage/viewer/tests/test_tools.py @@ -4,12 +4,22 @@ import numpy as np from numpy.testing import assert_equal from numpy.testing.decorators import skipif from skimage import data +<<<<<<< HEAD from skimage.viewer import ImageViewer, viewer_available from skimage.viewer.canvastools import ( LineTool, ThickLineTool, RectangleTool, PaintTool) from skimage.viewer.canvastools.base import CanvasToolBase from matplotlib.testing.decorators import cleanup +======= +try: + from skimage.viewer import ImageViewer + from skimage.viewer.canvastools import ( + LineTool, ThickLineTool, RectangleTool, PaintTool) + from skimage.viewer.canvastools.base import CanvasToolBase +except ImportError: + ImageViewer = None +>>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility def get_end_points(image): @@ -74,8 +84,12 @@ def do_event(viewer, etype, button=1, xdata=0, ydata=0, key=None): func(event) +<<<<<<< HEAD @cleanup @skipif(not viewer_available) +======= +@skipif(ImageViewer is None) +>>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility def test_line_tool(): img = data.camera() viewer = ImageViewer(img) @@ -100,8 +114,12 @@ def test_line_tool(): assert_equal(tool.geometry, np.array([[100, 100], [10, 10]])) +<<<<<<< HEAD @cleanup @skipif(not viewer_available) +======= +@skipif(ImageViewer is None) +>>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility def test_thick_line_tool(): img = data.camera() viewer = ImageViewer(img) @@ -124,8 +142,12 @@ def test_thick_line_tool(): assert_equal(tool.linewidth, 1) +<<<<<<< HEAD @cleanup @skipif(not viewer_available) +======= +@skipif(ImageViewer is None) +>>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility def test_rect_tool(): img = data.camera() viewer = ImageViewer(img) @@ -153,8 +175,12 @@ def test_rect_tool(): assert_equal(tool.geometry, [10, 100, 10, 100]) +<<<<<<< HEAD @cleanup @skipif(not viewer_available) +======= +@skipif(ImageViewer is None) +>>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility def test_paint_tool(): img = data.moon() viewer = ImageViewer(img) @@ -187,8 +213,12 @@ def test_paint_tool(): assert_equal(tool.overlay.sum(), 0) +<<<<<<< HEAD @cleanup @skipif(not viewer_available) +======= +@skipif(ImageViewer is None) +>>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility def test_base_tool(): img = data.moon() viewer = ImageViewer(img) diff --git a/skimage/viewer/tests/test_utils.py b/skimage/viewer/tests/test_utils.py index 77f30299..5c962e5c 100644 --- a/skimage/viewer/tests/test_utils.py +++ b/skimage/viewer/tests/test_utils.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- -from skimage.viewer import viewer_available -from skimage.viewer.qt import QtCore, QtGui -from skimage.viewer import utils -from skimage.viewer.utils import dialogs +try: + from skimage.viewer import utils + from skimage.viewer.utils import dialogs + from skimage.viewer.qt import QtCore, QtGui +except ImportError: + QtCore = None from numpy.testing.decorators import skipif -from skimage.viewer import utils -from skimage.viewer.utils import dialogs -@skipif(not viewer_available) +@skipif(QtCore is None) def test_event_loop(): utils.init_qtapp() timer = QtCore.QTimer() @@ -16,7 +16,7 @@ def test_event_loop(): utils.start_qtapp() -@skipif(not viewer_available) +@skipif(QtCore is None) def test_format_filename(): fname = dialogs._format_filename(('apple', 2)) assert fname == 'apple' @@ -24,7 +24,7 @@ def test_format_filename(): assert fname is None -@skipif(not viewer_available) +@skipif(QtCore is None) def test_open_file_dialog(): utils.init_qtapp() timer = QtCore.QTimer() @@ -33,7 +33,7 @@ def test_open_file_dialog(): assert filename is None -@skipif(not viewer_available) +@skipif(QtCore is None) def test_save_file_dialog(): utils.init_qtapp() timer = QtCore.QTimer() diff --git a/skimage/viewer/tests/test_viewer.py b/skimage/viewer/tests/test_viewer.py index 1604ca6d..0709d18d 100644 --- a/skimage/viewer/tests/test_viewer.py +++ b/skimage/viewer/tests/test_viewer.py @@ -1,9 +1,12 @@ from skimage import data -from skimage.viewer.qt import QtGui, QtCore -from skimage.viewer import ImageViewer, CollectionViewer, viewer_available +try: + from skimage.viewer.qt import QtGui, QtCore + from skimage.viewer import ImageViewer, CollectionViewer + from skimage.viewer.plugins import OverlayPlugin +except ImportError: + ImageViewer = None from skimage.transform import pyramid_gaussian -from skimage.viewer.plugins import OverlayPlugin from skimage.filters import sobel from numpy.testing import assert_equal from numpy.testing.decorators import skipif @@ -11,7 +14,7 @@ from skimage._shared.version_requirements import is_installed from skimage._shared._warnings import expected_warnings -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_viewer(): astro = data.astronaut() coins = data.coins() @@ -38,7 +41,7 @@ def make_key_event(key): QtCore.Qt.NoModifier) -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_collection_viewer(): img = data.astronaut() @@ -54,7 +57,7 @@ def test_collection_viewer(): view._format_coord(10, 10) -@skipif(not viewer_available) +@skipif(ImageViewer is None) @skipif(not is_installed('matplotlib', '>=1.2')) def test_viewer_with_overlay(): img = data.coins() diff --git a/skimage/viewer/tests/test_widgets.py b/skimage/viewer/tests/test_widgets.py index 170c186c..8e76d29b 100644 --- a/skimage/viewer/tests/test_widgets.py +++ b/skimage/viewer/tests/test_widgets.py @@ -1,11 +1,14 @@ import os from skimage import data, img_as_float, io, img_as_uint -from skimage.viewer import ImageViewer, viewer_available -from skimage.viewer.widgets import ( - Slider, OKCancelButtons, SaveButtons, ComboBox, CheckBox, Text) -from skimage.viewer.plugins.base import Plugin -from skimage.viewer.qt import QtGui, QtCore +try: + from skimage.viewer import ImageViewer + from skimage.viewer.qt import QtGui, QtCore + from skimage.viewer.widgets import ( + Slider, OKCancelButtons, SaveButtons, ComboBox, CheckBox, Text) + from skimage.viewer.plugins.base import Plugin +except ImportError: + ImageViewer = None from numpy.testing import assert_almost_equal, assert_equal from numpy.testing.decorators import skipif from skimage._shared._warnings import expected_warnings @@ -18,7 +21,7 @@ def get_image_viewer(): return viewer -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_check_box(): viewer = get_image_viewer() cb = CheckBox('hello', value=True, alignment='left') @@ -33,7 +36,7 @@ def test_check_box(): assert_equal(cb.val, False) -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_combo_box(): viewer = get_image_viewer() cb = ComboBox('hello', ('a', 'b', 'c')) @@ -46,7 +49,7 @@ def test_combo_box(): assert_equal(cb.index, 2) -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_text_widget(): viewer = get_image_viewer() txt = Text('hello', 'hello, world!') @@ -57,7 +60,7 @@ def test_text_widget(): assert_equal(str(txt.text), 'goodbye, world!') -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_slider_int(): viewer = get_image_viewer() sld = Slider('radius', 2, 10, value_type='int') @@ -71,7 +74,7 @@ def test_slider_int(): assert_equal(sld.val, 5) -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_slider_float(): viewer = get_image_viewer() sld = Slider('alpha', 2.1, 3.1, value=2.1, value_type='float', @@ -86,7 +89,7 @@ def test_slider_float(): assert_almost_equal(sld.val, 2.5, 2) -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_save_buttons(): viewer = get_image_viewer() sv = SaveButtons() @@ -114,7 +117,7 @@ def test_save_buttons(): os.remove(filename) -@skipif(not viewer_available) +@skipif(ImageViewer is None) def test_ok_buttons(): viewer = get_image_viewer() ok = OKCancelButtons() diff --git a/skimage/viewer/utils/core.py b/skimage/viewer/utils/core.py index 242f2de4..dd81d2a7 100644 --- a/skimage/viewer/utils/core.py +++ b/skimage/viewer/utils/core.py @@ -1,28 +1,17 @@ import warnings import numpy as np +from skimage.viewer.qt import QtGui +import matplotlib as mpl +from matplotlib.figure import Figure +from matplotlib import _pylab_helpers +from matplotlib.colors import LinearSegmentedColormap -from ..qt import qt_api - -try: - import matplotlib as mpl - from matplotlib.figure import Figure - from matplotlib import _pylab_helpers - from matplotlib.colors import LinearSegmentedColormap - if not qt_api is None: - from matplotlib.backends.backend_qt4 import FigureManagerQT - from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg - if 'agg' not in mpl.get_backend().lower(): - print("Recommended matplotlib backend is `Agg` for full " - "skimage.viewer functionality.") - else: - FigureCanvasQTAgg = object - LinearSegmentedColormap = object -except ImportError: - FigureCanvasQTAgg = object # hack to prevent nosetest and autodoc errors - LinearSegmentedColormap = object - -from ..qt import QtGui +from matplotlib.backends.backend_qt4 import FigureManagerQT +from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg +if 'agg' not in mpl.get_backend().lower(): + warnings.warn("Recommended matplotlib backend is `Agg` for full " + "skimage.viewer functionality.") __all__ = ['init_qtapp', 'start_qtapp', 'RequiredAttr', 'figimage', diff --git a/skimage/viewer/utils/dialogs.py b/skimage/viewer/utils/dialogs.py index f160531d..20dd0e16 100644 --- a/skimage/viewer/utils/dialogs.py +++ b/skimage/viewer/utils/dialogs.py @@ -1,6 +1,6 @@ import os -from ..qt import QtGui +from skimage.viewer.qt import QtGui __all__ = ['open_file_dialog', 'save_file_dialog'] diff --git a/skimage/viewer/viewers/core.py b/skimage/viewer/viewers/core.py index 36ba6ee6..19112dc1 100644 --- a/skimage/viewer/viewers/core.py +++ b/skimage/viewer/viewers/core.py @@ -1,21 +1,15 @@ """ ImageViewer class for viewing and interacting with images. """ -from ..qt import QtGui, qt_api -from ..qt.QtCore import Qt, Signal - -if qt_api is not None: - has_qt = True -else: - has_qt = False +from skimage.viewer.qt import QtWidgets, Qt, Signal from skimage import io, img_as_float from skimage.util.dtype import dtype_range from skimage.exposure import rescale_intensity import numpy as np -from .. import utils from ..widgets import Slider -from ..utils import dialogs +from ..utils import ( + dialogs, init_qtapp, figimage, start_qtapp, update_axes_image) from ..plugins.base import Plugin @@ -152,7 +146,7 @@ class EventManager(object): tool.on_scroll(event) -class ImageViewer(QtGui.QMainWindow): +class ImageViewer(QtWidgets.QMainWindow): """Viewer for displaying images. This viewer is a simple container object that holds a Matplotlib axes @@ -194,7 +188,7 @@ class ImageViewer(QtGui.QMainWindow): def __init__(self, image, useblit=True): # Start main loop - utils.init_qtapp() + init_qtapp() super(ImageViewer, self).__init__() #TODO: Add ImageViewer to skimage.io window manager @@ -202,7 +196,7 @@ class ImageViewer(QtGui.QMainWindow): self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle("Image Viewer") - self.file_menu = QtGui.QMenu('&File', self) + self.file_menu = QtWidgets.QMenu('&File', self) self.file_menu.addAction('Open file', self.open_file, Qt.CTRL + Qt.Key_O) self.file_menu.addAction('Save to file', self.save_to_file, @@ -211,7 +205,7 @@ class ImageViewer(QtGui.QMainWindow): Qt.CTRL + Qt.Key_Q) self.menuBar().addMenu(self.file_menu) - self.main_widget = QtGui.QWidget() + self.main_widget = QtWidgets.QWidget() self.setCentralWidget(self.main_widget) if isinstance(image, Plugin): @@ -221,7 +215,7 @@ class ImageViewer(QtGui.QMainWindow): # When plugin is started, start plugin._started.connect(self._show) - self.fig, self.ax = utils.figimage(image) + self.fig, self.ax = figimage(image) self.canvas = self.fig.canvas self.canvas.setParent(self) self.ax.autoscale(enable=False) @@ -236,7 +230,7 @@ class ImageViewer(QtGui.QMainWindow): self._update_original_image(image) self.plugins = [] - self.layout = QtGui.QVBoxLayout(self.main_widget) + self.layout = QtWidgets.QVBoxLayout(self.main_widget) self.layout.addWidget(self.canvas) status_bar = self.statusBar() @@ -255,7 +249,7 @@ class ImageViewer(QtGui.QMainWindow): if plugin.dock: location = self.dock_areas[plugin.dock] dock_location = Qt.DockWidgetArea(location) - dock = QtGui.QDockWidget() + dock = QtWidgets.QDockWidget() dock.setWidget(plugin) dock.setWindowTitle(plugin.name) self.addDockWidget(dock_location, dock) @@ -341,7 +335,7 @@ class ImageViewer(QtGui.QMainWindow): """ self._show() if main_window: - utils.start_qtapp() + start_qtapp() return [p.output() for p in self.plugins] def redraw(self): @@ -357,7 +351,7 @@ class ImageViewer(QtGui.QMainWindow): @image.setter def image(self, image): self._img = image - utils.update_axes_image(self._image_plot, image) + update_axes_image(self._image_plot, image) # update display (otherwise image doesn't fill the canvas) h, w = image.shape[:2] @@ -487,7 +481,7 @@ class CollectionViewer(ImageViewer): self._update_original_image(image) def keyPressEvent(self, event): - if type(event) == QtGui.QKeyEvent: + if type(event) == QtWidgets.QKeyEvent: key = event.key() # Number keys (code: 0 = key 48, 9 = key 57) move to deciles if 48 <= key < 58: diff --git a/skimage/viewer/widgets/core.py b/skimage/viewer/widgets/core.py index 4da8925b..ae8be4e7 100644 --- a/skimage/viewer/widgets/core.py +++ b/skimage/viewer/widgets/core.py @@ -15,17 +15,15 @@ parameter type specified by its `ptype` attribute, which can be: property of the same name that updates the display. """ -from ..qt import QtGui -from ..qt import QtCore -from ..qt.QtCore import Qt - +from ..qt import QtWidgets, QtCore, Qt from ..utils import RequiredAttr __all__ = ['BaseWidget', 'Slider', 'ComboBox', 'CheckBox', 'Text'] -class BaseWidget(QtGui.QWidget): + +class BaseWidget(QtWidgets.QWidget): plugin = RequiredAttr("Widget is not attached to a Plugin.") @@ -49,11 +47,11 @@ class Text(BaseWidget): def __init__(self, name=None, text=''): super(Text, self).__init__(name) - self._label = QtGui.QLabel() + self._label = QtWidgets.QLabel() self.text = text - self.layout = QtGui.QHBoxLayout(self) + self.layout = QtWidgets.QHBoxLayout(self) if name is not None: - name_label = QtGui.QLabel() + name_label = QtWidgets.QLabel() name_label.setText(name) self.layout.addWidget(name_label) self.layout.addWidget(self._label) @@ -105,17 +103,17 @@ class Slider(BaseWidget): # Set widget orientation #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if orientation == 'vertical': - self.slider = QtGui.QSlider(Qt.Vertical) + self.slider = QtWidgets.QSlider(Qt.Vertical) alignment = QtCore.Qt.AlignHCenter align_text = QtCore.Qt.AlignHCenter align_value = QtCore.Qt.AlignHCenter - self.layout = QtGui.QVBoxLayout(self) + self.layout = QtWidgets.QVBoxLayout(self) elif orientation == 'horizontal': - self.slider = QtGui.QSlider(Qt.Horizontal) + self.slider = QtWidgets.QSlider(Qt.Horizontal) alignment = QtCore.Qt.AlignVCenter align_text = QtCore.Qt.AlignLeft align_value = QtCore.Qt.AlignRight - self.layout = QtGui.QHBoxLayout(self) + self.layout = QtWidgets.QHBoxLayout(self) else: msg = "Unexpected value %s for 'orientation'" raise ValueError(msg % orientation) @@ -151,11 +149,11 @@ class Slider(BaseWidget): raise ValueError("Unexpected value %s for 'update_on'" % update_on) self.slider.setFocusPolicy(QtCore.Qt.StrongFocus) - self.name_label = QtGui.QLabel() + self.name_label = QtWidgets.QLabel() self.name_label.setText(self.name) self.name_label.setAlignment(align_text) - self.editbox = QtGui.QLineEdit() + self.editbox = QtWidgets.QLineEdit() self.editbox.setMaximumWidth(max_edit_width) self.editbox.setText(self.value_fmt % self.val) self.editbox.setAlignment(align_value) @@ -229,20 +227,18 @@ class ComboBox(BaseWidget): def __init__(self, name, items, ptype='kwarg', callback=None): super(ComboBox, self).__init__(name, ptype, callback) - self.name_label = QtGui.QLabel() + self.name_label = QtWidgets.QLabel() self.name_label.setText(self.name) self.name_label.setAlignment(QtCore.Qt.AlignLeft) - self._combo_box = QtGui.QComboBox() + self._combo_box = QtWidgets.QComboBox() self._combo_box.addItems(list(items)) - self.layout = QtGui.QHBoxLayout(self) + self.layout = QtWidgets.QHBoxLayout(self) self.layout.addWidget(self.name_label) self.layout.addWidget(self._combo_box) self._combo_box.currentIndexChanged.connect(self._value_changed) - # self.connect(self._combo_box, - # SIGNAL("currentIndexChanged(int)"), self.updateUi) @property def val(self): @@ -283,11 +279,11 @@ class CheckBox(BaseWidget): callback=None): super(CheckBox, self).__init__(name, ptype, callback) - self._check_box = QtGui.QCheckBox() + self._check_box = QtWidgets.QCheckBox() self._check_box.setChecked(value) self._check_box.setText(self.name) - self.layout = QtGui.QHBoxLayout(self) + self.layout = QtWidgets.QHBoxLayout(self) if alignment == 'center': self.layout.setAlignment(QtCore.Qt.AlignCenter) elif alignment == 'left': diff --git a/skimage/viewer/widgets/history.py b/skimage/viewer/widgets/history.py index d1df8689..ba0f0cd3 100644 --- a/skimage/viewer/widgets/history.py +++ b/skimage/viewer/widgets/history.py @@ -1,8 +1,6 @@ from textwrap import dedent -from ..qt import QtGui -from ..qt import QtCore - +from ..qt import QtGui, QtCore import numpy as np import skimage From 47065d733c6cebafb4ca37decb9280e4e7b31b6f Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 23 Dec 2014 21:42:43 -0600 Subject: [PATCH 02/17] Use a smarter backend selection --- skimage/__init__.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/skimage/__init__.py b/skimage/__init__.py index 031ae075..f7339ba5 100644 --- a/skimage/__init__.py +++ b/skimage/__init__.py @@ -62,11 +62,10 @@ import functools as _functools import warnings as _warnings import matplotlib as _mpl -try: - _imp.find_module('PyQt5') - _mpl.use('Qt5Agg') -except ImportError: - _mpl.use('Qt4Agg') +if 'Qt5Agg' in _mpl.rcsetup.interactive_bk: + _mpl.rcParams['backend'] = 'Qt5Agg' +else: + _mpl.rcParams['backend'] = 'Qt4Agg' pkg_dir = _osp.abspath(_osp.dirname(__file__)) data_dir = _osp.join(pkg_dir, 'data') From 297ccea8b0a0fde4e89509f723502ba6c8b861d2 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 23 Dec 2014 22:02:21 -0600 Subject: [PATCH 03/17] Do not try and guess the mpl backend --- skimage/__init__.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/skimage/__init__.py b/skimage/__init__.py index f7339ba5..303ea34f 100644 --- a/skimage/__init__.py +++ b/skimage/__init__.py @@ -61,12 +61,6 @@ import imp as _imp import functools as _functools import warnings as _warnings -import matplotlib as _mpl -if 'Qt5Agg' in _mpl.rcsetup.interactive_bk: - _mpl.rcParams['backend'] = 'Qt5Agg' -else: - _mpl.rcParams['backend'] = 'Qt4Agg' - pkg_dir = _osp.abspath(_osp.dirname(__file__)) data_dir = _osp.join(pkg_dir, 'data') From 68508576f6a462653f1ad367a29aede384e31152 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 24 Dec 2014 06:06:01 -0600 Subject: [PATCH 04/17] Remove debug raise --- skimage/viewer/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/skimage/viewer/__init__.py b/skimage/viewer/__init__.py index 365e00a2..2ae55ed4 100644 --- a/skimage/viewer/__init__.py +++ b/skimage/viewer/__init__.py @@ -1,7 +1,6 @@ try: from .qt import QtGui as _QtGui except ImportError as e: - raise raise ImportError('Viewer requires Qt') from .viewers import ImageViewer, CollectionViewer From 8d40cbd1d2cf431454dcfd9a9088be73687e7c1a Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 24 Dec 2014 06:38:20 -0600 Subject: [PATCH 05/17] Allow viewer package to import without Qt --- skimage/viewer/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/skimage/viewer/__init__.py b/skimage/viewer/__init__.py index 2ae55ed4..8c2c33cf 100644 --- a/skimage/viewer/__init__.py +++ b/skimage/viewer/__init__.py @@ -1,6 +1,5 @@ +import warnings try: - from .qt import QtGui as _QtGui + from .viewers import ImageViewer, CollectionViewer except ImportError as e: - raise ImportError('Viewer requires Qt') - -from .viewers import ImageViewer, CollectionViewer + warnings.warn('Viewer requires Qt') From 6f57873526741458a343b7d9a131f568d3e79e0a Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 27 Dec 2014 10:54:13 -0600 Subject: [PATCH 06/17] Do not use ImportError behaviour in viewer Conflicts: skimage/viewer/tests/test_tools.py --- skimage/viewer/__init__.py | 7 +++-- skimage/viewer/qt.py | 21 ++++++++++++-- skimage/viewer/tests/test_plugins.py | 35 +++++++++++----------- skimage/viewer/tests/test_tools.py | 43 ++++------------------------ skimage/viewer/tests/test_utils.py | 17 +++++------ skimage/viewer/tests/test_viewer.py | 17 ++++++----- skimage/viewer/tests/test_widgets.py | 29 +++++++++---------- 7 files changed, 74 insertions(+), 95 deletions(-) diff --git a/skimage/viewer/__init__.py b/skimage/viewer/__init__.py index 8c2c33cf..443e5659 100644 --- a/skimage/viewer/__init__.py +++ b/skimage/viewer/__init__.py @@ -1,5 +1,6 @@ import warnings -try: - from .viewers import ImageViewer, CollectionViewer -except ImportError as e: +from .viewers import ImageViewer, CollectionViewer +from .qt import has_qt + +if not has_qt: warnings.warn('Viewer requires Qt') diff --git a/skimage/viewer/qt.py b/skimage/viewer/qt.py index bc827f9a..dc4852c9 100644 --- a/skimage/viewer/qt.py +++ b/skimage/viewer/qt.py @@ -1,8 +1,25 @@ +has_qt = True + try: from matplotlib.backends.qt_compat import QtGui, QtCore, QtWidgets except ImportError: - from matplotlib.backends.qt4_compat import QtGui, QtCore - QtWidgets = QtGui + try: + from matplotlib.backends.qt4_compat import QtGui, QtCore + QtWidgets = QtGui + except ImportError: + # Mock objects + class QtGui(object): + QMainWindow = object + QDialog = object + QWidget = object + + class QtCore(object): + Signal = object + Qt = object + + QtWidgets = QtGui + + has_qt = False Qt = QtCore.Qt Signal = QtCore.Signal diff --git a/skimage/viewer/tests/test_plugins.py b/skimage/viewer/tests/test_plugins.py index b5cf7ab9..699578f2 100644 --- a/skimage/viewer/tests/test_plugins.py +++ b/skimage/viewer/tests/test_plugins.py @@ -4,15 +4,12 @@ import skimage import skimage.data as data from skimage.filters.rank import median from skimage.morphology import disk -try: - from skimage.viewer import ImageViewer - from skimage.viewer.plugins.base import Plugin - from skimage.viewer.widgets import Slider - from skimage.viewer.plugins import ( - LineProfile, Measure, CannyPlugin, LabelPainter, Crop, ColorHistogram, - PlotPlugin) -except ImportError: - ImageViewer is None +from skimage.viewer import ImageViewer, has_qt +from skimage.viewer.plugins.base import Plugin +from skimage.viewer.widgets import Slider +from skimage.viewer.plugins import ( + LineProfile, Measure, CannyPlugin, LabelPainter, Crop, ColorHistogram, + PlotPlugin) from numpy.testing import assert_equal, assert_allclose, assert_almost_equal from numpy.testing.decorators import skipif <<<<<<< HEAD @@ -33,7 +30,7 @@ def setup_line_profile(image, limits='image'): return plugin -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_line_profile(): """ Test a line profile using an ndim=2 image""" plugin = setup_line_profile(data.camera()) @@ -47,7 +44,7 @@ def test_line_profile(): assert_allclose(scan_data.mean(), 0.2812, rtol=1e-3) -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_line_profile_rgb(): """ Test a line profile using an ndim=3 image""" plugin = setup_line_profile(data.chelsea(), limits=None) @@ -62,7 +59,7 @@ def test_line_profile_rgb(): assert_allclose(scan_data.mean(), 0.4359, rtol=1e-3) -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_line_profile_dynamic(): """Test a line profile updating after an image transform""" image = data.coins()[:-50, :] # shave some off to make the line lower @@ -88,7 +85,7 @@ def test_line_profile_dynamic(): assert_almost_equal(np.max(line) - np.min(line), 0.639, 1) -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_measure(): image = data.camera() viewer = ImageViewer(image) @@ -100,7 +97,7 @@ def test_measure(): assert_equal(str(m._angle.text[:5]), '135.0') -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_canny(): image = data.camera() viewer = ImageViewer(image) @@ -113,7 +110,7 @@ def test_canny(): assert edges.sum() == 2852 -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_label_painter(): image = data.camera() moon = data.moon() @@ -131,7 +128,7 @@ def test_label_painter(): assert_equal(lp.paint_tool.shape, moon.shape) -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_crop(): image = data.camera() viewer = ImageViewer(image) @@ -142,7 +139,7 @@ def test_crop(): assert_equal(viewer.image.shape, (101, 101)) -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_color_histogram(): image = skimage.img_as_float(data.load('color.png')) viewer = ImageViewer(image) @@ -154,7 +151,7 @@ def test_color_histogram(): assert_almost_equal(viewer.image.std(), 0.325, 3) -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_plot_plugin(): viewer = ImageViewer(data.moon()) plugin = PlotPlugin(image_filter=lambda x: x) @@ -166,7 +163,7 @@ def test_plot_plugin(): viewer.close() -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_plugin(): img = skimage.img_as_float(data.moon()) viewer = ImageViewer(img) diff --git a/skimage/viewer/tests/test_tools.py b/skimage/viewer/tests/test_tools.py index 6ea25940..aae44783 100644 --- a/skimage/viewer/tests/test_tools.py +++ b/skimage/viewer/tests/test_tools.py @@ -4,23 +4,12 @@ import numpy as np from numpy.testing import assert_equal from numpy.testing.decorators import skipif from skimage import data -<<<<<<< HEAD -from skimage.viewer import ImageViewer, viewer_available +from skimage.viewer import ImageViewer, has_qt from skimage.viewer.canvastools import ( LineTool, ThickLineTool, RectangleTool, PaintTool) from skimage.viewer.canvastools.base import CanvasToolBase from matplotlib.testing.decorators import cleanup -======= -try: - from skimage.viewer import ImageViewer - from skimage.viewer.canvastools import ( - LineTool, ThickLineTool, RectangleTool, PaintTool) - from skimage.viewer.canvastools.base import CanvasToolBase -except ImportError: - ImageViewer = None ->>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility - def get_end_points(image): h, w = image.shape[0:2] @@ -84,12 +73,8 @@ def do_event(viewer, etype, button=1, xdata=0, ydata=0, key=None): func(event) -<<<<<<< HEAD @cleanup -@skipif(not viewer_available) -======= -@skipif(ImageViewer is None) ->>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility +@skipif(not has_qt) def test_line_tool(): img = data.camera() viewer = ImageViewer(img) @@ -114,12 +99,8 @@ def test_line_tool(): assert_equal(tool.geometry, np.array([[100, 100], [10, 10]])) -<<<<<<< HEAD @cleanup -@skipif(not viewer_available) -======= -@skipif(ImageViewer is None) ->>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility +@skipif(not has_qt) def test_thick_line_tool(): img = data.camera() viewer = ImageViewer(img) @@ -142,12 +123,8 @@ def test_thick_line_tool(): assert_equal(tool.linewidth, 1) -<<<<<<< HEAD @cleanup -@skipif(not viewer_available) -======= -@skipif(ImageViewer is None) ->>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility +@skipif(not has_qt) def test_rect_tool(): img = data.camera() viewer = ImageViewer(img) @@ -175,12 +152,8 @@ def test_rect_tool(): assert_equal(tool.geometry, [10, 100, 10, 100]) -<<<<<<< HEAD @cleanup -@skipif(not viewer_available) -======= -@skipif(ImageViewer is None) ->>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility +@skipif(not has_qt) def test_paint_tool(): img = data.moon() viewer = ImageViewer(img) @@ -213,12 +186,8 @@ def test_paint_tool(): assert_equal(tool.overlay.sum(), 0) -<<<<<<< HEAD @cleanup -@skipif(not viewer_available) -======= -@skipif(ImageViewer is None) ->>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility +@skipif(not has_qt) def test_base_tool(): img = data.moon() viewer = ImageViewer(img) diff --git a/skimage/viewer/tests/test_utils.py b/skimage/viewer/tests/test_utils.py index 5c962e5c..ae158a90 100644 --- a/skimage/viewer/tests/test_utils.py +++ b/skimage/viewer/tests/test_utils.py @@ -1,14 +1,11 @@ # -*- coding: utf-8 -*- -try: - from skimage.viewer import utils - from skimage.viewer.utils import dialogs - from skimage.viewer.qt import QtCore, QtGui -except ImportError: - QtCore = None +from skimage.viewer import utils +from skimage.viewer.utils import dialogs +from skimage.viewer.qt import QtCore, QtGui, has_qt from numpy.testing.decorators import skipif -@skipif(QtCore is None) +@skipif(not has_qt) def test_event_loop(): utils.init_qtapp() timer = QtCore.QTimer() @@ -16,7 +13,7 @@ def test_event_loop(): utils.start_qtapp() -@skipif(QtCore is None) +@skipif(not has_qt) def test_format_filename(): fname = dialogs._format_filename(('apple', 2)) assert fname == 'apple' @@ -24,7 +21,7 @@ def test_format_filename(): assert fname is None -@skipif(QtCore is None) +@skipif(not has_qt) def test_open_file_dialog(): utils.init_qtapp() timer = QtCore.QTimer() @@ -33,7 +30,7 @@ def test_open_file_dialog(): assert filename is None -@skipif(QtCore is None) +@skipif(not has_qt) def test_save_file_dialog(): utils.init_qtapp() timer = QtCore.QTimer() diff --git a/skimage/viewer/tests/test_viewer.py b/skimage/viewer/tests/test_viewer.py index 0709d18d..548565a4 100644 --- a/skimage/viewer/tests/test_viewer.py +++ b/skimage/viewer/tests/test_viewer.py @@ -1,11 +1,10 @@ from skimage import data -try: - from skimage.viewer.qt import QtGui, QtCore - from skimage.viewer import ImageViewer, CollectionViewer - from skimage.viewer.plugins import OverlayPlugin -except ImportError: - ImageViewer = None + +from skimage.viewer.qt import QtGui, QtCore, has_qt +from skimage.viewer import ImageViewer, CollectionViewer +from skimage.viewer.plugins import OverlayPlugin + from skimage.transform import pyramid_gaussian from skimage.filters import sobel from numpy.testing import assert_equal @@ -14,7 +13,7 @@ from skimage._shared.version_requirements import is_installed from skimage._shared._warnings import expected_warnings -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_viewer(): astro = data.astronaut() coins = data.coins() @@ -41,7 +40,7 @@ def make_key_event(key): QtCore.Qt.NoModifier) -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_collection_viewer(): img = data.astronaut() @@ -57,7 +56,7 @@ def test_collection_viewer(): view._format_coord(10, 10) -@skipif(ImageViewer is None) +@skipif(not has_qt) @skipif(not is_installed('matplotlib', '>=1.2')) def test_viewer_with_overlay(): img = data.coins() diff --git a/skimage/viewer/tests/test_widgets.py b/skimage/viewer/tests/test_widgets.py index 8e76d29b..66b86fa4 100644 --- a/skimage/viewer/tests/test_widgets.py +++ b/skimage/viewer/tests/test_widgets.py @@ -1,14 +1,13 @@ import os from skimage import data, img_as_float, io, img_as_uint -try: - from skimage.viewer import ImageViewer - from skimage.viewer.qt import QtGui, QtCore - from skimage.viewer.widgets import ( - Slider, OKCancelButtons, SaveButtons, ComboBox, CheckBox, Text) - from skimage.viewer.plugins.base import Plugin -except ImportError: - ImageViewer = None + +from skimage.viewer import ImageViewer +from skimage.viewer.qt import QtGui, QtCore, has_qt +from skimage.viewer.widgets import ( + Slider, OKCancelButtons, SaveButtons, ComboBox, CheckBox, Text) +from skimage.viewer.plugins.base import Plugin + from numpy.testing import assert_almost_equal, assert_equal from numpy.testing.decorators import skipif from skimage._shared._warnings import expected_warnings @@ -21,7 +20,7 @@ def get_image_viewer(): return viewer -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_check_box(): viewer = get_image_viewer() cb = CheckBox('hello', value=True, alignment='left') @@ -36,7 +35,7 @@ def test_check_box(): assert_equal(cb.val, False) -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_combo_box(): viewer = get_image_viewer() cb = ComboBox('hello', ('a', 'b', 'c')) @@ -49,7 +48,7 @@ def test_combo_box(): assert_equal(cb.index, 2) -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_text_widget(): viewer = get_image_viewer() txt = Text('hello', 'hello, world!') @@ -60,7 +59,7 @@ def test_text_widget(): assert_equal(str(txt.text), 'goodbye, world!') -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_slider_int(): viewer = get_image_viewer() sld = Slider('radius', 2, 10, value_type='int') @@ -74,7 +73,7 @@ def test_slider_int(): assert_equal(sld.val, 5) -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_slider_float(): viewer = get_image_viewer() sld = Slider('alpha', 2.1, 3.1, value=2.1, value_type='float', @@ -89,7 +88,7 @@ def test_slider_float(): assert_almost_equal(sld.val, 2.5, 2) -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_save_buttons(): viewer = get_image_viewer() sv = SaveButtons() @@ -117,7 +116,7 @@ def test_save_buttons(): os.remove(filename) -@skipif(ImageViewer is None) +@skipif(not has_qt) def test_ok_buttons(): viewer = get_image_viewer() ok = OKCancelButtons() From 2eee1faaffa52bcf5cd8cb83692c2e50b7c02b55 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 24 Dec 2014 11:23:46 -0600 Subject: [PATCH 07/17] Fix import error in viewer.utils.core --- skimage/viewer/utils/core.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/skimage/viewer/utils/core.py b/skimage/viewer/utils/core.py index dd81d2a7..4f8bfade 100644 --- a/skimage/viewer/utils/core.py +++ b/skimage/viewer/utils/core.py @@ -1,17 +1,21 @@ import warnings import numpy as np -from skimage.viewer.qt import QtGui +from skimage.viewer.qt import QtGui, has_qt import matplotlib as mpl from matplotlib.figure import Figure from matplotlib import _pylab_helpers from matplotlib.colors import LinearSegmentedColormap -from matplotlib.backends.backend_qt4 import FigureManagerQT -from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg -if 'agg' not in mpl.get_backend().lower(): - warnings.warn("Recommended matplotlib backend is `Agg` for full " - "skimage.viewer functionality.") +if has_qt: + from matplotlib.backends.backend_qt4 import FigureManagerQT + from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg + if 'agg' not in mpl.get_backend().lower(): + warnings.warn("Recommended matplotlib backend is `Agg` for full " + "skimage.viewer functionality.") +else: + FigureManagerQT = None + FigureCanvasQTAgg = None __all__ = ['init_qtapp', 'start_qtapp', 'RequiredAttr', 'figimage', From 11c6a9f86a6574a0a38cf26b9b0fa2c61a38e308 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 24 Dec 2014 17:12:00 -0600 Subject: [PATCH 08/17] Fix another viewer import --- skimage/viewer/utils/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skimage/viewer/utils/core.py b/skimage/viewer/utils/core.py index 4f8bfade..517370e1 100644 --- a/skimage/viewer/utils/core.py +++ b/skimage/viewer/utils/core.py @@ -14,8 +14,8 @@ if has_qt: warnings.warn("Recommended matplotlib backend is `Agg` for full " "skimage.viewer functionality.") else: - FigureManagerQT = None - FigureCanvasQTAgg = None + FigureManagerQT = object + FigureCanvasQTAgg = object __all__ = ['init_qtapp', 'start_qtapp', 'RequiredAttr', 'figimage', From 6d445776c91d1b6c77e40fdf405fe21146aa7025 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 24 Dec 2014 17:30:15 -0600 Subject: [PATCH 09/17] Fix the dummy Signal --- skimage/viewer/qt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/viewer/qt.py b/skimage/viewer/qt.py index dc4852c9..9a6537b3 100644 --- a/skimage/viewer/qt.py +++ b/skimage/viewer/qt.py @@ -14,7 +14,7 @@ except ImportError: QWidget = object class QtCore(object): - Signal = object + def Signal(*args, **kwargs): pass Qt = object QtWidgets = QtGui From 7474db1f0d5c1491f852cfea8217656fa866284f Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 24 Dec 2014 19:42:12 -0600 Subject: [PATCH 10/17] Another fix for dummy Signal --- skimage/viewer/qt.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/skimage/viewer/qt.py b/skimage/viewer/qt.py index 9a6537b3..b36c15dc 100644 --- a/skimage/viewer/qt.py +++ b/skimage/viewer/qt.py @@ -14,9 +14,11 @@ except ImportError: QWidget = object class QtCore(object): - def Signal(*args, **kwargs): pass Qt = object + def Signal(self, *args, **kwargs): + pass + QtWidgets = QtGui has_qt = False From c3ade4416febecb040513a901495f4b61cc08ba9 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 24 Dec 2014 20:55:11 -0600 Subject: [PATCH 11/17] Instantiate QtCore dummy class --- skimage/viewer/qt.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/skimage/viewer/qt.py b/skimage/viewer/qt.py index b36c15dc..1d26fbcf 100644 --- a/skimage/viewer/qt.py +++ b/skimage/viewer/qt.py @@ -13,12 +13,13 @@ except ImportError: QDialog = object QWidget = object - class QtCore(object): + class QtCore_cls(object): Qt = object def Signal(self, *args, **kwargs): pass + QtCore = QtCore_cls() QtWidgets = QtGui has_qt = False From 8da175d70eaa036909edc8bdac0311163a4ca12a Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 24 Dec 2014 21:20:59 -0600 Subject: [PATCH 12/17] Add dummy Qt properties --- skimage/viewer/qt.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/skimage/viewer/qt.py b/skimage/viewer/qt.py index 1d26fbcf..e3880f55 100644 --- a/skimage/viewer/qt.py +++ b/skimage/viewer/qt.py @@ -14,13 +14,16 @@ except ImportError: QWidget = object class QtCore_cls(object): - Qt = object + class Qt(object): + TopDockWidgetArea = None + BottomDockWidgetArea = None + LeftDockWidgetArea = None + RightDockWidgetArea = None def Signal(self, *args, **kwargs): pass - QtCore = QtCore_cls() - QtWidgets = QtGui + QtCore = QtWidgets = QtCore_cls() has_qt = False From 42b330e5629b25db45e7a0f3f08bdb21e608b106 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Wed, 24 Dec 2014 21:40:33 -0600 Subject: [PATCH 13/17] Add QWidget to the mock Qt --- skimage/viewer/qt.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/skimage/viewer/qt.py b/skimage/viewer/qt.py index e3880f55..1166b3b6 100644 --- a/skimage/viewer/qt.py +++ b/skimage/viewer/qt.py @@ -23,6 +23,8 @@ except ImportError: def Signal(self, *args, **kwargs): pass + QWidget = object + QtCore = QtWidgets = QtCore_cls() has_qt = False From 67fadc0ed846a95f6d603827313b555e98985959 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 25 Dec 2014 17:02:45 -0600 Subject: [PATCH 14/17] Fix mock Qt objects again --- skimage/viewer/qt.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/skimage/viewer/qt.py b/skimage/viewer/qt.py index 1166b3b6..240e794c 100644 --- a/skimage/viewer/qt.py +++ b/skimage/viewer/qt.py @@ -8,7 +8,7 @@ except ImportError: QtWidgets = QtGui except ImportError: # Mock objects - class QtGui(object): + class QtGui_cls(object): QMainWindow = object QDialog = object QWidget = object @@ -23,9 +23,8 @@ except ImportError: def Signal(self, *args, **kwargs): pass - QWidget = object - - QtCore = QtWidgets = QtCore_cls() + QtGui = QtWidgets = QtGui_cls() + QtCore = QtCore_cls() has_qt = False From 51ab675d3c6fd67c1f10d218ddc7d9bf04572cb0 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 27 Dec 2014 11:11:36 -0600 Subject: [PATCH 15/17] Fix lingering merge issue --- skimage/viewer/tests/test_plugins.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/skimage/viewer/tests/test_plugins.py b/skimage/viewer/tests/test_plugins.py index 699578f2..8dcf7855 100644 --- a/skimage/viewer/tests/test_plugins.py +++ b/skimage/viewer/tests/test_plugins.py @@ -12,15 +12,7 @@ from skimage.viewer.plugins import ( PlotPlugin) from numpy.testing import assert_equal, assert_allclose, assert_almost_equal from numpy.testing.decorators import skipif -<<<<<<< HEAD -from skimage.viewer.plugins import ( - LineProfile, Measure, CannyPlugin, LabelPainter, Crop, ColorHistogram, - PlotPlugin) -from skimage.viewer.plugins.base import Plugin -from skimage.viewer.widgets import Slider from skimage._shared._warnings import expected_warnings -======= ->>>>>>> 7e2fdf7... Update viewer for PyQt5 compatibility def setup_line_profile(image, limits='image'): From 14a2b5d8f3f021219ef3ea09c90eeda78f539476 Mon Sep 17 00:00:00 2001 From: Phil Schaf Date: Tue, 30 Dec 2014 14:20:21 +0100 Subject: [PATCH 16/17] fixed Qt5 compat --- skimage/viewer/qt.py | 16 +++++++++++++--- skimage/viewer/utils/core.py | 22 ++++++++-------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/skimage/viewer/qt.py b/skimage/viewer/qt.py index 240e794c..0f6f5616 100644 --- a/skimage/viewer/qt.py +++ b/skimage/viewer/qt.py @@ -1,11 +1,10 @@ -has_qt = True - try: - from matplotlib.backends.qt_compat import QtGui, QtCore, QtWidgets + from matplotlib.backends.qt_compat import QtGui, QtCore, QtWidgets, QT_RC_MAJOR_VERSION as has_qt except ImportError: try: from matplotlib.backends.qt4_compat import QtGui, QtCore QtWidgets = QtGui + has_qt = 4 except ImportError: # Mock objects class QtGui_cls(object): @@ -28,5 +27,16 @@ except ImportError: has_qt = False +if has_qt == 5: + from matplotlib.backends.backend_qt5 import FigureManagerQT + from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg +elif has_qt == 4: + from matplotlib.backends.backend_qt4 import FigureManagerQT + from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg +else: + assert not has_qt, 'Unsupported Qt version {0}'.format(has_qt) + FigureManagerQT = object + FigureCanvasQTAgg = object + Qt = QtCore.Qt Signal = QtCore.Signal diff --git a/skimage/viewer/utils/core.py b/skimage/viewer/utils/core.py index 517370e1..524e9b61 100644 --- a/skimage/viewer/utils/core.py +++ b/skimage/viewer/utils/core.py @@ -1,21 +1,15 @@ import warnings import numpy as np -from skimage.viewer.qt import QtGui, has_qt +from skimage.viewer.qt import QtWidgets, has_qt, FigureManagerQT, FigureCanvasQTAgg import matplotlib as mpl from matplotlib.figure import Figure from matplotlib import _pylab_helpers from matplotlib.colors import LinearSegmentedColormap -if has_qt: - from matplotlib.backends.backend_qt4 import FigureManagerQT - from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg - if 'agg' not in mpl.get_backend().lower(): - warnings.warn("Recommended matplotlib backend is `Agg` for full " - "skimage.viewer functionality.") -else: - FigureManagerQT = object - FigureCanvasQTAgg = object +if has_qt and 'agg' not in mpl.get_backend().lower(): + warnings.warn("Recommended matplotlib backend is `Agg` for full " + "skimage.viewer functionality.") __all__ = ['init_qtapp', 'start_qtapp', 'RequiredAttr', 'figimage', @@ -32,9 +26,9 @@ def init_qtapp(): The QApplication needs to be initialized before creating any QWidgets """ global QApp - QApp = QtGui.QApplication.instance() + QApp = QtWidgets.QApplication.instance() if QApp is None: - QApp = QtGui.QApplication([]) + QApp = QtWidgets.QApplication([]) return QApp @@ -121,8 +115,8 @@ class FigureCanvas(FigureCanvasQTAgg): self.fig = figure FigureCanvasQTAgg.__init__(self, self.fig) FigureCanvasQTAgg.setSizePolicy(self, - QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Expanding) + QtWidgets.QSizePolicy.Expanding, + QtWidgets.QSizePolicy.Expanding) FigureCanvasQTAgg.updateGeometry(self) def resizeEvent(self, event): From d714d0bcbc5ae6928b362c16f8482ec0def5609d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 30 Dec 2014 21:01:23 -0600 Subject: [PATCH 17/17] Separate qt version check from has_qt flag --- skimage/viewer/qt.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/skimage/viewer/qt.py b/skimage/viewer/qt.py index 0f6f5616..8f6768a5 100644 --- a/skimage/viewer/qt.py +++ b/skimage/viewer/qt.py @@ -1,10 +1,13 @@ +_qt_version = None +has_qt = True + try: - from matplotlib.backends.qt_compat import QtGui, QtCore, QtWidgets, QT_RC_MAJOR_VERSION as has_qt + from matplotlib.backends.qt_compat import QtGui, QtCore, QtWidgets, QT_RC_MAJOR_VERSION as _qt_version except ImportError: try: from matplotlib.backends.qt4_compat import QtGui, QtCore QtWidgets = QtGui - has_qt = 4 + _qt_version = 4 except ImportError: # Mock objects class QtGui_cls(object): @@ -27,14 +30,13 @@ except ImportError: has_qt = False -if has_qt == 5: +if _qt_version == 5: from matplotlib.backends.backend_qt5 import FigureManagerQT from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg -elif has_qt == 4: +elif _qt_version == 4: from matplotlib.backends.backend_qt4 import FigureManagerQT from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg else: - assert not has_qt, 'Unsupported Qt version {0}'.format(has_qt) FigureManagerQT = object FigureCanvasQTAgg = object