Source code for datalab.gui.h5io
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
"""
HDF5 I/O
========
The :mod:`datalab.gui.h5io` module provides the HDF5 file open/save into/from
DataLab data model/main window.
.. autoclass:: H5InputOutput
"""
from __future__ import annotations
import os.path as osp
from typing import TYPE_CHECKING
from guidata.qthelpers import exec_dialog
from qtpy import QtWidgets as QW
from sigima.objects import SignalObj
from datalab.config import _
from datalab.env import execenv
from datalab.h5 import H5Importer
from datalab.h5.native import NativeH5Reader, NativeH5Writer
from datalab.utils.qthelpers import create_progress_bar, qt_try_loadsave_file
from datalab.widgets.h5browser import H5BrowserDialog
if TYPE_CHECKING:
from datalab.gui.main import DLMainWindow
from datalab.h5.common import BaseNode
[docs]
class H5InputOutput:
"""Object handling HDF5 file open/save into/from DataLab data model/main window
Args:
mainwindow: Main window
"""
def __init__(self, mainwindow: DLMainWindow) -> None:
self.mainwindow = mainwindow
self.uint32_wng: bool = None
@staticmethod
def __progbartitle(fname: str) -> str:
"""Return progress bar title"""
return _("Loading data from %s...") % osp.basename(fname)
[docs]
def save_file(self, filename: str) -> None:
"""Save all signals and images from DataLab model into a HDF5 file"""
writer = NativeH5Writer(filename)
for panel in self.mainwindow.panels:
panel.serialize_to_hdf5(writer)
writer.close()
[docs]
def open_file_headless(self, filename: str, reset_all: bool) -> bool:
"""Open native DataLab HDF5 file without any GUI elements.
This method can be safely called from any thread (e.g., the console thread)
as it does not create any Qt widgets or dialogs.
Args:
filename: HDF5 filename
reset_all: Reset all application data before importing
Returns:
True if file was successfully opened as a native DataLab file,
False if the file format is not compatible (KeyError was raised)
"""
try:
reader = NativeH5Reader(filename)
if reset_all:
self.mainwindow.reset_all()
for panel in self.mainwindow.panels:
panel.deserialize_from_hdf5(reader, reset_all)
reader.close()
return True
except KeyError:
return False
[docs]
def open_file(self, filename: str, import_all: bool, reset_all: bool) -> None:
"""Open HDF5 file"""
progress = None
try:
reader = NativeH5Reader(filename)
if reset_all:
self.mainwindow.reset_all()
with create_progress_bar(
self.mainwindow, self.__progbartitle(filename), 2
) as progress:
for idx, panel in enumerate(self.mainwindow.panels):
progress.setValue(idx + 1)
QW.QApplication.processEvents()
panel.deserialize_from_hdf5(reader, reset_all)
if progress.wasCanceled():
break
reader.close()
except KeyError:
if progress is not None:
# KeyError was encoutered when deserializing datasets (DataLab data
# model is not compatible with this version)
progress.close()
self.import_files([filename], import_all, reset_all)
def __add_object_from_node(self, node: BaseNode) -> None:
"""Add DataLab object from h5 node"""
obj = node.get_native_object()
if obj is None:
return
self.uint32_wng = self.uint32_wng or node.uint32_wng
if isinstance(obj, SignalObj):
self.mainwindow.signalpanel.add_object(obj)
else:
self.mainwindow.imagepanel.add_object(obj)
def __eventually_show_warnings(self) -> None:
"""Eventually show warnings after everything is imported"""
if self.uint32_wng:
QW.QMessageBox.warning(
self.mainwindow, _("Warning"), _("Clipping uint32 data to int32.")
)
[docs]
def import_files(
self, filenames: list[str], import_all: bool, reset_all: bool
) -> None:
"""Import HDF5 files"""
h5browser = H5BrowserDialog(self.mainwindow)
for filename in filenames:
with qt_try_loadsave_file(self.mainwindow, filename, "load"):
h5browser.open_file(filename)
if h5browser.is_empty():
h5browser.cleanup()
if not execenv.unattended:
QW.QMessageBox.warning(
self.mainwindow,
_("Warning"),
_("No supported data available in HDF5 file(s)."),
)
return
if execenv.unattended:
# Unattended mode: import all datasets (for testing)
import_all = True
if import_all or exec_dialog(h5browser):
if import_all:
nodes = h5browser.get_all_nodes()
else:
nodes = h5browser.get_nodes()
if nodes is not None:
if reset_all:
self.mainwindow.reset_all()
with qt_try_loadsave_file(self.mainwindow, "*.h5", "load"):
with create_progress_bar(self.mainwindow, "", len(nodes)) as prog:
self.uint32_wng = False
for idx, node in enumerate(nodes):
prog.setLabelText(self.__progbartitle(node.h5file.filename))
prog.setValue(idx + 1)
QW.QApplication.processEvents()
if prog.wasCanceled():
break
self.__add_object_from_node(node)
self.__eventually_show_warnings()
h5browser.cleanup()
[docs]
def import_dataset_from_file(self, filename: str, dsetname: str) -> None:
"""Import dataset from HDF5 file"""
h5importer = H5Importer(filename)
try:
node = h5importer.get(dsetname)
self.uint32_wng = False
self.__add_object_from_node(node)
self.__eventually_show_warnings()
except KeyError as exc:
raise KeyError(f"Dataset not found: {dsetname}") from exc
h5importer.close()