summaryrefslogtreecommitdiff
path: root/src/silx/gui/dialog/test/test_datafiledialog.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/silx/gui/dialog/test/test_datafiledialog.py')
-rw-r--r--src/silx/gui/dialog/test/test_datafiledialog.py990
1 files changed, 990 insertions, 0 deletions
diff --git a/src/silx/gui/dialog/test/test_datafiledialog.py b/src/silx/gui/dialog/test/test_datafiledialog.py
new file mode 100644
index 0000000..887ff1c
--- /dev/null
+++ b/src/silx/gui/dialog/test/test_datafiledialog.py
@@ -0,0 +1,990 @@
+# /*##########################################################################
+#
+# Copyright (c) 2016-2022 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""Test for silx.gui.hdf5 module"""
+
+__authors__ = ["V. Valls"]
+__license__ = "MIT"
+__date__ = "08/03/2019"
+
+
+import tempfile
+import numpy
+import shutil
+import os
+import io
+import weakref
+import fabio
+import h5py
+import silx.io.url
+from silx.gui import qt
+from silx.gui.utils import testutils
+from ..DataFileDialog import DataFileDialog
+from silx.gui.hdf5 import Hdf5TreeModel
+
+_tmpDirectory = None
+
+
+def setUpModule():
+ global _tmpDirectory
+ _tmpDirectory = tempfile.mkdtemp(prefix=__name__)
+
+ data = numpy.arange(100 * 100)
+ data.shape = 100, 100
+
+ filename = _tmpDirectory + "/singleimage.edf"
+ image = fabio.edfimage.EdfImage(data=data)
+ 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["nxdata/foo"] = 10
+ f["nxdata"].attrs["NX_class"] = "NXdata"
+ f.close()
+
+ 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["nxdata/foo"] = 10
+ f["nxdata"].attrs["NX_class"] = "NXdata"
+ f.close()
+
+ filename = _tmpDirectory + "/badformat.h5"
+ with io.open(filename, "wb") as f:
+ f.write(b"{\nHello Nurse!")
+
+
+def tearDownModule():
+ global _tmpDirectory
+ for _ in range(10):
+ try:
+ shutil.rmtree(_tmpDirectory)
+ except PermissionError: # Might fail on appveyor
+ testutils.TestCaseQt.qWait(500)
+ else:
+ break
+ _tmpDirectory = None
+
+
+class _UtilsMixin(object):
+ def createDialog(self):
+ self._deleteDialog()
+ self._dialog = self._createDialog()
+ return self._dialog
+
+ def _createDialog(self):
+ return DataFileDialog()
+
+ def _deleteDialog(self):
+ if not hasattr(self, "_dialog"):
+ return
+ if self._dialog is not None:
+ ref = weakref.ref(self._dialog)
+ self._dialog = None
+ self.qWaitForDestroy(ref)
+
+ def qWaitForPendingActions(self, dialog):
+ for _ in range(20):
+ if not dialog.hasPendingEvents():
+ return
+ self.qWait(10)
+ raise RuntimeError("Still have pending actions")
+
+ def assertSamePath(self, path1, path2):
+ path1_ = os.path.normcase(path1)
+ path2_ = os.path.normcase(path2)
+ if path1_ != path2_:
+ # Use the unittest API to log and display error
+ self.assertEqual(path1, path2)
+
+ def assertNotSamePath(self, path1, path2):
+ path1_ = os.path.normcase(path1)
+ path2_ = os.path.normcase(path2)
+ if path1_ == path2_:
+ # Use the unittest API to log and display error
+ self.assertNotEqual(path1, path2)
+
+
+class TestDataFileDialogInteraction(testutils.TestCaseQt, _UtilsMixin):
+ def tearDown(self):
+ self._deleteDialog()
+ testutils.TestCaseQt.tearDown(self)
+
+ def testDisplayAndKeyEscape(self):
+ dialog = self.createDialog()
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ self.assertTrue(dialog.isVisible())
+
+ self.keyClick(dialog, qt.Qt.Key_Escape)
+ self.assertFalse(dialog.isVisible())
+ self.assertEqual(dialog.result(), qt.QDialog.Rejected)
+
+ def testDisplayAndClickCancel(self):
+ dialog = self.createDialog()
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ self.assertTrue(dialog.isVisible())
+
+ button = testutils.findChildren(dialog, qt.QPushButton, name="cancel")[0]
+ self.mouseClick(button, qt.Qt.LeftButton)
+ self.assertFalse(dialog.isVisible())
+ self.assertFalse(dialog.isVisible())
+ self.assertEqual(dialog.result(), qt.QDialog.Rejected)
+
+ def testDisplayAndClickLockedOpen(self):
+ dialog = self.createDialog()
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ self.assertTrue(dialog.isVisible())
+
+ button = testutils.findChildren(dialog, qt.QPushButton, name="open")[0]
+ self.mouseClick(button, qt.Qt.LeftButton)
+ # open button locked, dialog is not closed
+ self.assertTrue(dialog.isVisible())
+ self.assertEqual(dialog.result(), qt.QDialog.Rejected)
+
+ def testSelectRoot_Activate(self):
+ dialog = self.createDialog()
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ self.assertTrue(dialog.isVisible())
+ filename = _tmpDirectory + "/data.h5"
+ dialog.selectFile(os.path.dirname(filename))
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = browser.rootIndex().model().index(filename)
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ button = testutils.findChildren(dialog, qt.QPushButton, name="open")[0]
+ self.assertTrue(button.isEnabled())
+ self.mouseClick(button, qt.Qt.LeftButton)
+ url = silx.io.url.DataUrl(dialog.selectedUrl())
+ self.assertTrue(url.data_path() is not None)
+ self.assertFalse(dialog.isVisible())
+ self.assertEqual(dialog.result(), qt.QDialog.Accepted)
+
+ def testSelectGroup_Activate(self):
+ dialog = self.createDialog()
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ self.assertTrue(dialog.isVisible())
+ filename = _tmpDirectory + "/data.h5"
+ dialog.selectFile(os.path.dirname(filename))
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = browser.rootIndex().model().index(filename)
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = (
+ browser.rootIndex()
+ .model()
+ .indexFromH5Object(dialog._AbstractDataFileDialog__h5["/group"])
+ )
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ button = testutils.findChildren(dialog, qt.QPushButton, name="open")[0]
+ self.assertTrue(button.isEnabled())
+ self.mouseClick(button, qt.Qt.LeftButton)
+ url = silx.io.url.DataUrl(dialog.selectedUrl())
+ self.assertEqual(url.data_path(), "/group")
+ self.assertFalse(dialog.isVisible())
+ self.assertEqual(dialog.result(), qt.QDialog.Accepted)
+
+ def testSelectDataset_Activate(self):
+ dialog = self.createDialog()
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ self.assertTrue(dialog.isVisible())
+ filename = _tmpDirectory + "/data.h5"
+ dialog.selectFile(os.path.dirname(filename))
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = browser.rootIndex().model().index(filename)
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = (
+ browser.rootIndex()
+ .model()
+ .indexFromH5Object(dialog._AbstractDataFileDialog__h5["/scalar"])
+ )
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ button = testutils.findChildren(dialog, qt.QPushButton, name="open")[0]
+ self.assertTrue(button.isEnabled())
+ self.mouseClick(button, qt.Qt.LeftButton)
+ url = silx.io.url.DataUrl(dialog.selectedUrl())
+ self.assertEqual(url.data_path(), "/scalar")
+ self.assertFalse(dialog.isVisible())
+ self.assertEqual(dialog.result(), qt.QDialog.Accepted)
+
+ def testClickOnBackToParentTool(self):
+ dialog = self.createDialog()
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+
+ url = testutils.findChildren(dialog, qt.QLineEdit, name="url")[0]
+ action = testutils.findChildren(dialog, qt.QAction, name="toParentAction")[0]
+ toParentButton = testutils.getQToolButtonFromAction(action)
+ filename = _tmpDirectory + "/data/data.h5"
+
+ # init state
+ path = silx.io.url.DataUrl(file_path=filename, data_path="/group/image").path()
+ dialog.selectUrl(path)
+ self.qWaitForPendingActions(dialog)
+ path = silx.io.url.DataUrl(
+ scheme="silx", file_path=filename, data_path="/group/image"
+ ).path()
+ self.assertSamePath(url.text(), path)
+ # test
+ self.mouseClick(toParentButton, qt.Qt.LeftButton)
+ self.qWaitForPendingActions(dialog)
+ path = silx.io.url.DataUrl(
+ scheme="silx", file_path=filename, data_path="/"
+ ).path()
+ self.assertSamePath(url.text(), path)
+
+ self.mouseClick(toParentButton, qt.Qt.LeftButton)
+ self.qWaitForPendingActions(dialog)
+ self.assertSamePath(url.text(), _tmpDirectory + "/data")
+
+ self.mouseClick(toParentButton, qt.Qt.LeftButton)
+ self.qWaitForPendingActions(dialog)
+ self.assertSamePath(url.text(), _tmpDirectory)
+
+ def testClickOnBackToRootTool(self):
+ dialog = self.createDialog()
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+
+ url = testutils.findChildren(dialog, qt.QLineEdit, name="url")[0]
+ action = testutils.findChildren(dialog, qt.QAction, name="toRootFileAction")[0]
+ button = testutils.getQToolButtonFromAction(action)
+ filename = _tmpDirectory + "/data.h5"
+
+ # init state
+ path = silx.io.url.DataUrl(
+ scheme="silx", file_path=filename, data_path="/group/image"
+ ).path()
+ dialog.selectUrl(path)
+ self.qWaitForPendingActions(dialog)
+ self.assertSamePath(url.text(), path)
+ self.assertTrue(button.isEnabled())
+ # test
+ self.mouseClick(button, qt.Qt.LeftButton)
+ self.qWaitForPendingActions(dialog)
+ path = silx.io.url.DataUrl(
+ scheme="silx", file_path=filename, data_path="/"
+ ).path()
+ self.assertSamePath(url.text(), path)
+ # self.assertFalse(button.isEnabled())
+
+ def testClickOnBackToDirectoryTool(self):
+ dialog = self.createDialog()
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+
+ url = testutils.findChildren(dialog, qt.QLineEdit, name="url")[0]
+ action = testutils.findChildren(dialog, qt.QAction, name="toDirectoryAction")[0]
+ button = testutils.getQToolButtonFromAction(action)
+ filename = _tmpDirectory + "/data.h5"
+
+ # init state
+ path = silx.io.url.DataUrl(file_path=filename, data_path="/group/image").path()
+ dialog.selectUrl(path)
+ self.qWaitForPendingActions(dialog)
+ path = silx.io.url.DataUrl(
+ scheme="silx", file_path=filename, data_path="/group/image"
+ ).path()
+ self.assertSamePath(url.text(), path)
+ self.assertTrue(button.isEnabled())
+ # test
+ self.mouseClick(button, qt.Qt.LeftButton)
+ self.qWaitForPendingActions(dialog)
+ self.assertSamePath(url.text(), _tmpDirectory)
+ self.assertFalse(button.isEnabled())
+
+ # FIXME: There is an unreleased qt.QWidget without nameObject
+ # No idea where it come from.
+ self.allowedLeakingWidgets = 1
+
+ def testClickOnHistoryTools(self):
+ dialog = self.createDialog()
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+
+ url = testutils.findChildren(dialog, qt.QLineEdit, name="url")[0]
+ forwardAction = testutils.findChildren(
+ dialog, qt.QAction, name="forwardAction"
+ )[0]
+ backwardAction = testutils.findChildren(
+ dialog, qt.QAction, name="backwardAction"
+ )[0]
+ filename = _tmpDirectory + "/data.h5"
+
+ dialog.setDirectory(_tmpDirectory)
+ self.qWaitForPendingActions(dialog)
+ # No way to use QTest.mouseDClick with QListView, QListWidget
+ # Then we feed the history using selectPath
+ dialog.selectUrl(filename)
+ self.qWaitForPendingActions(dialog)
+ path2 = silx.io.url.DataUrl(
+ scheme="silx", file_path=filename, data_path="/"
+ ).path()
+ dialog.selectUrl(path2)
+ self.qWaitForPendingActions(dialog)
+ path3 = silx.io.url.DataUrl(
+ scheme="silx", file_path=filename, data_path="/group"
+ ).path()
+ dialog.selectUrl(path3)
+ self.qWaitForPendingActions(dialog)
+ self.assertFalse(forwardAction.isEnabled())
+ self.assertTrue(backwardAction.isEnabled())
+
+ button = testutils.getQToolButtonFromAction(backwardAction)
+ self.mouseClick(button, qt.Qt.LeftButton)
+ self.qWaitForPendingActions(dialog)
+ self.assertTrue(forwardAction.isEnabled())
+ self.assertTrue(backwardAction.isEnabled())
+ self.assertSamePath(url.text(), path2)
+
+ button = testutils.getQToolButtonFromAction(forwardAction)
+ self.mouseClick(button, qt.Qt.LeftButton)
+ self.qWaitForPendingActions(dialog)
+ self.assertFalse(forwardAction.isEnabled())
+ self.assertTrue(backwardAction.isEnabled())
+ self.assertSamePath(url.text(), path3)
+
+ def testSelectImageFromEdf(self):
+ dialog = self.createDialog()
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+
+ # init state
+ 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.assertEqual(dialog._selectedData().shape, (100, 100))
+ self.assertSamePath(dialog.selectedFile(), filename)
+ self.assertSamePath(dialog.selectedUrl(), url.path())
+
+ def testSelectImage(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="/image"
+ ).path()
+ dialog.selectUrl(path)
+ # test
+ self.assertEqual(dialog._selectedData().shape, (100, 100))
+ self.assertSamePath(dialog.selectedFile(), filename)
+ self.assertSamePath(dialog.selectedUrl(), path)
+
+ def testSelectScalar(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="/scalar"
+ ).path()
+ dialog.selectUrl(path)
+ # test
+ self.assertEqual(dialog._selectedData()[()], 10)
+ self.assertSamePath(dialog.selectedFile(), filename)
+ self.assertSamePath(dialog.selectedUrl(), path)
+
+ def testSelectGroup(self):
+ dialog = self.createDialog()
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+
+ # init state
+ filename = _tmpDirectory + "/data.h5"
+ uri = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/group")
+ dialog.selectUrl(uri.path())
+ self.qWaitForPendingActions(dialog)
+ # test
+ self.assertTrue(silx.io.is_group(dialog._selectedData()))
+ self.assertSamePath(dialog.selectedFile(), filename)
+ uri = silx.io.url.DataUrl(dialog.selectedUrl())
+ self.assertSamePath(uri.data_path(), "/group")
+
+ def testSelectRoot(self):
+ dialog = self.createDialog()
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+
+ # init state
+ filename = _tmpDirectory + "/data.h5"
+ uri = silx.io.url.DataUrl(scheme="silx", file_path=filename, data_path="/")
+ dialog.selectUrl(uri.path())
+ self.qWaitForPendingActions(dialog)
+ # test
+ self.assertTrue(silx.io.is_file(dialog._selectedData()))
+ self.assertSamePath(dialog.selectedFile(), filename)
+ uri = silx.io.url.DataUrl(dialog.selectedUrl())
+ self.assertSamePath(uri.data_path(), "/")
+
+ def testSelectH5_Activate(self):
+ dialog = self.createDialog()
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+
+ # init state
+ dialog.selectUrl(_tmpDirectory)
+ self.qWaitForPendingActions(dialog)
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+ filename = _tmpDirectory + "/data.h5"
+ path = silx.io.url.DataUrl(
+ scheme="silx", file_path=filename, data_path="/"
+ ).path()
+ index = browser.rootIndex().model().index(filename)
+ # click
+ browser.selectIndex(index)
+ # double click
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+ # test
+ self.assertSamePath(dialog.selectedUrl(), path)
+
+ def testSelectBadFileFormat_Activate(self):
+ dialog = self.createDialog()
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+
+ # init state
+ dialog.selectUrl(_tmpDirectory)
+ self.qWaitForPendingActions(dialog)
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+ filename = _tmpDirectory + "/badformat.h5"
+ index = browser.model().index(filename)
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+ # test
+ self.assertSamePath(dialog.selectedUrl(), filename)
+
+ def _countSelectableItems(self, model, rootIndex):
+ selectable = 0
+ for i in range(model.rowCount(rootIndex)):
+ index = model.index(i, 0, rootIndex)
+ flags = model.flags(index)
+ isEnabled = flags & qt.Qt.ItemIsEnabled == qt.Qt.ItemIsEnabled
+ if isEnabled:
+ selectable += 1
+ return selectable
+
+ def testFilterExtensions(self):
+ dialog = self.createDialog()
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ dialog.selectUrl(_tmpDirectory)
+ self.qWaitForPendingActions(dialog)
+ self.assertEqual(
+ self._countSelectableItems(browser.model(), browser.rootIndex()), 4
+ )
+
+
+class TestDataFileDialog_FilterDataset(testutils.TestCaseQt, _UtilsMixin):
+ def tearDown(self):
+ self._deleteDialog()
+ testutils.TestCaseQt.tearDown(self)
+
+ def _createDialog(self):
+ dialog = DataFileDialog()
+ dialog.setFilterMode(DataFileDialog.FilterMode.ExistingDataset)
+ return dialog
+
+ def testSelectGroup_Activate(self):
+ dialog = self.createDialog()
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ self.assertTrue(dialog.isVisible())
+ filename = _tmpDirectory + "/data.h5"
+ dialog.selectFile(os.path.dirname(filename))
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = browser.rootIndex().model().index(filename)
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = (
+ browser.rootIndex()
+ .model()
+ .indexFromH5Object(dialog._AbstractDataFileDialog__h5["/group"])
+ )
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ button = testutils.findChildren(dialog, qt.QPushButton, name="open")[0]
+ self.assertFalse(button.isEnabled())
+
+ def testSelectDataset_Activate(self):
+ dialog = self.createDialog()
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ self.assertTrue(dialog.isVisible())
+ filename = _tmpDirectory + "/data.h5"
+ dialog.selectFile(os.path.dirname(filename))
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = browser.rootIndex().model().index(filename)
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = (
+ browser.rootIndex()
+ .model()
+ .indexFromH5Object(dialog._AbstractDataFileDialog__h5["/scalar"])
+ )
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ button = testutils.findChildren(dialog, qt.QPushButton, name="open")[0]
+ self.assertTrue(button.isEnabled())
+ self.mouseClick(button, qt.Qt.LeftButton)
+ url = silx.io.url.DataUrl(dialog.selectedUrl())
+ self.assertEqual(url.data_path(), "/scalar")
+ self.assertFalse(dialog.isVisible())
+ self.assertEqual(dialog.result(), qt.QDialog.Accepted)
+
+ data = dialog.selectedData()
+ self.assertEqual(data, 10)
+
+
+class TestDataFileDialog_FilterGroup(testutils.TestCaseQt, _UtilsMixin):
+ def tearDown(self):
+ self._deleteDialog()
+ testutils.TestCaseQt.tearDown(self)
+
+ def _createDialog(self):
+ dialog = DataFileDialog()
+ dialog.setFilterMode(DataFileDialog.FilterMode.ExistingGroup)
+ return dialog
+
+ def testSelectGroup_Activate(self):
+ dialog = self.createDialog()
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ self.assertTrue(dialog.isVisible())
+ filename = _tmpDirectory + "/data.h5"
+ dialog.selectFile(os.path.dirname(filename))
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = browser.rootIndex().model().index(filename)
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = (
+ browser.rootIndex()
+ .model()
+ .indexFromH5Object(dialog._AbstractDataFileDialog__h5["/group"])
+ )
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ button = testutils.findChildren(dialog, qt.QPushButton, name="open")[0]
+ self.assertTrue(button.isEnabled())
+ self.mouseClick(button, qt.Qt.LeftButton)
+ url = silx.io.url.DataUrl(dialog.selectedUrl())
+ self.assertEqual(url.data_path(), "/group")
+ self.assertFalse(dialog.isVisible())
+ self.assertEqual(dialog.result(), qt.QDialog.Accepted)
+
+ self.assertRaises(Exception, dialog.selectedData)
+
+ def testSelectDataset_Activate(self):
+ dialog = self.createDialog()
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ self.assertTrue(dialog.isVisible())
+ filename = _tmpDirectory + "/data.h5"
+ dialog.selectFile(os.path.dirname(filename))
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = browser.rootIndex().model().index(filename)
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = (
+ browser.rootIndex()
+ .model()
+ .indexFromH5Object(dialog._AbstractDataFileDialog__h5["/scalar"])
+ )
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ button = testutils.findChildren(dialog, qt.QPushButton, name="open")[0]
+ self.assertFalse(button.isEnabled())
+
+
+class TestDataFileDialog_FilterNXdata(testutils.TestCaseQt, _UtilsMixin):
+ def tearDown(self):
+ self._deleteDialog()
+ testutils.TestCaseQt.tearDown(self)
+
+ def _createDialog(self):
+ def customFilter(obj):
+ if "NX_class" in obj.attrs:
+ return obj.attrs["NX_class"] == "NXdata"
+ return False
+
+ dialog = DataFileDialog()
+ dialog.setFilterMode(DataFileDialog.FilterMode.ExistingGroup)
+ dialog.setFilterCallback(customFilter)
+ return dialog
+
+ def testSelectGroupRefused_Activate(self):
+ dialog = self.createDialog()
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ self.assertTrue(dialog.isVisible())
+ filename = _tmpDirectory + "/data.h5"
+ dialog.selectFile(os.path.dirname(filename))
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = browser.rootIndex().model().index(filename)
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = (
+ browser.rootIndex()
+ .model()
+ .indexFromH5Object(dialog._AbstractDataFileDialog__h5["/group"])
+ )
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ button = testutils.findChildren(dialog, qt.QPushButton, name="open")[0]
+ self.assertFalse(button.isEnabled())
+
+ self.assertRaises(Exception, dialog.selectedData)
+
+ def testSelectNXdataAccepted_Activate(self):
+ dialog = self.createDialog()
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+ dialog.show()
+ self.qWaitForWindowExposed(dialog)
+ self.assertTrue(dialog.isVisible())
+ filename = _tmpDirectory + "/data.h5"
+ dialog.selectFile(os.path.dirname(filename))
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = browser.rootIndex().model().index(filename)
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ # select, then double click on the file
+ index = (
+ browser.rootIndex()
+ .model()
+ .indexFromH5Object(dialog._AbstractDataFileDialog__h5["/nxdata"])
+ )
+ browser.selectIndex(index)
+ browser.activated.emit(index)
+ self.qWaitForPendingActions(dialog)
+
+ button = testutils.findChildren(dialog, qt.QPushButton, name="open")[0]
+ self.assertTrue(button.isEnabled())
+ self.mouseClick(button, qt.Qt.LeftButton)
+ url = silx.io.url.DataUrl(dialog.selectedUrl())
+ self.assertEqual(url.data_path(), "/nxdata")
+ self.assertFalse(dialog.isVisible())
+ self.assertEqual(dialog.result(), qt.QDialog.Accepted)
+
+
+class TestDataFileDialogApi(testutils.TestCaseQt, _UtilsMixin):
+ def tearDown(self):
+ self._deleteDialog()
+ testutils.TestCaseQt.tearDown(self)
+
+ def _createDialog(self):
+ dialog = DataFileDialog()
+ return dialog
+
+ def testSaveRestoreState(self):
+ dialog = self.createDialog()
+ dialog.setDirectory(_tmpDirectory)
+ self.qWaitForPendingActions(dialog)
+ state = dialog.saveState()
+ dialog = None
+
+ dialog2 = self.createDialog()
+ result = dialog2.restoreState(state)
+ self.assertTrue(result)
+ dialog2 = None
+
+ def printState(self):
+ """
+ Print state of the ImageFileDialog.
+
+ Can be used to add or regenerate `STATE_VERSION1_QT4` or
+ `STATE_VERSION1_QT5`.
+
+ >>> ./run_tests.py -v silx.gui.dialog.test.test_datafiledialog.TestDataFileDialogApi.printState
+ """
+ dialog = self.createDialog()
+ dialog.setDirectory("")
+ dialog.setHistory([])
+ dialog.setSidebarUrls([])
+ state = dialog.saveState()
+ string = ""
+ strings = []
+ for i in range(state.size()):
+ d = state.data()[i]
+ if not isinstance(d, int):
+ d = ord(d)
+ if d > 0x20 and d < 0x7F:
+ string += chr(d)
+ else:
+ string += "\\x%02X" % d
+ if len(string) > 60:
+ strings.append(string)
+ string = ""
+ strings.append(string)
+ strings = ["b'%s'" % s for s in strings]
+ print()
+ print("\\\n".join(strings))
+
+ STATE_VERSION1_QT4 = (
+ b""
+ b"\x00\x00\x00Z\x00s\x00i\x00l\x00x\x00.\x00g\x00u\x00i\x00.\x00"
+ b"d\x00i\x00a\x00l\x00o\x00g\x00.\x00D\x00a\x00t\x00a\x00F\x00i"
+ b"\x00l\x00e\x00D\x00i\x00a\x00l\x00o\x00g\x00.\x00D\x00a\x00t\x00"
+ b"a\x00F\x00i\x00l\x00e\x00D\x00i\x00a\x00l\x00o\x00g\x00\x00\x00"
+ b'\x01\x00\x00\x00\x0C\x00\x00\x00\x00"\x00\x00\x00\xFF\x00\x00'
+ b"\x00\x00\x00\x00\x00\x03\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+ b"\xFF\xFF\x01\x00\x00\x00\x06\x01\x00\x00\x00\x01\x00\x00\x00\x00"
+ b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0C\x00\x00\x00\x00"
+ b"}\x00\x00\x00\x0E\x00B\x00r\x00o\x00w\x00s\x00e\x00r\x00\x00\x00"
+ b"\x01\x00\x00\x00\x0C\x00\x00\x00\x00Z\x00\x00\x00\xFF\x00\x00"
+ b"\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00"
+ b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ b"\x00\x01\x90\x00\x00\x00\x04\x01\x01\x00\x00\x00\x00\x00\x00\x00"
+ b"\x00\x00\x00\x00\x00\x00\x00d\xFF\xFF\xFF\xFF\x00\x00\x00\x81"
+ b"\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x01\x90\x00\x00\x00\x04"
+ b"\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00"
+ b"\x01\xFF\xFF\xFF\xFF"
+ )
+ """Serialized state on Qt4. Generated using :meth:`printState`"""
+
+ STATE_VERSION1_QT5 = (
+ b""
+ b"\x00\x00\x00Z\x00s\x00i\x00l\x00x\x00.\x00g\x00u\x00i\x00.\x00"
+ b"d\x00i\x00a\x00l\x00o\x00g\x00.\x00D\x00a\x00t\x00a\x00F\x00i"
+ b"\x00l\x00e\x00D\x00i\x00a\x00l\x00o\x00g\x00.\x00D\x00a\x00t\x00"
+ b"a\x00F\x00i\x00l\x00e\x00D\x00i\x00a\x00l\x00o\x00g\x00\x00\x00"
+ b"\x01\x00\x00\x00\x0C\x00\x00\x00\x00#\x00\x00\x00\xFF\x00\x00"
+ b"\x00\x01\x00\x00\x00\x03\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
+ b"\xFF\xFF\x01\xFF\xFF\xFF\xFF\x01\x00\x00\x00\x01\x00\x00\x00\x00"
+ b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0C\x00\x00\x00"
+ b"\x00\xAA\x00\x00\x00\x0E\x00B\x00r\x00o\x00w\x00s\x00e\x00r\x00"
+ b"\x00\x00\x01\x00\x00\x00\x0C\x00\x00\x00\x00\x87\x00\x00\x00\xFF"
+ b"\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
+ b"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ b"\x00\x00\x00\x01\x90\x00\x00\x00\x04\x01\x01\x00\x00\x00\x00\x00"
+ b"\x00\x00\x00\x00\x00\x00\x00\x00\x00d\xFF\xFF\xFF\xFF\x00\x00"
+ b"\x00\x81\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00d\x00\x00"
+ b"\x00\x01\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x01\x00\x00"
+ b"\x00\x00\x00\x00\x00d\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00"
+ b"\x00d\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\xE8\x00\xFF"
+ b"\xFF\xFF\xFF\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01"
+ )
+ """Serialized state on Qt5. Generated using :meth:`printState`"""
+
+ def testAvoidRestoreRegression_Version1(self):
+ version = qt.qVersion().split(".")[0]
+ if version == "4":
+ state = self.STATE_VERSION1_QT4
+ elif version == "5":
+ state = self.STATE_VERSION1_QT5
+ else:
+ self.skipTest("Resource not available")
+
+ state = qt.QByteArray(state)
+ dialog = self.createDialog()
+ result = dialog.restoreState(state)
+ self.assertTrue(result)
+
+ def testRestoreRobusness(self):
+ """What's happen if you try to open a config file with a different
+ binding."""
+ state = qt.QByteArray(self.STATE_VERSION1_QT4)
+ dialog = self.createDialog()
+ dialog.restoreState(state)
+ state = qt.QByteArray(self.STATE_VERSION1_QT5)
+ dialog = None
+ dialog = self.createDialog()
+ dialog.restoreState(state)
+
+ def testRestoreNonExistingDirectory(self):
+ directory = os.path.join(_tmpDirectory, "dir")
+ os.mkdir(directory)
+ dialog = self.createDialog()
+ dialog.setDirectory(directory)
+ self.qWaitForPendingActions(dialog)
+ state = dialog.saveState()
+ os.rmdir(directory)
+ dialog = None
+
+ dialog2 = self.createDialog()
+ result = dialog2.restoreState(state)
+ self.assertTrue(result)
+ self.assertNotEqual(dialog2.directory(), directory)
+
+ def testHistory(self):
+ dialog = self.createDialog()
+ history = dialog.history()
+ dialog.setHistory([])
+ self.assertEqual(dialog.history(), [])
+ dialog.setHistory(history)
+ self.assertEqual(dialog.history(), history)
+
+ def testSidebarUrls(self):
+ dialog = self.createDialog()
+ urls = dialog.sidebarUrls()
+ dialog.setSidebarUrls([])
+ self.assertEqual(dialog.sidebarUrls(), [])
+ dialog.setSidebarUrls(urls)
+ self.assertEqual(dialog.sidebarUrls(), urls)
+
+ def testDirectory(self):
+ dialog = self.createDialog()
+ self.qWaitForPendingActions(dialog)
+ dialog.selectUrl(_tmpDirectory)
+ self.qWaitForPendingActions(dialog)
+ self.assertSamePath(dialog.directory(), _tmpDirectory)
+
+ def testBadFileFormat(self):
+ dialog = self.createDialog()
+ dialog.selectUrl(_tmpDirectory + "/badformat.h5")
+ self.qWaitForPendingActions(dialog)
+ self.assertIsNone(dialog._selectedData())
+
+ def testBadPath(self):
+ dialog = self.createDialog()
+ dialog.selectUrl("#$%/#$%")
+ self.qWaitForPendingActions(dialog)
+ self.assertIsNone(dialog._selectedData())
+
+ def testBadSubpath(self):
+ dialog = self.createDialog()
+ self.qWaitForPendingActions(dialog)
+
+ browser = testutils.findChildren(dialog, qt.QWidget, name="browser")[0]
+
+ filename = _tmpDirectory + "/data.h5"
+ url = silx.io.url.DataUrl(
+ scheme="silx", file_path=filename, data_path="/group/foobar"
+ )
+ dialog.selectUrl(url.path())
+ self.qWaitForPendingActions(dialog)
+ self.assertIsNotNone(dialog._selectedData())
+
+ # an existing node is browsed, but the wrong path is selected
+ index = browser.rootIndex()
+ obj = index.model().data(index, role=Hdf5TreeModel.H5PY_OBJECT_ROLE)
+ self.assertEqual(obj.name, "/group")
+ url = silx.io.url.DataUrl(dialog.selectedUrl())
+ self.assertEqual(url.data_path(), "/group")
+
+ def testUnsupportedSlicingPath(self):
+ dialog = self.createDialog()
+ self.qWaitForPendingActions(dialog)
+ dialog.selectUrl(_tmpDirectory + "/data.h5?path=/cube&slice=0")
+ self.qWaitForPendingActions(dialog)
+ data = dialog._selectedData()
+ if data is None:
+ # Maybe nothing is selected
+ self.assertTrue(True)
+ else:
+ # Maybe the cube is selected but not sliced
+ self.assertEqual(len(data.shape), 3)