Source code for cosedaUI.h5_metadata_loader

"""Background worker for collecting essential HDF5 metadata."""

from __future__ import annotations

import h5py
import numpy as np
from PyQt6.QtCore import QObject, pyqtSignal

from coseda.nexus.paths import get_mask_dataset
from h5py import SoftLink, ExternalLink

[docs] class H5MetadataLoader(QObject): """Load lightweight metadata from an HDF5 file without blocking the UI.""" finished = pyqtSignal(dict) failed = pyqtSignal(str) def __init__(self, h5_path: str): super().__init__() self.h5_path = h5_path
[docs] def run(self): info = { 'total_frames': 0, 'stagepos_exists': False, 'intensity_exists': False, 'has_peaks': False, 'has_center': False, 'mask': None, 'has_npeaks': False, } try: with h5py.File(self.h5_path, 'r') as file: data_group = file.get('entry/data') if data_group is None: raise KeyError("entry/data group missing in HDF5 file.") # Resolve images dataset robustly (follow internal links) link_obj = data_group.get('images', getlink=True) if isinstance(link_obj, SoftLink): target = link_obj.path if target in file: images_ds = file[target] else: raise KeyError(f"entry/data/images soft-links to '{target}', which is missing.") elif isinstance(link_obj, ExternalLink): # Internal-only expected; surface a clearer message if external raise KeyError(f"entry/data/images is an external link to '{link_obj.filename}:{link_obj.path}', which is not accessible from this environment.") elif link_obj is None: images_ds = None else: # Hard link or dataset images_ds = data_group.get('images') if images_ds is None: raise KeyError("entry/data/images dataset missing in HDF5 file.") try: info['total_frames'] = images_ds.shape[0] except Exception as exc: raise KeyError(f"entry/data/images exists but shape could not be read: {exc}") info['stagepos_exists'] = all( key in data_group for key in ['stagepos_x_refined', 'stagepos_y'] ) info['intensity_exists'] = any( key in data_group for key in ['frame_mean_intensities', 'frame_total_intensities'] ) info['has_peaks'] = ( 'peakXPosRaw' in data_group and 'peakYPosRaw' in data_group ) info['has_center'] = ( 'center_x' in data_group and 'center_y' in data_group ) info['has_npeaks'] = 'nPeaks' in data_group info['bit_depth'] = images_ds.dtype.itemsize * 8 mask_ds = get_mask_dataset(file) if mask_ds is not None: if mask_ds.ndim == 2: info['mask'] = np.asarray(mask_ds[()]) self.finished.emit(info) except Exception as exc: self.failed.emit(str(exc))