Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions DataLab.spec
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ from PyInstaller.utils.hooks import collect_submodules, collect_data_files, copy
all_hidden_imports = collect_submodules('datalab')
# Plugins installed from a file may import any Sigima module
all_hidden_imports += collect_submodules('sigima', filter=lambda name: '.tests' not in name)
# imagecodecs loads its codec extension modules lazily (``importlib``), so they
# are not detected by static analysis; tifffile needs them for compressed TIFF
all_hidden_imports += collect_submodules(
'imagecodecs', filter=lambda name: name.split('.')[-1] not in ('numcodecs', 'zarr')
)
datas = collect_data_files('datalab') + [('datalab\\plugins\\builtin', 'datalab\\plugins\\builtin')]
# Macro templates are read with ``pkgutil.get_data`` and must exist on disk,
# not only inside the PYZ archive (``collect_data_files`` skips ``.py`` files):
Expand Down
43 changes: 41 additions & 2 deletions datalab/aiassistant/tools/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
from qtpy import QtCore as QC
from qtpy import QtGui as QG
from qtpy import QtWidgets as QW
from sigima.io import RawImageImportParam
from sigima.io.image.raw import RAW_DTYPES

from datalab.aiassistant.providers.base import ChatMessage
from datalab.aiassistant.tools.registry import Tool, ToolRegistry, ToolResult
Expand Down Expand Up @@ -408,10 +410,15 @@ def _tool_load_file(
mainwindow: DLMainWindow,
filename: str,
panel: str | None = None,
import_parameters: dict[str, Any] | None = None,
) -> dict[str, Any]:
panel_widget = _resolve_panel(mainwindow, panel)
proxy.set_current_panel(panel_widget.PANEL_STR_ID)
objs = panel_widget.load_from_files([filename])
param = None
if import_parameters:
# ``create`` rejects unknown parameter names
param = RawImageImportParam.create(**import_parameters)
objs = panel_widget.load_from_files([filename], param=param)
return {
"filename": filename,
"panel": panel_widget.PANEL_STR_ID,
Expand Down Expand Up @@ -994,13 +1001,45 @@ def build_default_registry(expose_macro_tool: bool = True) -> ToolRegistry:
name="load_file",
description=(
"Load a signal or image file into DataLab. The file format is "
"auto-detected based on the filename extension."
"auto-detected based on the filename extension. Headerless RAW "
"image files (*.raw) require 'import_parameters' describing their "
"binary layout; otherwise, the user is asked for them."
),
parameters={
"type": "object",
"properties": {
"filename": {"type": "string"},
"panel": _PANEL_PARAM,
"import_parameters": {
"type": "object",
"description": (
"RAW image import parameters (image panel only). "
"Omitted fields keep their default value."
),
"properties": {
"dtype": {"type": "string", "enum": list(RAW_DTYPES)},
"width": {"type": "integer", "minimum": 1},
"height": {"type": "integer", "minimum": 1},
"offset": {
"type": "integer",
"minimum": 0,
"description": "Bytes before the first image.",
},
"count": {
"type": "integer",
"minimum": 1,
"description": "Number of images in the file.",
},
"gap": {
"type": "integer",
"minimum": 0,
"description": "Bytes between consecutive images.",
},
"little_endian": {"type": "boolean"},
"white_is_zero": {"type": "boolean"},
},
"additionalProperties": False,
},
},
"required": ["filename"],
"additionalProperties": False,
Expand Down
32 changes: 26 additions & 6 deletions datalab/control/baseproxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,19 +283,29 @@ def open_capsule(self, filename: str, workspace: str | None = None) -> None:
"""

@abc.abstractmethod
def load_from_files(self, filenames: list[str]) -> None:
def load_from_files(
self, filenames: list[str], param: gds.DataSet | None = None
) -> None:
"""Open objects from files in current panel (signals/images).

Args:
filenames: list of file names
param: Optional format-specific import parameters applied to every
file requiring them, without showing any dialog (e.g.
:class:`sigima.io.RawImageImportParam` for RAW images).
Defaults to None.
"""

@abc.abstractmethod
def load_from_directory(self, path: str) -> None:
def load_from_directory(self, path: str, param: gds.DataSet | None = None) -> None:
"""Open objects from directory in current panel (signals/images).

Args:
path: directory path
param: Optional format-specific import parameters applied to every
file requiring them, without showing any dialog (e.g.
:class:`sigima.io.RawImageImportParam` for RAW images).
Defaults to None.
"""

@abc.abstractmethod
Expand Down Expand Up @@ -886,21 +896,31 @@ def open_capsule(self, filename: str, workspace: str | None = None) -> None:
"""
self._datalab.open_capsule(filename, workspace)

def load_from_files(self, filenames: list[str]) -> None:
def load_from_files(
self, filenames: list[str], param: gds.DataSet | None = None
) -> None:
"""Open objects from files in current panel (signals/images).

Args:
filenames: list of file names
param: Optional format-specific import parameters applied to every
file requiring them, without showing any dialog (e.g.
:class:`sigima.io.RawImageImportParam` for RAW images).
Defaults to None.
"""
self._datalab.load_from_files(filenames)
self._datalab.load_from_files(filenames, param)

def load_from_directory(self, path: str) -> None:
def load_from_directory(self, path: str, param: gds.DataSet | None = None) -> None:
"""Open objects from directory in current panel (signals/images).

Args:
path: directory path
param: Optional format-specific import parameters applied to every
file requiring them, without showing any dialog (e.g.
:class:`sigima.io.RawImageImportParam` for RAW images).
Defaults to None.
"""
self._datalab.load_from_directory(path)
self._datalab.load_from_directory(path, param)

def get_sel_object_uuids(self, include_groups: bool = False) -> list[str]:
"""Return selected objects uuids.
Expand Down
54 changes: 48 additions & 6 deletions datalab/control/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ class RemoteServer(QC.QThread):
SIG_ADD_OBJECT = QC.Signal(object, str, bool)
SIG_SET_OBJECT = QC.Signal(object)
SIG_ADD_GROUP = QC.Signal(str, str, bool)
SIG_LOAD_FROM_FILES = QC.Signal(list)
SIG_LOAD_FROM_DIRECTORY = QC.Signal(str)
SIG_LOAD_FROM_FILES = QC.Signal(list, object)
SIG_LOAD_FROM_DIRECTORY = QC.Signal(str, object)
SIG_SELECT_OBJECTS = QC.Signal(list, str)
SIG_SELECT_GROUPS = QC.Signal(list, str)
SIG_SELECT_ALL_GROUPS = QC.Signal(str)
Expand Down Expand Up @@ -412,22 +412,32 @@ def open_capsule(self, filename: str, workspace: str | None = None) -> None:
)

@remote_call
def load_from_files(self, filenames: list[str]) -> None:
def load_from_files(
self, filenames: list[str], param_data: list[str] | None = None
) -> None:
"""Open objects from files in current panel (signals/images).

Args:
filenames: list of file names
param_data: Optional serialized format-specific import parameters
applied to every file requiring them. Defaults to None.
"""
self.SIG_LOAD_FROM_FILES.emit(filenames)
param = None if param_data is None else utils.rpcjson_to_dataset(param_data)
self.SIG_LOAD_FROM_FILES.emit(filenames, param)

@remote_call
def load_from_directory(self, path: str) -> None:
def load_from_directory(
self, path: str, param_data: list[str] | None = None
) -> None:
"""Open objects from directory in current panel (signals/images).

Args:
path: directory path
param_data: Optional serialized format-specific import parameters
applied to every file requiring them. Defaults to None.
"""
self.SIG_LOAD_FROM_DIRECTORY.emit(path)
param = None if param_data is None else utils.rpcjson_to_dataset(param_data)
self.SIG_LOAD_FROM_DIRECTORY.emit(path, param)

@remote_call
def add_signal(
Expand Down Expand Up @@ -1193,6 +1203,38 @@ def set_object(self, obj: SignalObj | ImageObj) -> None:
obj_data = utils.dataset_to_rpcjson(obj)
self._datalab.set_object(obj_data)

def load_from_files(
self, filenames: list[str], param: gds.DataSet | None = None
) -> None:
"""Open objects from files in current panel (signals/images).

Args:
filenames: list of file names
param: Optional format-specific import parameters applied to every
file requiring them, without showing any dialog (e.g.
:class:`sigima.io.RawImageImportParam` for RAW images).
Defaults to None.
"""
if param is None:
self._datalab.load_from_files(filenames)
else:
self._datalab.load_from_files(filenames, utils.dataset_to_rpcjson(param))

def load_from_directory(self, path: str, param: gds.DataSet | None = None) -> None:
"""Open objects from directory in current panel (signals/images).

Args:
path: directory path
param: Optional format-specific import parameters applied to every
file requiring them, without showing any dialog (e.g.
:class:`sigima.io.RawImageImportParam` for RAW images).
Defaults to None.
"""
if param is None:
self._datalab.load_from_directory(path)
else:
self._datalab.load_from_directory(path, utils.dataset_to_rpcjson(param))

def calc(self, name: str, param: gds.DataSet | None = None) -> None:
"""Call computation feature ``name``

Expand Down
14 changes: 10 additions & 4 deletions datalab/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2581,24 +2581,30 @@ def set_object(self, obj: SignalObj | ImageObj) -> None:
raise TypeError(f"Unsupported object type {type(obj)}")

@remote_controlled
def load_from_files(self, filenames: list[str]) -> None:
def load_from_files(
self, filenames: list[str], param: gds.DataSet | None = None
) -> None:
"""Open objects from files in current panel (signals/images)

Args:
filenames: list of filenames
param: Optional format-specific import parameters applied to every
file requiring them, without showing any dialog. Defaults to None.
"""
panel = self.__get_current_basedatapanel()
panel.load_from_files(filenames)
panel.load_from_files(filenames, param=param)

@remote_controlled
def load_from_directory(self, path: str) -> None:
def load_from_directory(self, path: str, param: gds.DataSet | None = None) -> None:
"""Open objects from directory in current panel (signals/images).

Args:
path: directory path
param: Optional format-specific import parameters applied to every
file requiring them, without showing any dialog. Defaults to None.
"""
panel = self.__get_current_basedatapanel()
panel.load_from_directory(path)
panel.load_from_directory(path, param=param)

# ------Other methods related to AbstractDLControl interface
def get_version(self) -> str:
Expand Down
Loading
Loading