-
-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move interface definition from base class to protocol #162
Changes from 9 commits
62f6b97
a8f7cbd
e735a23
38cdcd7
9ad7797
aa84748
9baba2b
e7e5f5c
9dffe05
75bc9df
367d61f
f23ed64
fc0056f
e9afc48
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,12 +23,6 @@ | |
|
||
__all__ = ['ImageWidget'] | ||
|
||
# Allowed locations for cursor display | ||
ALLOWED_CURSOR_LOCATIONS = ['top', 'bottom', None] | ||
|
||
# List of marker names that are for internal use only | ||
RESERVED_MARKER_SET_NAMES = ['all'] | ||
|
||
|
||
class ImageWidget(ipyw.VBox): | ||
""" | ||
|
@@ -55,6 +49,11 @@ class ImageWidget(ipyw.VBox): | |
correct value to use.* | ||
|
||
""" | ||
# Allowed locations for cursor display | ||
ALLOWED_CURSOR_LOCATIONS = ('top', 'bottom', None) | ||
|
||
# List of marker names that are for internal use only | ||
RESERVED_MARKER_SET_NAMES = ('all', ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can these be imported from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, they should only be in one place. It feels a little inelegant to do it as constants. Will update. |
||
|
||
def __init__(self, logger=None, image_width=500, image_height=500, | ||
pixel_coords_offset=0, **kwargs): | ||
|
@@ -565,6 +564,9 @@ def get_markers(self, x_colname='x', y_colname='y', | |
del table[skycoord_colname] | ||
tables.append(table) | ||
|
||
if len(tables) == 0: | ||
return None | ||
|
||
stacked = vstack(tables, join_type='exact') | ||
|
||
if coordinates: | ||
|
@@ -648,11 +650,11 @@ def _validate_marker_name(self, marker_name): | |
""" | ||
Raise an error if the marker_name is not allowed. | ||
""" | ||
if marker_name in RESERVED_MARKER_SET_NAMES: | ||
if marker_name in self.RESERVED_MARKER_SET_NAMES: | ||
raise ValueError('The marker name {} is not allowed. Any name is ' | ||
'allowed except these: ' | ||
'{}'.format(marker_name, | ||
', '.join(RESERVED_MARKER_SET_NAMES))) | ||
', '.join(self.RESERVED_MARKER_SET_NAMES))) | ||
|
||
def add_markers(self, table, x_colname='x', y_colname='y', | ||
skycoord_colname='coord', use_skycoord=False, | ||
|
@@ -888,7 +890,7 @@ def cursor(self, val): | |
else: | ||
raise ValueError('Invalid value {} for cursor.' | ||
'Valid values are: ' | ||
'{}'.format(val, ALLOWED_CURSOR_LOCATIONS)) | ||
'{}'.format(val, self.ALLOWED_CURSOR_LOCATIONS)) | ||
self._cursor = val | ||
|
||
@property | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
from typing import Protocol, runtime_checkable, Any | ||
from abc import abstractmethod | ||
|
||
# Allowed locations for cursor display | ||
ALLOWED_CURSOR_LOCATIONS = ['top', 'bottom', None] | ||
|
||
# List of marker names that are for internal use only | ||
RESERVED_MARKER_SET_NAMES = ['all'] | ||
|
||
__all__ = [ | ||
'ImageViewerInterface', | ||
'ALLOWED_CURSOR_LOCATIONS', | ||
'RESERVED_MARKER_SET_NAMES' | ||
] | ||
|
||
|
||
@runtime_checkable | ||
class ImageViewerInterface(Protocol): | ||
# This are attributes, not methods. The type annotations are there | ||
# to make sure Protocol knows they are attributes. Python does not | ||
# do any checking at all of these types. | ||
Comment on lines
+20
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could enforce it with third-party type checker package but is that going too far? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, although it's a bit odd, when I think about it I like the cleanness of the interface not doing any checking but the APITest class doing it. So maybe if we implemented type-checking it could use this information but actually do the type checking when the APITest is invoked by a backend? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I should clarify -- I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if we want to enforce run-time checking. Might be too controversial. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed -- and the way we have set things up I don't think we can. |
||
click_center: bool | ||
click_drag: bool | ||
scroll_pan: bool | ||
image_width: int | ||
image_height: int | ||
zoom_level: float | ||
marker: Any | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These were omitted the first time through:
|
||
cuts: Any | ||
stretch: str | ||
# viewer: Any | ||
|
||
# Allowed locations for cursor display | ||
ALLOWED_CURSOR_LOCATIONS = ('top', 'bottom', None) | ||
|
||
# List of marker names that are for internal use only | ||
RESERVED_MARKER_SET_NAMES = ('all', ) | ||
|
||
# The methods, grouped loosely by purpose | ||
|
||
# Methods for loading data | ||
@abstractmethod | ||
def load_fits(self, file): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know you say API change is out of scope, but I want to note that we agreed at Astropy Coordination Meeting 2023 to change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks -- I added this to the agenda in the running notes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I opened #163 |
||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def load_array(self, array): | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def load_nddata(self, data): | ||
raise NotImplementedError | ||
|
||
# Saving contents of the view and accessing the view | ||
@abstractmethod | ||
def save(self, filename): | ||
raise NotImplementedError | ||
|
||
# Marker-related methods | ||
@abstractmethod | ||
def start_marking(self, marker_name=None): | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def stop_marking(self, clear_markers=False): | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def add_markers(self, table, x_colname='x', y_colname='y', | ||
skycoord_colname='coord', use_skycoord=False, | ||
marker_name=None): | ||
raise NotImplementedError | ||
|
||
# @abstractmethod | ||
# def remove_all_markers(self): | ||
# raise NotImplementedError | ||
|
||
@abstractmethod | ||
def reset_markers(self): | ||
raise NotImplementedError | ||
|
||
# @abstractmethod | ||
# def remove_markers_by_name(self, marker_name=None): | ||
# raise NotImplementedError | ||
|
||
@abstractmethod | ||
def remove_markers(self, marker_name=None): | ||
raise NotImplementedError | ||
|
||
# @abstractmethod | ||
# def get_all_markers(self): | ||
# raise NotImplementedError | ||
|
||
@abstractmethod | ||
def get_markers(self, x_colname='x', y_colname='y', | ||
skycoord_colname='coord', | ||
marker_name=None): | ||
raise NotImplementedError | ||
|
||
# Methods that modify the view | ||
@abstractmethod | ||
def center_on(self): | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def offset_by(self): | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def zoom(self): | ||
raise NotImplementedError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you say API change is out of scope, but I want to note that we agreed at Astropy Coordination Meeting 2023 to move backend specific implementations (ginga, bqplot, Firefly, etc) out of
astrowidgets
repo.https://docs.google.com/document/d/1sBImbw1p4mM3VYWGQJDRbj2N3uqP5Ed_Wfb8BDjJj2o/edit#heading=h.8ah2ugluz5xr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I opened #164 and #165