@@ -406,11 +406,17 @@ def raw_keys(self):
406406 return self .file [data_path ].keys ()
407407 else :
408408 from PyMca5 .PyMcaIO import HDF5Utils
409- return HDF5Utils .safe_hdf5_group_keys (file_path ,
409+ result = HDF5Utils .safe_hdf5_group_keys (file_path ,
410410 data_path = data_path )
411+ if result :
412+ return result
413+ _logger .debug ("Subprocess returned empty." )
414+ # It could be a really empty or a silent failure (file locked by writer).
415+ # becasue `safe_hdf5_group_keys (..., default=list())`.
416+ # If it is empty standard approach will also return empty.
411417 except Exception :
412- _logger .debug ("Using standard approach" )
413- return self .file [data_path ].keys ()
418+ _logger .debug ("Trying standard approach" )
419+ return self .file [data_path ].keys ()
414420 else :
415421 file_path = self .file .filename
416422 data_path = self .name
@@ -666,6 +672,26 @@ def clear(self):
666672 # rootItem.deleteChild(child)
667673 rootItem .children .clear ()
668674 self .endResetModel ()
675+
676+ def swapFileHandles (self , handleMap ):
677+ """Replace ``_file`` on loaded proxy nodes to point at fresh handles."""
678+ if not handleMap :
679+ return
680+ for child in self .rootItem ._children :
681+ try :
682+ oldName = child ._file ._sourceName
683+ except (ReferenceError , AttributeError , ValueError ):
684+ continue
685+ fresh = handleMap .get (oldName )
686+ if fresh is None :
687+ continue
688+ self ._swapNodeFile (child , fresh )
689+
690+ def _swapNodeFile (self , node , freshHandle ):
691+ """Replace ``_file`` on node and its children."""
692+ node ._file = freshHandle
693+ for child in node ._children :
694+ self ._swapNodeFile (child , freshHandle )
669695
670696class FileView (qt .QTreeView ):
671697
@@ -707,6 +733,7 @@ def fileUpdated(self, ddict):
707733class HDF5Widget (FileView ):
708734 def __init__ (self , model , parent = None , multi_selection = False ):
709735 FileView .__init__ (self , model , parent )
736+ self ._savedTreeState = None
710737 self .setSelectionBehavior (qt .QAbstractItemView .SelectRows )
711738 if multi_selection :
712739 self .setSelectionMode (qt .QAbstractItemView .ExtendedSelection )
@@ -848,9 +875,90 @@ def getSelectedEntries(self):
848875 entry = "/" + path .split ("/" )[1 ]
849876 if (entry , filename ) not in entryList :
850877 entryList .append ((entry , filename ))
851- _logger .info ("Returned entryList %s" % entryList )
878+ # `debug` instead of `info` to avoid spam during `auto-refresh`
879+ _logger .debug ("Returned entryList %s" % entryList )
852880 return entryList
853881
882+ def saveTreeState (self ):
883+ """Save and return expanded and selected sets in HDF5 tree."""
884+ expanded = set ()
885+ selected = set ()
886+ model = self .model ()
887+ if model is None :
888+ self ._savedTreeState = (expanded , selected )
889+ return self ._savedTreeState
890+ rootIndex = self .rootIndex ()
891+ self ._collectExpandedPaths (model , rootIndex , expanded )
892+ for modelIndex in self .selectedIndexes ():
893+ if modelIndex .column () != 0 :
894+ continue
895+ item = model .getProxyFromIndex (modelIndex )
896+ try :
897+ selected .add ((item .file .filename , item .name ))
898+ except Exception :
899+ continue
900+ self ._savedTreeState = (expanded , selected )
901+ return self ._savedTreeState
902+
903+ def _collectExpandedPaths (self , model , parentIndex , expanded ):
904+ """Recursively collect expanded-node."""
905+ for row in range (model .rowCount (parentIndex )):
906+ if not model .hasIndex (row , 0 , parentIndex ):
907+ continue
908+ index = model .index (row , 0 , parentIndex )
909+ if not self .isExpanded (index ):
910+ continue
911+ item = model .getProxyFromIndex (index )
912+ try :
913+ expanded .add ((item .file .filename , item .name ))
914+ except Exception :
915+ continue
916+ self ._collectExpandedPaths (model , index , expanded )
917+
918+ def hasSavedTreeState (self ):
919+ return self ._savedTreeState is not None
920+
921+ def clearSavedTreeState (self ):
922+ self ._savedTreeState = None
923+
924+ def restoreTreeState (self , state = None ):
925+ """Re-expand and re-select nodes from (saved) state."""
926+ if state is None :
927+ state = self ._savedTreeState
928+ self ._savedTreeState = None
929+ if state is None :
930+ return
931+ expandedPaths , selectedPaths = state
932+ model = self .model ()
933+ if model is None :
934+ return
935+ if expandedPaths or selectedPaths :
936+ rootIndex = self .rootIndex ()
937+ selModel = self .selectionModel ()
938+ self ._restoreTreeNodes (model , rootIndex , expandedPaths ,
939+ selectedPaths , selModel )
940+
941+ def _restoreTreeNodes (self , model , parentIndex ,
942+ expandPaths , selectPaths , selModel ):
943+ for row in range (model .rowCount (parentIndex )):
944+ if not model .hasIndex (row , 0 , parentIndex ):
945+ continue
946+ index = model .index (row , 0 , parentIndex )
947+ item = model .getProxyFromIndex (index )
948+ try :
949+ key = (item .file .filename , item .name )
950+ except Exception :
951+ continue
952+ if key in selectPaths :
953+ selModel .select (
954+ index ,
955+ qt .QItemSelectionModel .Select |
956+ qt .QItemSelectionModel .Rows )
957+ if key in expandPaths :
958+ self .setExpanded (index , True )
959+ self ._restoreTreeNodes (model , index , expandPaths ,
960+ selectPaths , selModel )
961+
854962
855963class Hdf5SelectionDialog (qt .QDialog ):
856964 """Dialog widget to select a HDF5 item in a file.
0 commit comments