# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
"""Convenience I/O functions
This module provides convenient wrapper functions for input/output operations with
signals and images. These functions offer a simplified interface to the underlying
I/O system, making common tasks easier to perform.
"""
from __future__ import annotations
import os.path as osp
from typing import Generator, Sequence
import guidata.dataset as gds
from sigima.config import _
from sigima.io.common.basename import format_basenames
from sigima.io.image.base import ImageIORegistry
from sigima.io.signal.base import SignalIORegistry
from sigima.objects import ImageObj, SignalObj, TypeObj
[docs]
class SaveToDirectoryParam(gds.DataSet):
"""Save to directory parameters."""
def build_filenames(self, objs: list[TypeObj]) -> list[str]:
"""Build filenames according to current parameters."""
filenames = format_basenames(objs, self.basename + self.extension)
used: set[str] = set() # Ensure all filenames are unique.
for i, filename in enumerate(filenames):
root, ext = osp.splitext(filename)
filepath = osp.join(self.directory, filename)
k = 1
while (filename in used) or (not self.overwrite and osp.exists(filepath)):
filename = f"{root}_{k}{ext}"
filepath = osp.join(self.directory, filename)
k += 1
used.add(filename)
filenames[i] = filename
return filenames
def generate_filepath_obj_pairs(
self, objs: list[TypeObj]
) -> Generator[tuple[str, TypeObj], None, None]:
"""Iterate over (filepath, object) pairs to be saved."""
for filename, obj in zip(self.build_filenames(objs), objs):
yield osp.join(self.directory, filename), obj
directory = gds.DirectoryItem(_("Directory"))
basename = gds.StringItem(
_("Basename pattern"),
default="{title}",
help=_("""Pattern accepts a Python format string.
Standard Python formatting fields may be used, including:
{title}, {index}, {count}, {xlabel}, {xunit}, {ylabel}, {yunit},
{metadata}, {metadata[key]}"""),
)
extension = gds.StringItem(
_("Extension"),
help=_("File extension with leading dot (e.g. .txt or .csv)"),
regexp=r"^\.\w+$",
)
overwrite = gds.BoolItem(
_("Overwrite"), default=False, help=_("Overwrite existing files")
)
def read_signals(filename: str) -> Sequence[SignalObj]:
"""Read a list of signals from a file.
Args:
filename: File name.
Returns:
List of signals.
"""
return SignalIORegistry.read(filename)
def read_signal(filename: str) -> SignalObj:
"""Read a signal from a file.
Args:
filename: File name.
Returns:
Signal.
"""
return read_signals(filename)[0]
def write_signal(filename: str, signal: SignalObj) -> None:
"""Write a signal to a file.
Args:
filename: File name.
signal: Signal.
"""
SignalIORegistry.write(filename, signal)
def write_signals(p: SaveToDirectoryParam, signals: list[SignalObj]) -> None:
"""Write a list of signals to a file.
Args:
p: Save to directory parameters.
signals: List of signals.
"""
for filepath, signal in p.generate_filepath_obj_pairs(signals):
SignalIORegistry.write(filepath, signal)
def read_images(filename: str) -> Sequence[ImageObj]:
"""Read a list of images from a file.
Args:
filename: File name.
Returns:
List of images.
"""
return ImageIORegistry.read(filename)
def read_image(filename: str) -> ImageObj:
"""Read an image from a file.
Args:
filename: File name.
Returns:
Image.
"""
return read_images(filename)[0]
def write_image(filename: str, image: ImageObj) -> None:
"""Write an image to a file.
Args:
filename: File name.
image: Image.
"""
ImageIORegistry.write(filename, image)
def write_images(p: SaveToDirectoryParam, images: list[ImageObj]) -> None:
"""Write a list of images to files in a directory.
Args:
p: Save to directory parameters.
images: List of images.
"""
for filepath, image in p.generate_filepath_obj_pairs(images):
ImageIORegistry.write(filepath, image)