diff --git a/skimage/viewer/__init__.py b/skimage/viewer/__init__.py index cfbeb0c0..e3681339 100644 --- a/skimage/viewer/__init__.py +++ b/skimage/viewer/__init__.py @@ -1,4 +1 @@ -try: - from viewers import ImageViewer, CollectionViewer -except ImportError: - print("Could not import PyQt4 -- ImageViewer not available.") +from viewers import ImageViewer, CollectionViewer diff --git a/skimage/viewer/plugins/base.py b/skimage/viewer/plugins/base.py index 5adf986e..aa6b585a 100644 --- a/skimage/viewer/plugins/base.py +++ b/skimage/viewer/plugins/base.py @@ -1,18 +1,13 @@ """ Base class for Plugins that interact with ImageViewer. """ -try: - from PyQt4 import QtGui - from PyQt4.QtCore import Qt - from PyQt4.QtGui import QDialog -except ImportError: - QDialog = object # hack to prevent nosetest and autodoc errors - print("Could not import PyQt4 -- skimage.viewer not available.") +from ..qt import QtGui +from ..qt.QtCore import Qt from ..utils import RequiredAttr, init_qtapp -class Plugin(QDialog): +class Plugin(QtGui.QDialog): """Base class for plugins that interact with an ImageViewer. A plugin connects an image filter (or another function) to an image viewer. diff --git a/skimage/viewer/plugins/plotplugin.py b/skimage/viewer/plugins/plotplugin.py index 850d0b71..c120756c 100644 --- a/skimage/viewer/plugins/plotplugin.py +++ b/skimage/viewer/plugins/plotplugin.py @@ -1,9 +1,5 @@ import numpy as np - -try: - from PyQt4 import QtGui -except ImportError: - print("Could not import PyQt4 -- skimage.viewer not available.") +from ..qt import QtGui from ..utils import new_plot from .base import Plugin diff --git a/skimage/viewer/qt/QtCore.py b/skimage/viewer/qt/QtCore.py new file mode 100644 index 00000000..897b8760 --- /dev/null +++ b/skimage/viewer/qt/QtCore.py @@ -0,0 +1,9 @@ +from . import qt_api + +if qt_api == 'pyside': + from PySide.QtCore import * +elif qt_api == 'pyqt': + from PyQt4.QtCore import * +else: + # Mock objects + Qt = None diff --git a/skimage/viewer/qt/QtGui.py b/skimage/viewer/qt/QtGui.py new file mode 100644 index 00000000..12e9837f --- /dev/null +++ b/skimage/viewer/qt/QtGui.py @@ -0,0 +1,11 @@ +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 new file mode 100644 index 00000000..993ae1a5 --- /dev/null +++ b/skimage/viewer/qt/README.rst @@ -0,0 +1,5 @@ +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 new file mode 100644 index 00000000..55cafcfe --- /dev/null +++ b/skimage/viewer/qt/__init__.py @@ -0,0 +1,19 @@ +import os +import warnings + +qt_api = os.environ.get('QT_API') + +if qt_api is None: + try: + import PySide + qt_api = 'pyside' + except ImportError: + try: + import PyQt4 + qt_api = 'pyqt' + 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!") + os.environ['QT_API'] = qt_api diff --git a/skimage/viewer/utils/core.py b/skimage/viewer/utils/core.py index 38bf7c6c..0dcd4176 100644 --- a/skimage/viewer/utils/core.py +++ b/skimage/viewer/utils/core.py @@ -14,10 +14,7 @@ except ImportError: LinearSegmentedColormap = object print("Could not import matplotlib -- skimage.viewer not available.") -try: - from PyQt4 import QtGui -except ImportError: - print("Could not import PyQt4 -- skimage.viewer not available.") +from ..qt import QtGui __all__ = ['init_qtapp', 'start_qtapp', 'RequiredAttr', 'figimage', diff --git a/skimage/viewer/utils/dialogs.py b/skimage/viewer/utils/dialogs.py index 849fcad7..f160531d 100644 --- a/skimage/viewer/utils/dialogs.py +++ b/skimage/viewer/utils/dialogs.py @@ -1,23 +1,32 @@ import os -try: - from PyQt4 import QtGui -except ImportError: - print("Could not import PyQt4 -- skimage.viewer not available.") +from ..qt import QtGui -def open_file_dialog(default_format='png'): - """Return user-selected file path.""" - filename = str(QtGui.QFileDialog.getOpenFileName()) +__all__ = ['open_file_dialog', 'save_file_dialog'] + + +def _format_filename(filename): + if isinstance(filename, tuple): + # Handle discrepancy between PyQt4 and PySide APIs. + filename = filename[0] if len(filename) == 0: return None + return str(filename) + + +def open_file_dialog(): + """Return user-selected file path.""" + filename = QtGui.QFileDialog.getOpenFileName() + filename = _format_filename(filename) return filename def save_file_dialog(default_format='png'): """Return user-selected file path.""" - filename = str(QtGui.QFileDialog.getSaveFileName()) - if len(filename) == 0: + filename = QtGui.QFileDialog.getSaveFileName() + filename = _format_filename(filename) + if filename is None: return None #TODO: io plugins should assign default image formats basename, ext = os.path.splitext(filename) diff --git a/skimage/viewer/viewers/core.py b/skimage/viewer/viewers/core.py index 193c5114..9d6261c6 100644 --- a/skimage/viewer/viewers/core.py +++ b/skimage/viewer/viewers/core.py @@ -1,12 +1,8 @@ """ ImageViewer class for viewing and interacting with images. """ -try: - from PyQt4 import QtGui, QtCore - from PyQt4.QtGui import QMainWindow -except ImportError: - QMainWindow = object # hack to prevent nosetest and autodoc errors - print("Could not import PyQt4 -- skimage.viewer not available.") +from ..qt import QtGui +from ..qt import QtCore from skimage import io, img_as_float from skimage.util.dtype import dtype_range @@ -32,7 +28,7 @@ def mpl_image_to_rgba(mpl_image): return img_as_float(image) -class ImageViewer(QMainWindow): +class ImageViewer(QtGui.QMainWindow): """Viewer for displaying images. This viewer is a simple container object that holds a Matplotlib axes diff --git a/skimage/viewer/widgets/core.py b/skimage/viewer/widgets/core.py index 14259f1e..ad69bfcf 100644 --- a/skimage/viewer/widgets/core.py +++ b/skimage/viewer/widgets/core.py @@ -15,14 +15,9 @@ parameter type specified by its `ptype` attribute, which can be: property of the same name that updates the display. """ -try: - from PyQt4.QtCore import Qt - from PyQt4 import QtGui - from PyQt4 import QtCore - from PyQt4.QtGui import QWidget -except ImportError: - QWidget = object # hack to prevent nosetest and autodoc errors - print("Could not import PyQt4 -- skimage.viewer not available.") +from ..qt import QtGui +from ..qt import QtCore +from ..qt.QtCore import Qt from ..utils import RequiredAttr @@ -30,7 +25,7 @@ from ..utils import RequiredAttr __all__ = ['BaseWidget', 'Slider', 'ComboBox', 'Text'] -class BaseWidget(QWidget): +class BaseWidget(QtGui.QWidget): plugin = RequiredAttr("Widget is not attached to a Plugin.") @@ -165,9 +160,9 @@ class Slider(BaseWidget): self.editbox.setAlignment(align_value) self.editbox.editingFinished.connect(self._on_editbox_changed) - self.layout.addWidget(self.name_label, alignment=align_text) - self.layout.addWidget(self.slider, alignment=alignment) - self.layout.addWidget(self.editbox, alignment=align_value) + self.layout.addWidget(self.name_label) + self.layout.addWidget(self.slider) + self.layout.addWidget(self.editbox) def _on_slider_changed(self): """Call callback function with slider's name and value as parameters""" @@ -241,7 +236,7 @@ class ComboBox(BaseWidget): self.layout = QtGui.QHBoxLayout(self) self.layout.addWidget(self.name_label) - self.layout.addWidget(self._combo_box, alignment=QtCore.Qt.AlignLeft) + self.layout.addWidget(self._combo_box) self._combo_box.currentIndexChanged.connect(self._value_changed) # self.connect(self._combo_box, diff --git a/skimage/viewer/widgets/history.py b/skimage/viewer/widgets/history.py index e3aaa938..ce20b559 100644 --- a/skimage/viewer/widgets/history.py +++ b/skimage/viewer/widgets/history.py @@ -1,9 +1,7 @@ from textwrap import dedent -try: - from PyQt4 import QtGui, QtCore -except ImportError: - print("Could not import PyQt4 -- skimage.viewer not available.") +from ..qt import QtGui +from ..qt import QtCore import numpy as np