Skip to content

Commit 9094b63

Browse files
committed
Added simplified DataLab's object model
1 parent 52116f9 commit 9094b63

4 files changed

Lines changed: 226 additions & 0 deletions

File tree

cdlclient/remote.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
from guidata.env import execenv
3333
from guidata.userconfig import get_config_basedir
3434

35+
from cdlclient import simplemodel
3536
from cdlclient.baseproxy import SimpleBaseProxy
37+
from cdlclient.simplemodel import ImageObj, SignalObj
3638

3739
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
3840
# pylint: disable=duplicate-code
@@ -111,6 +113,9 @@ def json_to_dataset(param_data: list[str]) -> gds.DataSet:
111113
guidata DataSet
112114
"""
113115
param_module, param_clsname, param_json = param_data
116+
# Replacing DataLab's model by DataLab Simple Client's model:
117+
param_module = param_module.replace("cdl.core.model.signal", simplemodel.__name__)
118+
param_module = param_module.replace("cdl.core.model.image", simplemodel.__name__)
114119
mod = importlib.__import__(param_module, fromlist=[param_clsname])
115120
klass = getattr(mod, param_clsname)
116121
param = klass()
@@ -377,6 +382,71 @@ def calc(self, name: str, param: gds.DataSet | None = None) -> gds.DataSet:
377382
return self._cdl.calc(name)
378383
return self._cdl.calc(name, dataset_to_json(param))
379384

385+
def get_object_from_title(
386+
self, title: str, panel: str | None = None
387+
) -> SignalObj | ImageObj:
388+
"""Get object (signal/image) from title
389+
390+
Args:
391+
title (str): object
392+
panel (str | None): panel name (valid values: "signal", "image").
393+
If None, current panel is used.
394+
395+
Returns:
396+
Union[SignalObj, ImageObj]: object
397+
398+
Raises:
399+
ValueError: if object not found
400+
ValueError: if panel not found
401+
"""
402+
param_data = self._cdl.get_object_from_title(title, panel)
403+
return json_to_dataset(param_data)
404+
405+
def get_object(
406+
self,
407+
index: int | None = None,
408+
group_index: int | None = None,
409+
panel: str | None = None,
410+
) -> SignalObj | ImageObj:
411+
"""Get object (signal/image) from index.
412+
413+
Args:
414+
index (int): Object index in current panel. Defaults to None.
415+
group_index (int | None): Group index. Defaults to None.
416+
panel (str | None): Panel name. Defaults to None.
417+
418+
If ``index`` is not specified, returns the currently selected object.
419+
If ``group_index`` is not specified, return an object from the current group.
420+
If ``panel`` is not specified, return an object from the current panel.
421+
422+
Returns:
423+
Union[SignalObj, ImageObj]: object
424+
425+
Raises:
426+
IndexError: if object not found
427+
"""
428+
param_data = self._cdl.get_object(index, group_index, panel)
429+
return json_to_dataset(param_data)
430+
431+
def get_object_from_uuid(
432+
self, oid: str, panel: str | None = None
433+
) -> SignalObj | ImageObj:
434+
"""Get object (signal/image) from uuid
435+
436+
Args:
437+
oid (str): object uuid
438+
panel (str | None): panel name (valid values: "signal", "image").
439+
440+
Returns:
441+
Union[SignalObj, ImageObj]: object
442+
443+
Raises:
444+
ValueError: if object not found
445+
ValueError: if panel not found
446+
"""
447+
param_data = self._cdl.get_object_from_uuid(oid, panel)
448+
return json_to_dataset(param_data)
449+
380450
def get_object_shapes(
381451
self,
382452
index: int | None = None,

cdlclient/simplemodel.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# Licensed under the terms of the BSD 3-Clause
4+
# (see cdlclient/LICENSE for details)
5+
6+
"""
7+
DataLab Simple Model
8+
"""
9+
10+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
11+
12+
from __future__ import annotations
13+
14+
from typing import Any
15+
16+
import guidata.dataset as gds
17+
from guidata.dataset.io import JSONReader
18+
19+
ROI_KEY = "_roi_"
20+
ANN_KEY = "_ann_"
21+
22+
23+
class BaseObj:
24+
"""Base object"""
25+
26+
# This is overriden in children classes with a gds.DictItem instance:
27+
metadata: dict[str, Any] = {}
28+
29+
def __set_annotations(self, annotations: str | None) -> None:
30+
"""Set object annotations (JSON string describing annotation plot items)
31+
32+
Args:
33+
annotations (str | None): JSON string describing annotation plot items,
34+
or None to remove annotations
35+
"""
36+
if annotations is None:
37+
if ANN_KEY in self.metadata:
38+
self.metadata.pop(ANN_KEY)
39+
else:
40+
self.metadata[ANN_KEY] = annotations
41+
42+
def __get_annotations(self) -> str:
43+
"""Get object annotations (JSON string describing annotation plot items)"""
44+
return self.metadata.get(ANN_KEY, "")
45+
46+
annotations = property(__get_annotations, __set_annotations)
47+
48+
def get_annotated_shapes(self):
49+
"""Get annotated shapes"""
50+
from plotpy.io import load_items # pylint: disable=import-outside-toplevel
51+
52+
if self.annotations:
53+
return load_items(JSONReader(self.annotations))
54+
return []
55+
56+
57+
class SignalObj(gds.DataSet, BaseObj):
58+
"""Signal object (simplified version of DataLab's Signal object)"""
59+
60+
_tabs = gds.BeginTabGroup("all")
61+
62+
_datag = gds.BeginGroup("Data and metadata")
63+
title = gds.StringItem("Signal title", default="Untitled")
64+
xydata = gds.FloatArrayItem("Data", transpose=True, minmax="rows")
65+
metadata = gds.DictItem("Metadata", default={})
66+
_e_datag = gds.EndGroup("Data and metadata")
67+
68+
_unitsg = gds.BeginGroup("Titles and units")
69+
title = gds.StringItem("Signal title", default="Untitled")
70+
_tabs_u = gds.BeginTabGroup("units")
71+
_unitsx = gds.BeginGroup("X-axis")
72+
xlabel = gds.StringItem("Title", default="")
73+
xunit = gds.StringItem("Unit", default="")
74+
_e_unitsx = gds.EndGroup("X-axis")
75+
_unitsy = gds.BeginGroup("Y-axis")
76+
ylabel = gds.StringItem("Title", default="")
77+
yunit = gds.StringItem("Unit", default="")
78+
_e_unitsy = gds.EndGroup("Y-axis")
79+
_e_tabs_u = gds.EndTabGroup("units")
80+
_e_unitsg = gds.EndGroup("Titles and units")
81+
82+
_e_tabs = gds.EndTabGroup("all")
83+
84+
85+
class ImageObj(gds.DataSet, BaseObj):
86+
"""Image object (simplified version of DataLab's Image object)"""
87+
88+
_tabs = gds.BeginTabGroup("all")
89+
90+
_datag = gds.BeginGroup("Data")
91+
data = gds.FloatArrayItem("Data")
92+
metadata = gds.DictItem("Metadata", default={})
93+
_e_datag = gds.EndGroup("Data")
94+
95+
_dxdyg = gds.BeginGroup("Origin / Pixel spacing")
96+
_origin = gds.BeginGroup("Origin")
97+
x0 = gds.FloatItem("X<sub>0</sub>", default=0.0)
98+
y0 = gds.FloatItem("Y<sub>0</sub>", default=0.0).set_pos(col=1)
99+
_e_origin = gds.EndGroup("Origin")
100+
_pixel_spacing = gds.BeginGroup("Pixel spacing")
101+
dx = gds.FloatItem("Δx", default=1.0, nonzero=True)
102+
dy = gds.FloatItem("Δy", default=1.0, nonzero=True).set_pos(col=1)
103+
_e_pixel_spacing = gds.EndGroup("Pixel spacing")
104+
_e_dxdyg = gds.EndGroup("Origin / Pixel spacing")
105+
106+
_unitsg = gds.BeginGroup("Titles / Units")
107+
title = gds.StringItem("Image title", default="Untitled")
108+
_tabs_u = gds.BeginTabGroup("units")
109+
_unitsx = gds.BeginGroup("X-axis")
110+
xlabel = gds.StringItem("Title", default="")
111+
xunit = gds.StringItem("Unit", default="")
112+
_e_unitsx = gds.EndGroup("X-axis")
113+
_unitsy = gds.BeginGroup("Y-axis")
114+
ylabel = gds.StringItem("Title", default="")
115+
yunit = gds.StringItem("Unit", default="")
116+
_e_unitsy = gds.EndGroup("Y-axis")
117+
_unitsz = gds.BeginGroup("Z-axis")
118+
zlabel = gds.StringItem("Title", default="")
119+
zunit = gds.StringItem("Unit", default="")
120+
_e_unitsz = gds.EndGroup("Z-axis")
121+
_e_tabs_u = gds.EndTabGroup("units")
122+
_e_unitsg = gds.EndGroup("Titles / Units")
123+
124+
_e_tabs = gds.EndTabGroup("all")

cdlclient/tests/remoteclient_app.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ def add_additional_buttons(self):
175175
add_btn("Execute multiple commands", self.exec_multiple_cmd, 10)
176176
add_btn("Get object titles", self.get_object_titles, 10)
177177
add_btn("Get object uuids", self.get_object_uuids, 10)
178+
add_btn("Get object", self.get_object)
178179

179180
@try_send_command()
180181
def exec_multiple_cmd(self):
@@ -208,6 +209,18 @@ def get_object_uuids(self):
208209
else:
209210
self.host.log(" Empty.")
210211

212+
@try_send_command()
213+
def get_object(self):
214+
"""Get object (signal/image) at index for current panel"""
215+
if self.cdl is not None:
216+
titles = self.cdl.get_object_titles()
217+
if titles:
218+
obj = self.cdl.get_object()
219+
self.host.log(f"Object '{obj.title}'")
220+
self.host.log(str(obj))
221+
else:
222+
self.host.log("🏴‍☠️ Object list is empty!")
223+
211224
def add_signals(self):
212225
"""Add signals to DataLab"""
213226
if self.cdl is not None:
@@ -266,6 +279,9 @@ def test_remote_client():
266279
window.get_object_titles()
267280
with qt_wait_print(dt, "Getting object uuids"):
268281
window.get_object_uuids()
282+
with qt_wait_print(dt, "Getting object"):
283+
window.cdl.select_objects([0])
284+
window.get_object()
269285
with qt_wait_print(dt, "Adding signals"):
270286
window.add_signals()
271287
with qt_wait_print(dt, "Adding images"):

doc/api.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,19 @@ Base proxy
1212

1313
.. autoclass:: cdlclient.SimpleBaseProxy
1414
:members:
15+
16+
Simple model
17+
^^^^^^^^^^^^
18+
19+
DataLab Simple Client provides a simple model for accessing the DataLab
20+
objects.
21+
22+
.. warning::
23+
24+
This model is not complete and does not cover all the DataLab features.
25+
26+
.. autoclass:: cdlclient.simplemodel.SignalObj
27+
:members:
28+
29+
.. autoclass:: cdlclient.simplemodel.ImageObj
30+
:members:

0 commit comments

Comments
 (0)