diff --git a/stellarphot/io/__init__.py b/stellarphot/io/__init__.py index 94ecd83a..06033810 100644 --- a/stellarphot/io/__init__.py +++ b/stellarphot/io/__init__.py @@ -1,4 +1,4 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst from .aij import * -from .tess_submission import * +from .tess import * diff --git a/stellarphot/io/tess.py b/stellarphot/io/tess.py new file mode 100644 index 00000000..4cb7a7be --- /dev/null +++ b/stellarphot/io/tess.py @@ -0,0 +1,253 @@ +from dataclasses import dataclass +from pathlib import Path +import re + +from astropy.coordinates import SkyCoord +from astropy.table import Table +from astropy.time import Time +from astropy import units as u +from astropy.utils.data import download_file + +from astroquery.mast import Catalogs + +import requests + +from stellarphot.analysis.exotic import get_tic_info + +__all__ = ["TessSubmission", "TOI", "TessTargetFile"] + +# Makes me want to vomit, but.... +DEFAULT_TABLE_LOCATION = "who.the.fuck.knows" +TOI_TABLE_URL = "https://exofop.ipac.caltech.edu/tess/download_toi.php?output=csv" +TIC_regex = re.compile(r"[tT][iI][cC][^\d]?(?P\d+)(?P\.\d\d)?") + + +@dataclass +class TessSubmission: + telescope_code: str + filter: str + utc_start: int + tic_id: int + planet_number: int + + def __post_init__(self, *args, **kwargs): + self._tic_info = None + + @classmethod + def from_header(cls, header, telescope_code="", planet=0): + # Set some default dummy values + + tic_id = 0 + filter = "" + fails = {} + try: + dateobs = header['date-obs'] + except KeyError: + fails["utc_start"] = "UTC date of first image" + else: + dateobs = dateobs.split("T")[0].replace("-", "") + + try: + filter = header['filter'] + except KeyError: + fails["filter"] = ("filter/passband") + + try: + obj = header['object'] + except KeyError: + fails['tic_id'] = "TIC ID number" + else: + result = TIC_regex.match(obj) + if result: + tic_id = int(result.group("star")) + # Explicit argument overrules the header + if result.group("planet") and not planet: + # Drop the leading period from the match + planet = int(result.group("planet")[1:]) + else: + # No star from the object after all + fails['tic_id'] = "TIC ID number" + + fail_msg = "" + fail = [] + for k, v in fails.items(): + fail.append(f"Unable to determine {k}, {v}, from header.") + + fail = "\n".join(fail) + + if fail: + raise ValueError(fail) + + return cls(utc_start=dateobs, + filter=filter, + telescope_code=telescope_code, + tic_id=tic_id, + planet_number=planet) + + def _valid_tele_code(self): + return len(self.telescope_code) > 0 + + def _valid_planet(self): + return self.planet_number > 0 + + def _valid_tic_num(self): + return self.tic_id < 10_000_000_000 + + def _valid(self): + """ + Check whether the information so far is valid, meaning: + + Telescope code is not the empty string + + Planet number is not zero + + TIC ID is not more than 10 digits + """ + valid = ( + self._valid_tele_code() and + self._valid_planet() and + self._valid_tic_num() + ) + return valid + + @property + def base_name(self): + if self._valid(): + pieces = [ + f"TIC{self.tic_id}-{self.planet_number:02d}", + self.utc_start, + self.telescope_code, + self.filter + ] + return "_".join(pieces) + + @property + def seeing_profile(self): + return self.base_name + "_seeing-profile.png" + + @property + def field_image(self): + return self.base_name + "_field.png" + + @property + def field_image_zoom(self): + return self.base_name + "_field-zoom.png" + + @property + def apertures(self): + return self.base_name + "_measurements.apertures" + + @property + def tic_coord(self): + if not self._tic_info: + self._tic_info = get_tic_info(self.tic_id) + return SkyCoord(ra=self._tic_info['ra'][0], dec=self._tic_info['dec'][0], unit='degree') + + def invalid_parts(self): + if self._valid(): + return + + if not self._valid_tele_code(): + print(f"Telescope code {self.telescope_code} is not valid") + + if not self._valid_planet(): + print(f"Planet number {self.planet_number} is not valid") + + if not self._valid_tic_num(): + print(f"TIC ID {self.tic_id} is not valid.") + + +class TOI: + def __init__(self, tic_id, toi_table=DEFAULT_TABLE_LOCATION, allow_download=True): + path = Path(toi_table) + if not path.is_file(): + if not allow_download: + raise ValueError(f"File {toi_table} not found.") + toi_table = download_file(TOI_TABLE_URL, cache=True, show_progress=True, timeout=60) + + self._toi_table = Table.read(toi_table, format="ascii.csv") + self._toi_table = self._toi_table[self._toi_table['TIC ID'] == tic_id] + if len(self._toi_table) != 1: + raise RuntimeError(f"Found {len(self._toi_table)} rows in table, expected one.") + self._tic_info = get_tic_info(tic_id) + + @property + def tess_mag(self): + return self._toi_table['TESS Mag'][0] + + @property + def tess_mag_error(self): + return self._toi_table['TESS Mag err'][0] + + @property + def depth(self): + """ + Depth, parts per thousand. + """ + return self._toi_table['Depth (ppm)'][0] / 1000 + + @property + def depth_error(self): + """ + Error in depth, parts per thousand. + """ + return self._toi_table['Depth (ppm) err'][0] / 1000 + + @property + def epoch(self): + return Time(self._toi_table['Epoch (BJD)'][0], scale='tdb', format='jd') + + @property + def epoch_error(self): + return self._toi_table['Epoch (BJD) err'][0] * u.day + + @property + def period(self): + return self._toi_table['Period (days)'][0] * u.day + + @property + def period_error(self): + return self._toi_table['Period (days) err'][0] * u.day + + @property + def duration(self): + return self._toi_table['Duration (hours)'][0] * u.hour + + @property + def duration_error(self): + return self._toi_table['Duration (hours) err'][0] * u.hour + + @property + def coord(self): + return SkyCoord(ra=self._tic_info['ra'][0], dec=self._tic_info['dec'][0], unit='degree') + + +@dataclass +class TessTargetFile: + coord : SkyCoord + magnitude : float + depth : float + file : str = "aperture_locations.csv" + aperture_server : str = "https://www.astro.louisville.edu/" + + def __post_init__(self): + self._path = Path(self.file) + self.target_file = self._retrieve_target_file() + self.target_table = self._build_table() + + def _retrieve_target_file(self): + params = dict( + ra = self.coord.ra.to_string(unit='hour', decimal=False, sep=":"), + dec = self.coord.dec.to_string(unit='degree', decimal=False, sep=":"), + mag=self.magnitude, + depth=self.depth + ) + result = requests.get(self.aperture_server + "cgi-bin/gaia_to_aij/upload_request.cgi", params=params) + links = re.search('href="(.+)"', result.text.replace('\n', ''), ) + download_link = self.aperture_server + links[1] + target_file_contents = requests.get(download_link) + with open(self._path, "w") as f: + f.write(target_file_contents.text) + + + def _build_table(self): + from stellarphot.visualization.comparison_functions import read_file + + self.table = read_file(self._path) diff --git a/stellarphot/io/tess_submission.py b/stellarphot/io/tess_submission.py deleted file mode 100644 index dbcd5783..00000000 --- a/stellarphot/io/tess_submission.py +++ /dev/null @@ -1,117 +0,0 @@ -from dataclasses import dataclass -import re - -__all__ = ["TessSubmission"] - -TIC_regex = re.compile(r"[tT][iI][cC][^\d]?(?P\d+)(?P\.\d\d)?") - - -@dataclass -class TessSubmission: - telescope_code: str - filter: str - utc_start: int - tic_id: int - planet_number: int - - @classmethod - def from_header(cls, header, telescope_code="", planet=0): - # Set some default dummy values - - tic_id = 0 - filter = "" - fails = {} - try: - dateobs = header['date-obs'] - except KeyError: - fails["utc_start"] = "UTC date of first image" - else: - dateobs = dateobs.split("T")[0].replace("-", "") - - try: - filter = header['filter'] - except KeyError: - fails["filter"] = ("filter/passband") - - try: - obj = header['object'] - except KeyError: - fails['tic_id'] = "TIC ID number" - else: - result = TIC_regex.match(obj) - if result: - tic_id = int(result.group("star")) - # Explicit argument overrules the header - if result.group("planet") and not planet: - # Drop the leading period from the match - planet = int(result.group("planet")[1:]) - else: - # No star from the object after all - fails['tic_id'] = "TIC ID number" - - fail_msg = "" - fail = [] - for k, v in fails.items(): - fail.append(f"Unable to determine {k}, {v}, from header.") - - fail = "\n".join(fail) - - if fail: - raise ValueError(fail) - - return cls(utc_start=dateobs, - filter=filter, - telescope_code=telescope_code, - tic_id=tic_id, - planet_number=planet) - - def _valid_tele_code(self): - return len(self.telescope_code) > 0 - - def _valid_planet(self): - return self.planet_number > 0 - - def _valid_tic_num(self): - return self.tic_id < 10_000_000_000 - - def _valid(self): - """ - Check whether the information so far is valid, meaning: - + Telescope code is not the empty string - + Planet number is not zero - + TIC ID is not more than 10 digits - """ - valid = ( - self._valid_tele_code() and - self._valid_planet() and - self._valid_tic_num() - ) - return valid - - @property - def base_name(self): - if self._valid(): - pieces = [ - f"TIC{self.tic_id}-{self.planet_number:02d}", - self.utc_start, - self.telescope_code, - self.filter - ] - return "_".join(pieces) - - @property - def seeing_profile(self): - return self.base_name + "_seeing-profile" - - def invalid_parts(self): - if self._valid(): - return - - if not self._valid_tele_code(): - print(f"Telescope code {self.telescope_code} is not valid") - - if not self._valid_planet(): - print(f"Planet number {self.planet_number} is not valid") - - if not self._valid_tic_num(): - print(f"TIC ID {self.tic_id} is not valid.") diff --git a/stellarphot/io/tests/test_tess_submission.py b/stellarphot/io/tests/test_tess_submission.py index a6c766f2..321df982 100644 --- a/stellarphot/io/tests/test_tess_submission.py +++ b/stellarphot/io/tests/test_tess_submission.py @@ -2,7 +2,7 @@ import pytest -from stellarphot.io.tess_submission import TessSubmission +from stellarphot.io.tess import TessSubmission GOOD_HEADER = { "date-obs": "2022-06-04T05:44:28.010", @@ -49,7 +49,7 @@ def test_base_name(): def test_seeing_profile(): tsub = TessSubmission.from_header(GOOD_HEADER_WITH_PLANET, telescope_code="ABS") - assert tsub.seeing_profile == "TIC237205154-01_20220604_ABS_ip_seeing-profile" + assert tsub.seeing_profile == "TIC237205154-01_20220604_ABS_ip_seeing-profile.png" def test_valid_method(): diff --git a/stellarphot/visualization/comparison_functions.py b/stellarphot/visualization/comparison_functions.py index ed903777..0e567c1c 100644 --- a/stellarphot/visualization/comparison_functions.py +++ b/stellarphot/visualization/comparison_functions.py @@ -12,6 +12,8 @@ from astropy import units as u from astropy.nddata import CCDData from astropy import units as u +from astropy.coordinates.name_resolve import NameResolveError + try: from astrowidgets import ImageWidget @@ -19,13 +21,17 @@ from astrowidgets.ginga import ImageWidget from stellarphot.differential_photometry import * +from stellarphot.io import TessSubmission, TOI, TessTargetFile from stellarphot.photometry import * from stellarphot.visualization.seeing_profile_functions import set_keybindings +from stellarphot.visualization.fits_opener import FitsOpener __all__ = ['read_file', 'set_up', 'match', 'mag_scale', 'in_field', 'make_markers', 'wrap'] +DESC_STYLE = {"description_width": "initial"} + def read_file(radec_file): """ @@ -161,6 +167,7 @@ def make_markers(iw, ccd, RD, vsx, ent, """ iw.load_nddata(ccd) iw.zoom_level = 'fit' + try: iw.reset_markers() except AttributeError: @@ -181,13 +188,11 @@ def make_markers(iw, ccd, RD, vsx, ent, iw.marker = {'type': 'circle', 'color': 'blue', 'radius': 10} iw.add_markers(vsx, skycoord_colname='coords', use_skycoord=True, marker_name='VSX') - iw.marker = {'type': 'circle', 'color': 'red', 'radius': 10} iw.add_markers(ent, skycoord_colname='coords', use_skycoord=True, marker_name='APASS comparison') iw.marker = {'type': 'cross', 'color': 'red', 'radius': 6} - def wrap(imagewidget, outputwidget): """ Make the bits that let you click to select/deselect comparisons @@ -232,14 +237,13 @@ def cb(viewer, event, data_x, data_y): else: print('sorry try again') imagewidget._viewer.onscreen_message('Click closer to a star') - print(all_table['marker name'][index]) - print(x, y, ra, dec, out_skycoord) + return cb class ComparisonViewer: def __init__(self, - image, + file="", directory='.', target_mag=10, bright_mag_limit=8, @@ -250,31 +254,50 @@ def __init__(self, self._label_name = 'labels' self._circle_name = 'target circle' + self._file_chooser = FitsOpener() + + self._directory = directory + self.target_mag = target_mag + self.bright_mag_limit = bright_mag_limit + self.dim_mag_limit = dim_mag_limit + self.targets_from_file = targets_from_file + self.tess_submission = None + self._tess_object_info = None + self.target_coord = object_coordinate + + self.box, self.iw = self._viewer() + + self.aperture_output_file = aperture_output_file + + if file: + self._file_chooser.set_file(file, directory=directory) + self._set_file(None) + + self._make_observers() + + def _init(self): + """ + Some initialization needs to be defered until a file is chosen. + """ + if self.tess_submission is not None: + self._tess_object_info.layout.visibility = "visible" self.ccd, self.vsx = \ - set_up(image, - directory_with_images=directory + set_up(self._file_chooser.path.name, + directory_with_images=self._file_chooser.path.parent ) apass, vsx_apass_angle, targets_apass_angle = match(self.ccd, - targets_from_file, + self.targets_from_file, self.vsx) - apass_good_coord, good_stars = mag_scale(target_mag, apass, vsx_apass_angle, + apass_good_coord, good_stars = mag_scale(self.target_mag, apass, vsx_apass_angle, targets_apass_angle, - brighter_dmag=target_mag - bright_mag_limit, - dimmer_dmag=dim_mag_limit - target_mag) + brighter_dmag=self.target_mag - self.bright_mag_limit, + dimmer_dmag=self.dim_mag_limit - self.target_mag) apass_comps = in_field(apass_good_coord, self.ccd, apass, good_stars) - - self.box, self.iw = self._viewer() - - make_markers(self.iw, self.ccd, targets_from_file, self.vsx, apass_comps, - name_or_coord=object_coordinate) - - self.target_coord = object_coordinate - self.aperture_output_file = aperture_output_file - - self._make_observers() + make_markers(self.iw, self.ccd, self.targets_from_file, self.vsx, apass_comps, + name_or_coord=self.target_coord) @property def variables(self): @@ -285,11 +308,72 @@ def variables(self): our_vsx['star_id'] = comp_table['star_id'][new_vsx_mark] return our_vsx + def _set_object(self): + """ + Try to automatically set object name immediately after file is chosen. + """ + try: + self.object_name.value = self._file_chooser.header['object'] + except KeyError: + # No object, will show empty box for name + self.object_name.disabled = False + + # We have a name, try to get coordinates from it + try: + self.target_coord = SkyCoord.from_name(self.object_name.value) + except NameResolveError: + pass + + # Maybe this is a tess object? + try: + self.tess_submission = TessSubmission.from_header( + self._file_chooser.header, + telescope_code="Paul-P-Feder-0.4m", + planet=1 + ) + except ValueError: + # Guess not, time to turn on the coordinates box + # self._turn_on_coordinates() + self.tess_submission = None + self.toi_info = None + self.targets_from_file = None + self._tess_object_info.layout.visibility = "hidden" + self.tess_save_toggle.value = False + self.tess_save_toggle.disabled = True + else: + self.tess_save_toggle.disabled = False + self.toi_info = TOI(self.tess_submission.tic_id) + + self._target_file_info = TessTargetFile(self.toi_info.coord, + self.toi_info.tess_mag, + self.toi_info.depth) + + self.target_coord = self.tess_submission.tic_coord + self._tess_object_info.mag.value = self.toi_info.tess_mag + self._tess_object_info.depth.value = self.toi_info.depth + self._tess_object_info.layout.visibility = "visible" + self.targets_from_file = self._target_file_info.table + + def _set_file(self, change): + self._set_object() + self._init() + self._update_tess_save_names() + + def _save_toggle_action(self, change): + activated = change['new'] + + if activated: + self._tess_save_box.layout.visibility = "visible" + else: + self._tess_save_box.layout.visibility = "hidden" + def _make_observers(self): self._show_labels_button.observe(self._show_label_button_handler, names='value') self._save_var_info.on_click(self._save_variables_to_file) self._save_aperture_file.on_click(self._save_aperture_to_file) + self._file_chooser.register_callback(self._set_file) + self.tess_save_toggle.observe(self._save_toggle_action, "value") def _save_variables_to_file(self, button=None, filename=''): if not filename: @@ -310,7 +394,7 @@ def _show_label_button_handler(self, change): def _save_aperture_to_file(self, button=None, filename=''): if not filename: filename = self.aperture_output_file - print(filename) + self.generate_table().write(filename) def _make_control_bar(self): @@ -334,6 +418,57 @@ def _make_control_bar(self): return controls + def _make_tess_object_info(self): + """ + Make the controls for the TESS mag and depth used to look up GAIA target list. + """ + tess_object_info = ipw.HBox() + tess_mag = ipw.FloatText(description="TESS mag") + tess_depth = ipw.FloatText(description="Depth (ppt)") + tess_object_info.children = [tess_mag, tess_depth] + self._tess_object_info = tess_object_info + self._tess_object_info.mag = tess_mag + self._tess_object_info.depth = tess_depth + if self.tess_submission is None: + self._tess_object_info.layout.visibility = "hidden" + + def _make_tess_save_box(self): + self.tess_save_toggle = ipw.ToggleButton(description="TESS files...", + disabled=True) + self._tess_save_box = ipw.VBox() + self._tess_save_box.layout.visibility = "hidden" + + scope_name = ipw.Text(description="Telescope code", + value="Paul-P-Feder-0.4m", + style=DESC_STYLE) + + planet_num = ipw.IntText(description="Planet", value=1) + + dumb = [] + dumb2 = [] + + for save in ["Full field of view", "Zoomed filed of view"]: # , "Aperture file"]: + box = ipw.HBox() + title = ipw.HTML(value=f"{save} file name") + label = ipw.Label(value="") + box.children = (title, label) + dumb.append(label) + dumb2.append(box) + + # self._field_name, self._zoom_name, self._aper_name = dumb + self._field_name, self._zoom_name = dumb + self.save_files = ipw.Button(description="Save") + self._tess_save_box.children = [scope_name, planet_num] + dumb2 + [self.save_files] + + def _update_tess_save_names(self): + if self.tess_submission is not None: + self._field_name.value = self.tess_submission.field_image + self._zoom_name.value = self.tess_submission.field_image_zoom + # self._aper_name.value = self.tess_submission.apertures + else: + self._field_name.value = "" + self._zoom_name.value = "" + def _viewer(self): header = ipw.HTML(value="""

Click and drag or use arrow keys to pan, use +/- keys to zoom

@@ -356,14 +491,27 @@ def _viewer(self): bind_map.map_event(None, ('shift',), 'ms_left', 'cursor') gvc.add_callback('cursor-down', wrap(iw, out)) + self.object_name = ipw.Text(description="Object", disabled=True) controls = self._make_control_bar() + self._make_tess_object_info() + self._make_tess_save_box() box = ipw.VBox() inner_box = ipw.HBox() inner_box.children = [iw, legend] - box.children = [header, inner_box, controls] + box.children = [self._file_chooser.file_chooser, self.object_name, self._tess_object_info, header, + inner_box, controls, self.tess_save_toggle, self._tess_save_box] return box, iw + def save_tess_files(self): + if self._field_name.value: + self.tess_field_view() + self.iw.save(self._field_name.value, overwrite=True) + + if self._zoom_name.value: + self.tess_field_zoom_view() + self.iw.save(self._zoom_name.value, overwrite=True) + def generate_table(self): try: all_table = self.iw.get_all_markers() @@ -407,6 +555,7 @@ def show_labels(self): plot_names = [] comp_table = self.generate_table() + original_mark = self.iw._marker for star in comp_table: star_id = star['star_id'] if star['marker name'] == 'TESS Targets': @@ -430,27 +579,65 @@ def show_labels(self): label = f'U{star_id}' print(f"Unrecognized marker name: {star['marker name']}") plot_names.append(label) + self.iw._marker = original_mark def remove_labels(self): try: - self.iw.remove_markers(marker_name=self._label_name) - except AttributeError: - self.iw.remove_markers_by_name(marker_name=self._label_name) + try: + self.iw.remove_markers(marker_name=self._label_name) + except AttributeError: + self.iw.remove_markers_by_name(marker_name=self._label_name) + except ValueError: + # No labels, keep going + pass def show_circle(self, radius=2.5 * u.arcmin, pixel_scale=0.56 * u.arcsec / u.pixel): radius_pixels = np.round((radius / pixel_scale).to(u.pixel).value, decimals=0) + orig_marker = self.iw.marker self.iw.marker = {'color': 'yellow', 'radius': radius_pixels, 'type': 'circle'} self.iw.add_markers(Table(data=[[self.target_coord]], names=['coords']), skycoord_colname='coords', use_skycoord=True, marker_name=self._circle_name) + self.iw.marker = orig_marker def remove_circle(self): try: self.iw.remove_markers(marker_name=self._circle_name) except AttributeError: self.iw.remove_markers_by_name(marker_name=self._circle_name) + + def tess_field_view(self): + # Show whole field of view + self.iw.zoom_level = 'fit' + + # Show the circle + self.show_circle() + + # Turn of labels -- too cluttered + self.remove_labels() + + def tess_field_zoom_view(self, width=6 * u.arcmin): + # Turn off labels -- too cluttered + self.remove_labels() + + left_side = self.ccd.wcs.pixel_to_world(0, self.ccd.shape[1]/2) + right_side = self.ccd.wcs.pixel_to_world(self.ccd.shape[0], self.ccd.shape[1]/2) + fov = left_side.separation(right_side) + + view_ratio = width / fov + # fit first to get zoom level at full field of view + self.iw.zoom_level = "fit" + + # Then set it to what we actually want... + self.iw.zoom_level = self.iw.zoom_level / view_ratio + + # Show the circle + self.show_circle() + + + diff --git a/stellarphot/visualization/fits_opener.py b/stellarphot/visualization/fits_opener.py new file mode 100644 index 00000000..5a05336b --- /dev/null +++ b/stellarphot/visualization/fits_opener.py @@ -0,0 +1,106 @@ +from pathlib import Path +import warnings + +from astropy.io import fits +from astropy.nddata import CCDData + +from ipyfilechooser import FileChooser + + +class FitsOpener: + def __init__(self, title="Choose an image", filter_pattern=None, **kwargs): + self._fc = FileChooser(title=title, **kwargs) + if not filter_pattern: + self._fc.filter_pattern = ['*.fit*', '*.fit*.[bg]z'] + + self._header = {} + self._selected_cache = self._fc.selected + self.object = "" + self.register_callback(lambda x: None) + + @property + def file_chooser(self): + """ + The actual FileChooser widget. + """ + return self._fc + + @property + def header(self): + self._set_header() + return self._header + + @property + def ccd(self): + """ + Return image as CCDData object + """ + return CCDData.read(self.path) + + @property + def path(self): + return Path(self._fc.selected) + + def _set_header(self): + if not self._header or self._fc.selected != self._selected_cache: + self._selected_cache = self._fc.selected + self._header = fits.getheader(self.path) + try: + self.object = self._header['object'] + except KeyError: + pass + + def register_callback(self, callable): + """ + Register a callback that is called when the value changes. This is + the alternative to observing a value, which is not implemented for + some reason. Only one callback function is allowed. + + This wraps the user-supplied callable to also update the header. + + Parameters + ---------- + + callable : function + A function that takes one argument. + """ + def wrap_call(change): + self._set_header() + callable(change) + + self._fc.register_callback(wrap_call) + + def load_in_image_widget(self, image_widget): + """ + Load the selected image into an `astrowidgets.ImageWidget`, doing the best + that can be done to suppress warnings. + + Parameters + ---------- + + image_widget : `astrowidgets.ImageWidget` + The widget into which to load the image. + """ + with warnings.catch_warnings(): + image_widget.load_fits(str(self.path)) + + def set_file(self, file, directory=None): + """ + Set the selected file of the ``FileChooser`` to be the input file. + + Parameters + ---------- + + file : Path-like + The file to be set as selected. Can be a string or a pathlib.Path. + Cannot contain any directory information. + + directory : Path-like, optional + Directory the file is in. The default value is the current directory of the + ``FileChooser``. + """ + if directory is None: + directory = self._fc.selected_path + + self._fc.reset(directory, file) + self._fc._apply_selection() diff --git a/stellarphot/visualization/seeing_profile_functions.py b/stellarphot/visualization/seeing_profile_functions.py index b50ececb..53cdc8d2 100644 --- a/stellarphot/visualization/seeing_profile_functions.py +++ b/stellarphot/visualization/seeing_profile_functions.py @@ -3,7 +3,6 @@ import numpy as np from photutils.centroids import centroid_com import ipywidgets as ipw -from ipyfilechooser import FileChooser from astropy.io import fits from astropy.stats import sigma_clipped_stats @@ -19,6 +18,7 @@ from stellarphot.io import TessSubmission from stellarphot.visualization import seeing_plot +from stellarphot.visualization.fits_opener import FitsOpener __all__ = ['set_keybindings', 'box', 'make_show_event'] @@ -279,7 +279,7 @@ def __init__(self, imagewidget=None, width=500): self.out3 = ipw.Output() # Build the larger widget self.container = ipw.VBox() - self.filechooser = FileChooser(title="Choose an image") + self.fits_file = FitsOpener(title="Choose an image") big_box = ipw.HBox() big_box = ipw.GridspecLayout(1, 2) layout = ipw.Layout(width='20ch') @@ -309,7 +309,7 @@ def __init__(self, imagewidget=None, width=500): # don't jump around as the image value changes. big_box.layout.justify_content = 'space-between' self.big_box = big_box - self.container.children = [self.filechooser, self.big_box] + self.container.children = [self.fits_file.file_chooser, self.big_box] self.box = self.container self._aperture_name = 'aperture' @@ -320,19 +320,13 @@ def __init__(self, imagewidget=None, width=500): self._set_observers() def load_fits(self, file): - with warnings.catch_warnings(): - self.iw.load_fits(file) + self.fits_file.load_in_image_widget(self.iw) def _update_file(self, change): - with warnings.catch_warnings(): - self.load_fits(change.selected) - try: - self.object_name = fits.getval(change.selected, 'object') - except KeyError: - pass + self.load_fits(change.selected) def _construct_tess_sub(self): - file = self.filechooser.selected + file = self.fits_file.path self._tess_sub = TessSubmission.from_header( fits.getheader(file), telescope_code=self.setting_box.telescope_code.value, @@ -341,7 +335,7 @@ def _construct_tess_sub(self): def _set_seeing_profile_name(self, change): self._construct_tess_sub() - self.seeing_file_name.value = self._tess_sub.seeing_profile + ".png" + self.seeing_file_name.value = self._tess_sub.seeing_profile def _save_toggle_action(self, change): activated = change['new'] @@ -361,7 +355,7 @@ def aperture_obs(change): self.ap_t.observe(aperture_obs, names='value') self.save_aps.on_click(self._save_ap_settings) - self.filechooser.register_callback(self._update_file) + self.fits_file.register_callback(self._update_file) self.save_toggle.observe(self._save_toggle_action, names='value') self.save_seeing.on_click(self._save_seeing_plot) self.setting_box.planet_num.observe(self._set_seeing_profile_name)