From 1bc829196a48ceb1969f075c9a54d06546e2c382 Mon Sep 17 00:00:00 2001 From: Marie Spitoni Date: Wed, 14 Aug 2024 16:16:37 +0200 Subject: [PATCH 1/3] missing ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1b88ef867..867c51d15 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ docs/_build/ # cython generated files src/PyMca5/PyMcaGraph/ctools/_ctools/cython/*.c src/PyMca5/PyMcaPhysics/xas/_xas/cython/*.c +src/PyMca5/PyMcaMath/mva/_cython_kmeans/*.c # PyCharm meta data .idea/ From 55ae83cb7ea5f1dfd828a131e29878e4f1dee692 Mon Sep 17 00:00:00 2001 From: Marie Spitoni Date: Wed, 14 Aug 2024 16:16:58 +0200 Subject: [PATCH 2/3] enriched HDF5Info for NeXus --- src/PyMca5/PyMcaGui/io/hdf5/NexusInfo.py | 108 ++++++++++++++++++++ src/PyMca5/PyMcaGui/io/hdf5/QNexusWidget.py | 8 +- 2 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 src/PyMca5/PyMcaGui/io/hdf5/NexusInfo.py diff --git a/src/PyMca5/PyMcaGui/io/hdf5/NexusInfo.py b/src/PyMca5/PyMcaGui/io/hdf5/NexusInfo.py new file mode 100644 index 000000000..42e664fea --- /dev/null +++ b/src/PyMca5/PyMcaGui/io/hdf5/NexusInfo.py @@ -0,0 +1,108 @@ +import h5py + +from PyMca5.PyMcaGui import PyMcaQt as qt +from . import HDF5Info + + +class NexusMotorInfoWidget(qt.QWidget): + def __init__(self, parent): + super().__init__(parent) + + self.mainLayout = qt.QVBoxLayout(self) + self.mainLayout.setContentsMargins(0, 0, 0, 0) + self.mainLayout.setSpacing(2) + + self.label = qt.QLabel(self) + self.label.setText("Number of motors: 0") + + column_names = ["Name", "Value", "Units"] + self._column_names = column_names + + self.table = qt.QTableWidget(self) + self.table.setColumnCount(len(column_names)) + for i in range(len(column_names)): + item = self.table.horizontalHeaderItem(i) + if item is None: + item = qt.QTableWidgetItem(column_names[i], qt.QTableWidgetItem.Type) + item.setText(column_names[i]) + self.table.setHorizontalHeaderItem(i, item) + self.table.setSortingEnabled(True) + + self.mainLayout.addWidget(self.label) + self.mainLayout.addWidget(self.table) + + def setInfoDict(self, ddict): + if "motors" in ddict: + self._setInfoDict(ddict["motors"]) + else: + self._setInfoDict(ddict) + + def _setInfoDict(self, ddict): + nrows = len(ddict.get(self._column_names[0], [])) + self.label.setText("Number of motors: %d" % nrows) + self.table.setRowCount(nrows) + + if not nrows: + self.hide() + return + + for row in range(nrows): + for col, label in enumerate(self._column_names): + text = str(ddict[label][row]) + item = self.table.item(row, col) + if item is None: + item = qt.QTableWidgetItem(text, qt.QTableWidgetItem.Type) + item.setFlags(qt.Qt.ItemIsSelectable | qt.Qt.ItemIsEnabled) + self.table.setItem(row, col, item) + else: + item.setText(text) + + for col in range(len(self._column_names)): + self.table.resizeColumnToContents(col) + + +class NexusInfoWidget(HDF5Info.HDF5InfoWidget): + + def _build(self): + super()._build() + self.motorInfoWidget = NexusMotorInfoWidget(self) + self.addTab(self.motorInfoWidget, "Motors") + + def setInfoDict(self, ddict): + super().setInfoDict(ddict) + self.motorInfoWidget.setInfoDict(ddict) + + +def getInfo(hdf5File, node): + """ + hdf5File is and HDF5 file-like insance + node is the posix path to the node + """ + info = HDF5Info.getInfo(hdf5File, node) + info["motors"] = get_motor_positions(hdf5File, node) + return info + + +def get_motor_positions(hdf5File, node): + node = hdf5File[node] + + nxentry_name = node.name.split("/")[1] + if not nxentry_name: + return dict() + + nxentry = hdf5File[nxentry_name] + if not isinstance(nxentry, h5py.Group): + return dict() + + nxpositioners = nxentry.get("instrument/positioners_start", None) + if nxpositioners is None or not isinstance(nxpositioners, h5py.Group): + return dict() + + positions = [ + (name, dset[()], dset.attrs.get("units", "")) + for name, dset in nxpositioners.items() + if isinstance(dset, h5py.Dataset) and dset.ndim == 0 + ] + column_names = "Name", "Value", "Units" + + return dict(zip(column_names, zip(*positions))) diff --git a/src/PyMca5/PyMcaGui/io/hdf5/QNexusWidget.py b/src/PyMca5/PyMcaGui/io/hdf5/QNexusWidget.py index de1c964ef..1961a434b 100644 --- a/src/PyMca5/PyMcaGui/io/hdf5/QNexusWidget.py +++ b/src/PyMca5/PyMcaGui/io/hdf5/QNexusWidget.py @@ -47,7 +47,7 @@ QString = str from . import HDF5Widget -from . import HDF5Info +from . import NexusInfo from . import HDF5CounterTable from . import HDF5McaTable from . import QNexusWidgetActions @@ -123,7 +123,7 @@ def __init__(self, parent=None, mca=False, buttons=False): self._autoCntList = [] self._autoAliasList = [] self._defaultModel = HDF5Widget.FileModel() - self.getInfo = HDF5Info.getInfo + self.getInfo = NexusInfo.getInfo self._modelDict = {} self._widgetDict = {} self._lastWidgetId = None @@ -507,8 +507,9 @@ def showInfoWidget(self, filename, name, dset=False): phynxFile = self.data._sourceObjectList[fileIndex] else: phynxFile = HDF5Widget.h5open(filename) + info = self.getInfo(phynxFile, name) - widget = HDF5Info.HDF5InfoWidget() + widget = NexusInfo.NexusInfoWidget() widget.notifyCloseEventToWidget(self) title = os.path.basename(filename) title += " %s" % name @@ -531,6 +532,7 @@ def sourceObjectDestroyed(weakrefReference): del self._widgetDict[wid] widget._sourceObjectWeakReference = weakref.ref(phynxFile, sourceObjectDestroyed) + widget.setInfoDict(info) # todo: this first `if` block can be dropped when silx is a hard dependency if dset and Hdf5NodeView is None: From add44bb3df54febac02e9d74243d8254f0327353 Mon Sep 17 00:00:00 2001 From: Marie Spitoni Date: Thu, 29 Aug 2024 16:21:03 +0200 Subject: [PATCH 3/3] Add getStartingPositionnersGroup function --- src/PyMca5/PyMcaCore/NexusTools.py | 22 ++++++++++++++++++++++ src/PyMca5/PyMcaGui/io/hdf5/NexusInfo.py | 4 +++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/PyMca5/PyMcaCore/NexusTools.py b/src/PyMca5/PyMcaCore/NexusTools.py index 4ce0b5547..f753a5e7f 100644 --- a/src/PyMca5/PyMcaCore/NexusTools.py +++ b/src/PyMca5/PyMcaCore/NexusTools.py @@ -510,6 +510,28 @@ def getPositionersGroup(h5file, path): positioners = group return positioners +def getStartingPositionersGroup(h5file, path): + """ + Retrieve the start positioners group associated to a path + retrieving them from the same entry. + + It assumes they are either in: + + - NXentry/NXinstrument/positioners_start or + + """ + entry_path = getEntryName(path, h5file=h5file) + instrument = getNXClassGroups(h5file, entry_path, ["NXinstrument", b"NXinstrument"], single=True) + positioners = None + if len(instrument): + instrument = instrument[0] + for key in instrument.keys(): + if key in ["positioners_start", b"positioners_start"]: + positioners = instrument[key] + if not isGroup(positioners): + positioners = None + return positioners + def getMeasurementGroup(h5file, path): """ Retrieve the measurement group associated to a path diff --git a/src/PyMca5/PyMcaGui/io/hdf5/NexusInfo.py b/src/PyMca5/PyMcaGui/io/hdf5/NexusInfo.py index 42e664fea..53356cf9e 100644 --- a/src/PyMca5/PyMcaGui/io/hdf5/NexusInfo.py +++ b/src/PyMca5/PyMcaGui/io/hdf5/NexusInfo.py @@ -1,6 +1,8 @@ import h5py from PyMca5.PyMcaGui import PyMcaQt as qt +from PyMca5.PyMcaCore.NexusTools import getStartingPositionersGroup + from . import HDF5Info @@ -94,7 +96,7 @@ def get_motor_positions(hdf5File, node): if not isinstance(nxentry, h5py.Group): return dict() - nxpositioners = nxentry.get("instrument/positioners_start", None) + nxpositioners = getStartingPositionersGroup(hdf5File, nxentry_name) if nxpositioners is None or not isinstance(nxpositioners, h5py.Group): return dict()