Repository: silx-kit/silx
Issue: [image dialog] Not possible to select an image from a cube of 1xWxH

As the third dimension is 1, the slider is not displayed, and then the URL is not generated with the needed slicing.

Changed files:
silx/gui/dialog/ImageFileDialog.py, silx/gui/dialog/test/test_imagefiledialog.py, silx/gui/dialog/test/test_datafiledialog.py, silx/gui/dialog/AbstractDataFileDialog.py

diff --git a/silx/gui/dialog/AbstractDataFileDialog.py b/silx/gui/dialog/AbstractDataFileDialog.py
index c660cd708..e79519d34 100644
--- a/silx/gui/dialog/AbstractDataFileDialog.py
+++ b/silx/gui/dialog/AbstractDataFileDialog.py
@@ -28,7 +28,7 @@ This module contains an :class:`AbstractDataFileDialog`.
 
 __authors__ = ["V. Valls"]
 __license__ = "MIT"
-__date__ = "03/12/2018"
+__date__ = "05/03/2019"
 
 
 import sys
@@ -468,9 +468,13 @@ class _FabioData(object):
     def shape(self):
         if self.__fabioFile.nframes == 0:
             return None
+        if self.__fabioFile.nframes == 1:
+            return [slice(None), slice(None)]
         return [self.__fabioFile.nframes, slice(None), slice(None)]
 
     def __getitem__(self, selector):
+        if self.__fabioFile.nframes == 1 and selector == tuple():
+            return self.__fabioFile.data
         if isinstance(selector, tuple) and len(selector) == 1:
             selector = selector[0]
 
@@ -542,6 +546,10 @@ class AbstractDataFileDialog(qt.QDialog):
     def _init(self):
         self.setWindowTitle("Open")
 
+        self.__openedFiles = []
+        """Store the list of files opened by the model itself."""
+        # FIXME: It should be managed one by one by Hdf5Item itself
+
         self.__directory = None
         self.__directoryLoadedFilter = None
         self.__errorWhileLoadingFile = None
@@ -591,10 +599,6 @@ class AbstractDataFileDialog(qt.QDialog):
         self.__fileTypeCombo.setCurrentIndex(0)
         self.__filterSelected(0)
 
-        self.__openedFiles = []
-        """Store the list of files opened by the model itself."""
-        # FIXME: It should be managed one by one by Hdf5Item itself
-
         # It is not possible to override the QObject destructor nor
         # to access to the content of the Python object with the `destroyed`
         # signal cause the Python method was already removed with the QWidget,
@@ -1040,13 +1044,12 @@ class AbstractDataFileDialog(qt.QDialog):
         self.__processing += 1
         index = self.__fileModel.setRootPath(path)
         if not index.isValid():
+            # There is a problem with this path
+            # No asynchronous process will be waked up
             self.__processing -= 1
             self.__browser.setRootIndex(index, model=self.__fileModel)
             self.__clearData()
             self.__updatePath()
-        else:
-            # asynchronous process
-            pass
 
     def __directoryLoaded(self, path):
         if self.__directoryLoadedFilter is not None:
@@ -1233,6 +1236,7 @@ class AbstractDataFileDialog(qt.QDialog):
         if self.__previewWidget is not None:
             self.__previewWidget.setData(None)
         if self.__selectorWidget is not None:
+            self.__selectorWidget.setData(None)
             self.__selectorWidget.hide()
         self.__selectedData = None
         self.__data = None
@@ -1250,6 +1254,8 @@ class AbstractDataFileDialog(qt.QDialog):
         If :meth:`_isDataSupported` returns false, this function will be
         inhibited and no data will be selected.
         """
+        if isinstance(data, _FabioData):
+            data = data[()]
         if self.__previewWidget is not None:
             fromDataSelector = self.__selectedData is not None
             self.__previewWidget.setData(data, fromDataSelector=fromDataSelector)
@@ -1317,8 +1323,10 @@ class AbstractDataFileDialog(qt.QDialog):
             filename = ""
             dataPath = None
 
-        if useSelectorWidget and self.__selectorWidget is not None and self.__selectorWidget.isVisible():
+        if useSelectorWidget and self.__selectorWidget is not None and self.__selectorWidget.isUsed():
             slicing = self.__selectorWidget.slicing()
+            if slicing == tuple():
+                slicing = None
         else:
             slicing = None
 
@@ -1483,9 +1491,7 @@ class AbstractDataFileDialog(qt.QDialog):
                     self.__clearData()
 
                 if self.__selectorWidget is not None:
-                    self.__selectorWidget.setVisible(url.data_slice() is not None)
-                    if url.data_slice() is not None:
-                        self.__selectorWidget.setSlicing(url.data_slice())
+                    self.__selectorWidget.selectSlicing(url.data_slice())
             else:
                 self.__errorWhileLoadingFile = (url.file_path(), "File not found")
                 self.__clearData()
diff --git a/silx/gui/dialog/ImageFileDialog.py b/silx/gui/dialog/ImageFileDialog.py
index ef6b47253..d015bd2d0 100644
--- a/silx/gui/dialog/ImageFileDialog.py
+++ b/silx/gui/dialog/ImageFileDialog.py
@@ -28,7 +28,7 @@ This module contains an :class:`ImageFileDialog`.
 
 __authors__ = ["V. Valls"]
 __license__ = "MIT"
-__date__ = "12/02/2018"
+__date__ = "05/03/2019"
 
 import logging
 from silx.gui.plot import actions
@@ -36,7 +36,6 @@ from silx.gui import qt
 from silx.gui.plot.PlotWidget import PlotWidget
 from .AbstractDataFileDialog import AbstractDataFileDialog
 import silx.io
-import fabio
 
 
 _logger = logging.getLogger(__name__)
@@ -61,7 +60,7 @@ class _ImageSelection(qt.QWidget):
 
     def isUsed(self):
         if self.__shape is None:
-            return None
+            return False
         return len(self.__shape) > 2
 
     def getSelectedData(self, data):
@@ -70,6 +69,10 @@ class _ImageSelection(qt.QWidget):
         return image
 
     def setData(self, data):
+        if data is None:
+            self.__visibleSliders = 0
+            return
+
         shape = data.shape
         if self.__shape is not None:
             # clean up
@@ -114,6 +117,22 @@ class _ImageSelection(qt.QWidget):
                 break
             self.__axis[i].setValue(value)
 
+    def selectSlicing(self, slicing):
+        """Select a slicing.
+
+        The provided value could be unconsistent and therefore is not supposed
+        to be retrivable with a getter.
+
+        :param Union[None,Tuple[int]] slicing:
+        """
+        if slicing is None:
+            # Create a default slicing
+            needed = self.__visibleSliders
+            slicing = (0,) * needed
+        if len(slicing) < self.__visibleSliders:
+            slicing = slicing + (0,) * (self.__visibleSliders - len(slicing))
+        self.setSlicing(slicing)
+
 
 class _ImagePreview(qt.QWidget):
     """Provide a preview of the selected image"""
diff --git a/silx/gui/dialog/test/test_datafiledialog.py b/silx/gui/dialog/test/test_datafiledialog.py
index 06f89610f..b60ea12b1 100644
--- a/silx/gui/dialog/test/test_datafiledialog.py
+++ b/silx/gui/dialog/test/test_datafiledialog.py
@@ -26,7 +26,7 @@
 
 __authors__ = ["V. Valls"]
 __license__ = "MIT"
-__date__ = "05/10/2018"
+__date__ = "08/03/2019"
 
 
 import unittest
@@ -130,7 +130,7 @@ class _UtilsMixin(object):
         path2_ = os.path.normcase(path2)
         if path1_ == path2_:
             # Use the unittest API to log and display error
-            self.assertNotEquals(path1, path2)
+            self.assertNotEqual(path1, path2)
 
 
 class TestDataFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
@@ -385,7 +385,7 @@ class TestDataFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
         filename = _tmpDirectory + "/singleimage.edf"
         url = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/scan_0/instrument/detector_0/data")
         dialog.selectUrl(url.path())
-        self.assertTrue(dialog._selectedData().shape, (100, 100))
+        self.assertEqual(dialog._selectedData().shape, (100, 100))
         self.assertSamePath(dialog.selectedFile(), filename)
         self.assertSamePath(dialog.selectedUrl(), url.path())
 
@@ -399,7 +399,7 @@ class TestDataFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
         path = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/image").path()
         dialog.selectUrl(path)
         # test
-        self.assertTrue(dialog._selectedData().shape, (100, 100))
+        self.assertEqual(dialog._selectedData().shape, (100, 100))
         self.assertSamePath(dialog.selectedFile(), filename)
         self.assertSamePath(dialog.selectedUrl(), path)
 
@@ -479,11 +479,12 @@ class TestDataFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
         self.qWaitForPendingActions(dialog)
         browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
         filename = _tmpDirectory + "/badformat.h5"
-        index = browser.rootIndex().model().index(filename)
+        index = browser.model().index(filename)
+        browser.selectIndex(index)
         browser.activated.emit(index)
         self.qWaitForPendingActions(dialog)
         # test
-        self.assertTrue(dialog.selectedUrl(), filename)
+        self.assertSamePath(dialog.selectedUrl(), filename)
 
     def _countSelectableItems(self, model, rootIndex):
         selectable = 0
@@ -853,7 +854,7 @@ class TestDataFileDialogApi(testutils.TestCaseQt, _UtilsMixin):
         dialog2 = self.createDialog()
         result = dialog2.restoreState(state)
         self.assertTrue(result)
-        self.assertNotEquals(dialog2.directory(), directory)
+        self.assertNotEqual(dialog2.directory(), directory)
 
     def testHistory(self):
         dialog = self.createDialog()
diff --git a/silx/gui/dialog/test/test_imagefiledialog.py b/silx/gui/dialog/test/test_imagefiledialog.py
index 068dcb99b..c019afbc0 100644
--- a/silx/gui/dialog/test/test_imagefiledialog.py
+++ b/silx/gui/dialog/test/test_imagefiledialog.py
@@ -26,7 +26,7 @@
 
 __authors__ = ["V. Valls"]
 __license__ = "MIT"
-__date__ = "05/10/2018"
+__date__ = "08/03/2019"
 
 
 import unittest
@@ -70,24 +70,24 @@ def setUpModule():
     image.write(filename)
 
     filename = _tmpDirectory + "/data.h5"
-    f = h5py.File(filename, "w")
-    f["scalar"] = 10
-    f["image"] = data
-    f["cube"] = [data, data + 1, data + 2]
-    f["complex_image"] = data * 1j
-    f["group/image"] = data
-    f.close()
+    with h5py.File(filename, "w") as f:
+        f["scalar"] = 10
+        f["image"] = data
+        f["cube"] = [data, data + 1, data + 2]
+        f["single_frame"] = [data + 5]
+        f["complex_image"] = data * 1j
+        f["group/image"] = data
 
     directory = os.path.join(_tmpDirectory, "data")
     os.mkdir(directory)
     filename = os.path.join(directory, "data.h5")
-    f = h5py.File(filename, "w")
-    f["scalar"] = 10
-    f["image"] = data
-    f["cube"] = [data, data + 1, data + 2]
-    f["complex_image"] = data * 1j
-    f["group/image"] = data
-    f.close()
+    with h5py.File(filename, "w") as f:
+        f["scalar"] = 10
+        f["image"] = data
+        f["cube"] = [data, data + 1, data + 2]
+        f["single_frame"] = [data + 5]
+        f["complex_image"] = data * 1j
+        f["group/image"] = data
 
     filename = _tmpDirectory + "/badformat.edf"
     with io.open(filename, "wb") as f:
@@ -137,7 +137,7 @@ class _UtilsMixin(object):
         path2_ = os.path.normcase(path2)
         if path1_ == path2_:
             # Use the unittest API to log and display error
-            self.assertNotEquals(path1, path2)
+            self.assertNotEqual(path1, path2)
 
 
 class TestImageFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
@@ -373,7 +373,7 @@ class TestImageFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
         filename = _tmpDirectory + "/singleimage.edf"
         path = filename
         dialog.selectUrl(path)
-        self.assertTrue(dialog.selectedImage().shape, (100, 100))
+        self.assertEqual(dialog.selectedImage().shape, (100, 100))
         self.assertSamePath(dialog.selectedFile(), filename)
         path = silx.io.url.DataUrl(scheme="fabio", file_path=filename).path()
         self.assertSamePath(dialog.selectedUrl(), path)
@@ -396,7 +396,7 @@ class TestImageFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
         browser.activated.emit(index)
         self.qWaitForPendingActions(dialog)
         # test
-        self.assertTrue(dialog.selectedImage().shape, (100, 100))
+        self.assertEqual(dialog.selectedImage().shape, (100, 100))
         self.assertSamePath(dialog.selectedFile(), filename)
         self.assertSamePath(dialog.selectedUrl(), path)
 
@@ -411,8 +411,8 @@ class TestImageFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
         dialog.selectUrl(path)
         # test
         image = dialog.selectedImage()
-        self.assertTrue(image.shape, (100, 100))
-        self.assertTrue(image[0, 0], 1)
+        self.assertEqual(image.shape, (100, 100))
+        self.assertEqual(image[0, 0], 1)
         self.assertSamePath(dialog.selectedFile(), filename)
         self.assertSamePath(dialog.selectedUrl(), path)
 
@@ -426,7 +426,7 @@ class TestImageFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
         path = silx.io.url.DataUrl(scheme="fabio", file_path=filename).path()
         dialog.selectUrl(path)
         # test
-        self.assertTrue(dialog.selectedImage().shape, (100, 100))
+        self.assertEqual(dialog.selectedImage().shape, (100, 100))
         self.assertSamePath(dialog.selectedFile(), filename)
         self.assertSamePath(dialog.selectedUrl(), path)
 
@@ -440,7 +440,7 @@ class TestImageFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
         path = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/image").path()
         dialog.selectUrl(path)
         # test
-        self.assertTrue(dialog.selectedImage().shape, (100, 100))
+        self.assertEqual(dialog.selectedImage().shape, (100, 100))
         self.assertSamePath(dialog.selectedFile(), filename)
         self.assertSamePath(dialog.selectedUrl(), path)
 
@@ -474,8 +474,23 @@ class TestImageFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
         path = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/cube", data_slice=(1, )).path()
         dialog.selectUrl(path)
         # test
-        self.assertTrue(dialog.selectedImage().shape, (100, 100))
-        self.assertTrue(dialog.selectedImage()[0, 0], 1)
+        self.assertEqual(dialog.selectedImage().shape, (100, 100))
+        self.assertEqual(dialog.selectedImage()[0, 0], 1)
+        self.assertSamePath(dialog.selectedFile(), filename)
+        self.assertSamePath(dialog.selectedUrl(), path)
+
+    def testSelectSingleFrameFromH5(self):
+        dialog = self.createDialog()
+        dialog.show()
+        self.qWaitForWindowExposed(dialog)
+
+        # init state
+        filename = _tmpDirectory + "/data.h5"
+        path = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/single_frame", data_slice=(0, )).path()
+        dialog.selectUrl(path)
+        # test
+        self.assertEqual(dialog.selectedImage().shape, (100, 100))
+        self.assertEqual(dialog.selectedImage()[0, 0], 5)
         self.assertSamePath(dialog.selectedFile(), filename)
         self.assertSamePath(dialog.selectedUrl(), path)
 
@@ -489,11 +504,12 @@ class TestImageFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
         self.qWaitForPendingActions(dialog)
         browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
         filename = _tmpDirectory + "/badformat.edf"
-        index = browser.rootIndex().model().index(filename)
+        index = browser.model().index(filename)
+        browser.selectIndex(index)
         browser.activated.emit(index)
         self.qWaitForPendingActions(dialog)
         # test
-        self.assertTrue(dialog.selectedUrl(), filename)
+        self.assertSamePath(dialog.selectedUrl(), filename)
 
     def _countSelectableItems(self, model, rootIndex):
         selectable = 0
@@ -549,7 +565,7 @@ class TestImageFileDialogApi(testutils.TestCaseQt, _UtilsMixin):
         result = dialog2.restoreState(state)
         self.qWaitForPendingActions(dialog2)
         self.assertTrue(result)
-        self.assertTrue(dialog2.colormap().getNormalization(), "log")
+        self.assertEqual(dialog2.colormap().getNormalization(), "log")
 
     def printState(self):
         """
@@ -646,7 +662,7 @@ class TestImageFileDialogApi(testutils.TestCaseQt, _UtilsMixin):
         result = dialog.restoreState(state)
         self.assertTrue(result)
         colormap = dialog.colormap()
-        self.assertTrue(colormap.getNormalization(), "log")
+        self.assertEqual(colormap.getNormalization(), "log")
 
     def testRestoreRobusness(self):
         """What's happen if you try to open a config file with a different
@@ -672,7 +688,7 @@ class TestImageFileDialogApi(testutils.TestCaseQt, _UtilsMixin):
         dialog2 = self.createDialog()
         result = dialog2.restoreState(state)
         self.assertTrue(result)
-        self.assertNotEquals(dialog2.directory(), directory)
+        self.assertNotEqual(dialog2.directory(), directory)
 
     def testHistory(self):
         dialog = self.createDialog()