Skip to content

Commit 9894ab6

Browse files
committed
Fix API changes and bug in multiple signals loading feature
1 parent dc8285a commit 9894ab6

File tree

20 files changed

+336
-277
lines changed

20 files changed

+336
-277
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ for future and past milestones.
55

66
## DataLab Version 0.14.2 ##
77

8+
⚠️ API changes required for fixing support for multiple signals loading feature:
9+
* Merged `open_object` and `open_objects` methods to `load_from_files` in proxy
10+
classes, main window and data panels
11+
* For consistency's sake: merged `save_object` and `save_objects` into `save_to_files`
12+
* To sum up, those changes lead to the following situation:
13+
* `load_from_files`: load a sequence of objects from multiple files
14+
* `save_to_files`: save a sequence of objects to multiple files (at the moment,
15+
it only supports saving a single object to a single file, but it may be extended
16+
in the future to support saving multiple objects to a single file)
17+
818
🛠️ Bug fixes:
919

1020
* Fixed [Issue #61](https://github.com/DataLab-Platform/DataLab/issues/61) - Text file import wizard: application crash when importing a multiple curve text file:

cdl/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import os
1818

19-
__version__ = "0.14.1"
19+
__version__ = "0.14.2"
2020
__docurl__ = __homeurl__ = "https://datalab-platform.com/"
2121
__supporturl__ = "https://github.com/DataLab-Platform/DataLab/issues/new/choose"
2222

cdl/core/baseproxy.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,11 @@ def import_h5_file(self, filename: str, reset_all: bool | None = None) -> None:
208208
"""
209209

210210
@abc.abstractmethod
211-
def open_object(self, filename: str) -> None:
212-
"""Open object from file in current panel (signal/image).
211+
def load_from_files(self, filenames: list[str]) -> None:
212+
"""Open objects from files in current panel (signals/images).
213213
214214
Args:
215-
filename (str): File name
215+
filenames: list of file names
216216
"""
217217

218218
@abc.abstractmethod
@@ -596,13 +596,13 @@ def import_h5_file(self, filename: str, reset_all: bool | None = None) -> None:
596596
"""
597597
self._cdl.import_h5_file(filename, reset_all)
598598

599-
def open_object(self, filename: str) -> None:
600-
"""Open object from file in current panel (signal/image).
599+
def load_from_files(self, filenames: list[str]) -> None:
600+
"""Open objects from files in current panel (signals/images).
601601
602602
Args:
603-
filename (str): File name
603+
filenames: list of file names
604604
"""
605-
self._cdl.open_object(filename)
605+
self._cdl.load_from_files(filenames)
606606

607607
def get_sel_object_uuids(self, include_groups: bool = False) -> list[str]:
608608
"""Return selected objects uuids.

cdl/core/gui/actionhandler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ def create_first_actions(self):
377377
# icon: fileopen_signal.svg or fileopen_image.svg
378378
icon=get_icon(f"fileopen_{self.__class__.__name__[:3].lower()}.svg"),
379379
tip=_("Open %s") % self.OBJECT_STR,
380-
triggered=self.panel.open_objects,
380+
triggered=self.panel.load_from_files,
381381
shortcut=QG.QKeySequence(QG.QKeySequence.Open),
382382
select_condition=SelectCond.always,
383383
toolbar_pos=-1,
@@ -387,7 +387,7 @@ def create_first_actions(self):
387387
# icon: filesave_signal.svg or filesave_image.svg
388388
icon=get_icon(f"filesave_{self.__class__.__name__[:3].lower()}.svg"),
389389
tip=_("Save selected %s") % self.OBJECT_STR,
390-
triggered=self.panel.save_objects,
390+
triggered=self.panel.save_to_files,
391391
shortcut=QG.QKeySequence(QG.QKeySequence.Save),
392392
select_condition=SelectCond.at_least_one,
393393
context_menu_pos=-1,

cdl/core/gui/main.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,17 +1399,14 @@ def add_object(self, obj: SignalObj | ImageObj) -> None:
13991399
raise TypeError(f"Unsupported object type {type(obj)}")
14001400

14011401
@remote_controlled
1402-
def open_object(self, filename: str) -> None:
1403-
"""Open object from file in current panel (signal/image)
1402+
def load_from_files(self, filenames: list[str]) -> None:
1403+
"""Open objects from files in current panel (signals/images)
14041404
14051405
Args:
1406-
filename (str): HDF5 filename
1407-
1408-
Returns:
1409-
None
1406+
filenames: list of filenames
14101407
"""
1411-
panel = self.tabwidget.currentWidget()
1412-
panel.open_object(filename)
1408+
panel = self.__get_current_basedatapanel()
1409+
panel.load_from_files(filenames)
14131410

14141411
# ------Other methods related to AbstractCDLControl interface
14151412
def get_version(self) -> str:

cdl/core/gui/panel/base.py

Lines changed: 70 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -551,8 +551,8 @@ def add_annotations_from_items(
551551
"""Add object annotations (annotation plot items).
552552
553553
Args:
554-
items (list): annotation plot items
555-
refresh_plot (bool | None): refresh plot. Defaults to True.
554+
items: annotation plot items
555+
refresh_plot: refresh plot. Defaults to True.
556556
"""
557557
for obj in self.objview.get_sel_objects(include_groups=True):
558558
obj.add_annotations_from_items(items)
@@ -594,8 +594,7 @@ def get_newparam_from_current(
594594
"""Get new object parameters from the current object.
595595
596596
Args:
597-
newparam (guidata.dataset.DataSet): new object parameters.
598-
If None, create a new one.
597+
newparam: new object parameters. If None, create a new one.
599598
600599
Returns:
601600
New object parameters
@@ -612,10 +611,10 @@ def new_object(
612611
"""Create a new object (signal/image).
613612
614613
Args:
615-
newparam (guidata.dataset.DataSet): new object parameters
616-
addparam (guidata.dataset.DataSet): additional parameters
617-
edit (bool): Open a dialog box to edit parameters (default: True)
618-
add_to_panel (bool): Add object to panel (default: True)
614+
newparam: new object parameters
615+
addparam: additional parameters
616+
edit: Open a dialog box to edit parameters (default: True)
617+
add_to_panel: Add object to panel (default: True)
619618
620619
Returns:
621620
New object
@@ -627,62 +626,38 @@ def set_current_object_title(self, title: str) -> None:
627626
obj.title = title
628627
self.objview.update_item(obj.uuid)
629628

630-
def open_object(
631-
self, filename: str
632-
) -> SignalObj | ImageObj | list[SignalObj | ImageObj]:
633-
"""Open object from file (signal/image), add it to DataLab and return it.
629+
def __load_from_file(self, filename: str) -> list[SignalObj] | list[ImageObj]:
630+
"""Open objects from file (signal/image), add them to DataLab and return them.
634631
635632
Args:
636-
filename (str): file name
633+
filename: file name
637634
638635
Returns:
639636
New object or list of new objects
640637
"""
641-
obj_or_objlist = self.IO_REGISTRY.read(filename)
642-
objs = obj_or_objlist if isinstance(obj_or_objlist, list) else [obj_or_objlist]
638+
objs = self.IO_REGISTRY.read(filename)
643639
for obj in objs:
644640
obj.metadata["source"] = filename
645641
self.add_object(obj, set_current=obj is objs[-1])
646642
self.selection_changed()
647-
if len(objs) == 1:
648-
return objs[0]
649643
return objs
650644

651-
def save_object(self, obj, filename: str | None = None) -> None:
652-
"""Save object to file (signal/image)"""
653-
if filename is None:
654-
basedir = Conf.main.base_dir.get()
655-
filters = self.IO_REGISTRY.get_filters(IOAction.SAVE)
656-
with save_restore_stds():
657-
filename, _filt = getsavefilename(self, _("Save as"), basedir, filters)
658-
if filename:
659-
with qt_try_loadsave_file(self.parent(), filename, "save"):
660-
Conf.main.base_dir.set(filename)
661-
self.IO_REGISTRY.write(filename, obj)
662-
663-
def handle_dropped_files(self, filenames: list[str] | None = None) -> None:
664-
"""Handle dropped files
645+
def __save_to_file(self, obj: SignalObj | ImageObj, filename: str) -> None:
646+
"""Save object to file (signal/image).
665647
666648
Args:
667-
filenames (list(str)): File names
668-
669-
Returns:
670-
None
649+
obj: object
650+
filename: file name
671651
"""
672-
h5_fnames = [fname for fname in filenames if fname.endswith(".h5")]
673-
other_fnames = sorted(list(set(filenames) - set(h5_fnames)))
674-
if h5_fnames:
675-
self.mainwindow.open_h5_files(h5_fnames, import_all=True)
676-
if other_fnames:
677-
self.open_objects(other_fnames)
652+
self.IO_REGISTRY.write(filename, obj)
678653

679-
def open_objects(
654+
def load_from_files(
680655
self, filenames: list[str] | None = None
681656
) -> list[SignalObj | ImageObj]:
682657
"""Open objects from file (signals/images), add them to DataLab and return them.
683658
684659
Args:
685-
filenames (list(str)): File names
660+
filenames: File names
686661
687662
Returns:
688663
list of new objects
@@ -698,25 +673,50 @@ def open_objects(
698673
for filename in filenames:
699674
with qt_try_loadsave_file(self.parent(), filename, "load"):
700675
Conf.main.base_dir.set(filename)
701-
objs.append(self.open_object(filename))
676+
objs += self.__load_from_file(filename)
702677
return objs
703678

704-
def save_objects(self, filenames: list[str] | None = None) -> None:
705-
"""Save selected objects to file (signal/image).
679+
def save_to_files(self, obj, filenames: list[str] | str | None = None) -> None:
680+
"""Save selected objects to files (signal/image).
706681
707682
Args:
708-
filenames (list(str)): File names
709-
710-
Returns:
711-
None
683+
filenames: File names
712684
"""
713685
objs = self.objview.get_sel_objects(include_groups=True)
714686
if filenames is None: # pragma: no cover
715687
filenames = [None] * len(objs)
716-
assert len(filenames) == len(objs)
688+
assert len(filenames) == len(
689+
objs
690+
), "Number of filenames must match number of objects"
717691
for index, obj in enumerate(objs):
718692
filename = filenames[index]
719-
self.save_object(obj, filename)
693+
if filename is None:
694+
basedir = Conf.main.base_dir.get()
695+
filters = self.IO_REGISTRY.get_filters(IOAction.SAVE)
696+
with save_restore_stds():
697+
filename, _filt = getsavefilename(
698+
self, _("Save as"), basedir, filters
699+
)
700+
if filename:
701+
with qt_try_loadsave_file(self.parent(), filename, "save"):
702+
Conf.main.base_dir.set(filename)
703+
self.__save_to_file(obj, filename)
704+
705+
def handle_dropped_files(self, filenames: list[str] | None = None) -> None:
706+
"""Handle dropped files
707+
708+
Args:
709+
filenames: File names
710+
711+
Returns:
712+
None
713+
"""
714+
h5_fnames = [fname for fname in filenames if fname.endswith(".h5")]
715+
other_fnames = sorted(list(set(filenames) - set(h5_fnames)))
716+
if h5_fnames:
717+
self.mainwindow.open_h5_files(h5_fnames, import_all=True)
718+
if other_fnames:
719+
self.load_from_files(other_fnames)
720720

721721
def exec_import_wizard(self) -> None:
722722
"""Execute import wizard"""
@@ -737,10 +737,7 @@ def import_metadata_from_file(self, filename: str | None = None) -> None:
737737
"""Import metadata from file (JSON).
738738
739739
Args:
740-
filename (str): File name
741-
742-
Returns:
743-
None
740+
filename: File name
744741
"""
745742
if filename is None: # pragma: no cover
746743
basedir = Conf.main.base_dir.get()
@@ -759,10 +756,7 @@ def export_metadata_from_file(self, filename: str | None = None) -> None:
759756
"""Export metadata to file (JSON).
760757
761758
Args:
762-
filename (str): File name
763-
764-
Returns:
765-
None
759+
filename: File name
766760
"""
767761
obj = self.objview.get_sel_objects(include_groups=True)[0]
768762
if filename is None: # pragma: no cover
@@ -782,7 +776,7 @@ def selection_changed(self, update_items: bool = False) -> None:
782776
object view.
783777
784778
Args:
785-
update_items (bool): Update plot items (default: False)
779+
update_items: Update plot items (default: False)
786780
"""
787781
selected_objects = self.objview.get_sel_objects(include_groups=True)
788782
selected_groups = self.objview.get_sel_groups()
@@ -804,7 +798,7 @@ def open_separate_view(self, oids: list[str] | None = None) -> PlotDialog | None
804798
Open separate view for visualizing selected objects
805799
806800
Args:
807-
oids (list(str)): Object IDs
801+
oids: Object IDs
808802
809803
Returns:
810804
QDialog instance
@@ -874,13 +868,13 @@ def create_new_dialog(
874868
"""Create new pop-up signal/image plot dialog.
875869
876870
Args:
877-
oids (list(str)): Object IDs
878-
edit (bool): Edit mode
879-
toolbar (bool): Show toolbar
880-
title (str): Dialog title
881-
tools (list(GuiTool)): list of tools to add to the toolbar
882-
name (str): Dialog name
883-
options (dict): Plot options
871+
oids: Object IDs
872+
edit: Edit mode
873+
toolbar: Show toolbar
874+
title: Dialog title
875+
tools: list of tools to add to the toolbar
876+
name: Dialog name
877+
options: Plot options
884878
885879
Returns:
886880
QDialog instance
@@ -939,11 +933,11 @@ def create_new_dialog_for_selection(
939933
"""Create new pop-up dialog for the currently selected signal/image.
940934
941935
Args:
942-
title (str): Dialog title
943-
name (str): Dialog name
944-
options (dict): Plot options
945-
toolbar (bool): Show toolbar
946-
tools (list(GuiTool)): list of tools to add to the toolbar
936+
title: Dialog title
937+
name: Dialog name
938+
options: Plot options
939+
toolbar: Show toolbar
940+
tools: list of tools to add to the toolbar
947941
948942
Returns:
949943
QDialog instance, selected object
@@ -1141,8 +1135,8 @@ def add_label_with_title(self, title: str | None = None) -> None:
11411135
"""Add a label with object title on the associated plot
11421136
11431137
Args:
1144-
title (str | None): Label title. Defaults to None.
1145-
If None, the title is the object title.
1138+
title: Label title. Defaults to None.
1139+
If None, the title is the object title.
11461140
"""
11471141
objs = self.objview.get_sel_objects(include_groups=True)
11481142
for obj in objs:

0 commit comments

Comments
 (0)