Skip to content

Commit

Permalink
🥅 Add ready decorator to prevent certains actions before Aladin Lite …
Browse files Browse the repository at this point in the history
…is ready
  • Loading branch information
Xen0Xys committed Aug 2, 2024
1 parent 06ae1c5 commit 1b41d1a
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 2 deletions.
1 change: 1 addition & 0 deletions js/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ function initAladinLite(model, el) {
y: twoAxisFoV[1],
});
model.set("_wcs", aladin.getViewWCS());
model.set("_ready", true);
model.save_changes();

el.appendChild(aladinDiv);
Expand Down
8 changes: 8 additions & 0 deletions src/ipyaladin/utils/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,11 @@ class WidgetReducedError(ValueError):
def __init__(self, message: str) -> None:
self.message = message
super(WidgetReducedError, self).__init__(message)


class WidgetNotReadyError(ValueError):
"""Error raised when a widget is not ready to be used."""

def __init__(self, message: str) -> None:
self.message = message
super(WidgetNotReadyError, self).__init__(message)
72 changes: 70 additions & 2 deletions src/ipyaladin/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@
import numpy as np
import traitlets

from .utils.exceptions import WidgetCommunicationError, WidgetReducedError
from .utils.exceptions import (
WidgetCommunicationError,
WidgetReducedError,
WidgetNotReadyError,
)
from .utils._coordinate_parser import parse_coordinate_string

try:
Expand Down Expand Up @@ -72,6 +76,48 @@
]


def is_ready(func: Callable) -> Callable:
"""Check if the widget is ready to execute a function.
Parameters
----------
func : Callable
The function to decorate.
Returns
-------
Callable
The decorated function.
"""

def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
"""Check if the widget is ready to execute a function.
Parameters
----------
self : any
The widget object.
*args : any
The arguments of the function.
**kwargs : any
The keyword arguments of the function.
Returns
-------
any
The result of the function if the widget is ready.
"""
if not getattr(self, "_ready", False):
raise WidgetNotReadyError(
"The object is not ready to execute this function."
)
return func(self, *args, **kwargs)

return wrapper


class Aladin(anywidget.AnyWidget):
"""Aladin Lite widget.
Expand Down Expand Up @@ -109,7 +155,7 @@ class Aladin(anywidget.AnyWidget):
).tag(sync=True, init_option=True)
projection = Unicode(
"SIN",
help=("The projection for the view. The keywords follow the FITS standard."),
help="The projection for the view. The keywords follow the FITS standard.",
).tag(sync=True, init_option=True)
samp = Bool(False, help="Wether to allow sending data via the SAMP protocol.").tag(
sync=True, init_option=True, only_init=True
Expand Down Expand Up @@ -212,6 +258,13 @@ class Aladin(anywidget.AnyWidget):
"to convert the view to an astropy.HDUList",
).tag(sync=True)

_ready = Bool(
False,
help="A private trait that stores if the widget is ready.",
).tag(
sync=True,
)

# Temporary traitlets for widget size problem
_is_reduced = Bool(
False,
Expand Down Expand Up @@ -393,6 +446,7 @@ def target(self, target: Union[str, SkyCoord]) -> None:
}
)

@is_ready
def get_view_as_fits(self) -> HDUList:
"""Get the base layer of the widget as an astropy HDUList object.
Expand Down Expand Up @@ -426,6 +480,7 @@ def get_view_as_fits(self) -> HDUList:
) from e
return fits

@is_ready
def add_catalog_from_URL(
self, votable_URL: str, votable_options: Optional[dict] = None
) -> None:
Expand All @@ -447,6 +502,7 @@ def add_catalog_from_URL(
}
)

@is_ready
def add_fits(self, fits: Union[str, Path, HDUList], **image_options: any) -> None:
"""Load a FITS file into the widget.
Expand Down Expand Up @@ -477,6 +533,7 @@ def add_fits(self, fits: Union[str, Path, HDUList], **image_options: any) -> Non

# MOCs

@is_ready
def add_moc(self, moc: any, **moc_options: any) -> None:
"""Add a MOC to the Aladin-Lite widget.
Expand Down Expand Up @@ -527,6 +584,7 @@ def add_moc(self, moc: any, **moc_options: any) -> None:
"library with 'pip install mocpy'."
) from imp

@is_ready
def add_moc_from_URL(
self, moc_URL: str, moc_options: Optional[dict] = None
) -> None:
Expand All @@ -551,6 +609,7 @@ def add_moc_from_URL(
moc_options = {}
self.add_moc(moc_URL, **moc_options)

@is_ready
def add_moc_from_dict(
self, moc_dict: dict, moc_options: Optional[dict] = None
) -> None:
Expand All @@ -576,6 +635,7 @@ def add_moc_from_dict(
moc_options = {}
self.add_moc(moc_dict, **moc_options)

@is_ready
def add_table(self, table: Union[QTable, Table], **table_options: any) -> None:
"""Load a table into the widget.
Expand All @@ -596,6 +656,7 @@ def add_table(self, table: Union[QTable, Table], **table_options: any) -> None:
buffers=[table_bytes.getvalue()],
)

@is_ready
def add_graphic_overlay_from_region(
self,
region: SupportedRegion,
Expand Down Expand Up @@ -676,6 +737,7 @@ def add_graphic_overlay_from_region(
}
)

@is_ready
def add_overlay_from_stcs(
self, stc_string: Union[List[str], str], **overlay_options: any
) -> None:
Expand All @@ -699,6 +761,7 @@ def add_overlay_from_stcs(
)
self.add_graphic_overlay_from_stcs(stc_string, **overlay_options)

@is_ready
def add_graphic_overlay_from_stcs(
self, stc_string: Union[List[str], str], **overlay_options: any
) -> None:
Expand Down Expand Up @@ -745,6 +808,7 @@ def get_JPEG_thumbnail(self) -> None:
"""
self.send({"event_name": "get_JPG_thumbnail"})

@is_ready
def set_color_map(self, color_map_name: str) -> None:
"""Change the color map of the Aladin Lite widget.
Expand All @@ -756,6 +820,7 @@ def set_color_map(self, color_map_name: str) -> None:
"""
self.send({"event_name": "change_colormap", "colormap": color_map_name})

@is_ready
def selection(self, selection_type: str = "rectangle") -> None:
"""Trigger the selection tool.
Expand All @@ -771,6 +836,7 @@ def selection(self, selection_type: str = "rectangle") -> None:
raise ValueError("selection_type must be 'circle' or 'rectangle'")
self.send({"event_name": "trigger_selection", "selection_type": selection_type})

@is_ready
def rectangular_selection(self) -> None:
"""Trigger the rectangular selection tool.
Expand All @@ -787,6 +853,7 @@ def rectangular_selection(self) -> None:

# Adding a listener

@is_ready
def set_listener(self, listener_type: str, callback: Callable) -> None:
"""Set a listener for an event to the widget.
Expand All @@ -813,6 +880,7 @@ def set_listener(self, listener_type: str, callback: Callable) -> None:
"'object_clicked', 'click' or 'select'"
)

@is_ready
def add_listener(self, listener_type: str, callback: Callable) -> None:
"""Add a listener to the widget. Use set_listener instead.
Expand Down

0 comments on commit 1b41d1a

Please sign in to comment.