Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/PyMca5/PyMcaCore/NexusDataSource.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,15 @@ def refresh(self):
phynxInstance._sourceName = pattern
self.__lastKeyInfo = {}

def close(self):
"""Close every open HDF5 handle held by this source."""
for instance in self._sourceObjectList:
try:
instance.close()
except Exception as e:
_logger.debug("Error closing HDF5 source: %s", e)
self._sourceObjectList = []

def getSourceInfo(self):
"""
Returns a dictionary with the key "KeyList" (list of all available keys
Expand Down
16 changes: 15 additions & 1 deletion src/PyMca5/PyMcaGui/io/hdf5/HDF5Widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,11 +425,18 @@ class FileModel(qt.QAbstractItemModel):
"""
sigFileUpdated = qt.pyqtSignal(object)
sigFileAppended = qt.pyqtSignal(object)
sigReadFailed = qt.pyqtSignal(object)

def __init__(self, parent=None):
qt.QAbstractItemModel.__init__(self, parent)
self.rootItem = RootItem(['File/Group/Dataset', 'Description', 'Shape', 'DType'])
self._idMap = {qt.QModelIndex().internalId(): self.rootItem}
# to warn only once about read error, can be reset intentionally
self._readErrorReported = False

def allowErrorDialog(self):
"""Allow the read error to appear (again)."""
self._readErrorReported = False

def sort(self, column, order):
#print("FileModel sort called with ", column, order)
Expand Down Expand Up @@ -572,7 +579,14 @@ def parent(self, index):
return self.createIndex(parent.row, 0, parent)

def rowCount(self, index):
return len(self.getProxyFromIndex(index))
try:
# the `len(self.children)` can fail.
return len(self.getProxyFromIndex(index))
except Exception:
if not self._readErrorReported:
self._readErrorReported = True
Comment thread
woutdenolf marked this conversation as resolved.
self.sigReadFailed.emit({"event": "readError", "index": index})
return 0

def openFile(self, filename, weakreference=False):
gc.collect()
Expand Down
26 changes: 26 additions & 0 deletions src/PyMca5/PyMcaGui/io/hdf5/QNexusWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ def dataSourceDestroyed(weakrefReference):
# stays visible and the tree does not collapse.
try:
newModel = HDF5Widget.FileModel()
newModel.sigReadFailed.connect(self._hdf5ReadFailed)
for source in self.data._sourceObjectList:
newModel.appendPhynxFile(source, weakreference=True)
self._modelDict[ref] = newModel
Expand All @@ -515,6 +516,31 @@ def dataSourceDestroyed(weakrefReference):
if hasattr(self.hdf5Widget, "expandToDepth"):
self.hdf5Widget.expandToDepth(0)

def _hdf5ReadFailed(self, ddict):
self._recoverFromReadError(ddict.get("index"))
self._showReadFailedMessage()

def _recoverFromReadError(self, index):
model = self.hdf5Widget.model()
# Collapse the node that failed to be read
if index is not None and index.isValid():
self.hdf5Widget.collapse(index)
# A later failed expansion should warn again
model.allowErrorDialog()

def _showReadFailedMessage(self):
msg = qt.QMessageBox(self)
msg.setIcon(qt.QMessageBox.Warning)
msg.setWindowTitle("Cannot read HDF5 file")
msg.setText("The selected file could not be read.")
msg.setInformativeText(
"It may be being written now. Try again later." \
"If it is stuck, try to refresh (F5) or close and open the file again.")
# `open()` (not `exec()`) blocks the window but not the code
# `WA_DeleteOnClose` to delete itself on close (to avoid a leak)
msg.setAttribute(qt.Qt.WA_DeleteOnClose)
msg.open()

def _autoRefreshDatasets(self, source=None, moveToLastSlice=True):
"""
Auto-refresh: re-read datasets and re-plot without re-building the tree.
Expand Down
39 changes: 23 additions & 16 deletions src/PyMca5/PyMcaGui/pymca/QDispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,23 +365,30 @@ def _sourceSelectorSlot(self, ddict):
if not found:
_logger.debug("WARNING: source not found")
return
sourceType = source.sourceType
del self.sourceList[self.sourceList.index(source)]
for source in self.sourceList:
if sourceType == source.sourceType:
closedSource = source
sourceType = closedSource.sourceType
del self.sourceList[self.sourceList.index(closedSource)]
try:
for source in self.sourceList:
if sourceType == source.sourceType:
self.selectorWidget[sourceType].setDataSource(source)
self.tabWidget.setCurrentWidget(self.selectorWidget[sourceType])
return
#there is no other selection of that type
if len(self.sourceList):
source = self.sourceList[0]
sourceType = source.sourceType
self.selectorWidget[sourceType].setDataSource(source)
self.tabWidget.setCurrentWidget(self.selectorWidget[sourceType])
return
#there is no other selection of that type
if len(self.sourceList):
source = self.sourceList[0]
sourceType = source.sourceType
self.selectorWidget[sourceType].setDataSource(source)
else:
self.selectorWidget[sourceType].setDataSource(None)
self.tabWidget.setCurrentWidget(self.selectorWidget[sourceType])
elif ddict["event"] == "SourceClosed":
_logger.debug("not implemented yet")
else:
self.selectorWidget[sourceType].setDataSource(None)
self.tabWidget.setCurrentWidget(self.selectorWidget[sourceType])
finally:
# Without this the file stays "open" in the process
# and could not be reopened until PyMca restarted.
try:
closedSource.close()
Comment thread
woutdenolf marked this conversation as resolved.
except Exception as e:
_logger.debug("Error closing source: %s", e)

def _selectionUpdatedSlot(self, ddict):
_logger.debug("_selectionUpdatedSlot(self, dict=%s)", ddict)
Expand Down
Loading