diff --git a/aperoll/proseco_data.py b/aperoll/proseco_data.py index feef429..d321978 100644 --- a/aperoll/proseco_data.py +++ b/aperoll/proseco_data.py @@ -147,7 +147,9 @@ def run_proseco(self): "review_table": aca_review, } except Exception as exc: - logger.debug(f"ProsecoData failed calling proseco ({type(exc).__name__}): {exc}") + logger.debug( + f"ProsecoData failed calling proseco ({type(exc).__name__}): {exc}" + ) trace = traceback.extract_tb(exc.__traceback__) for step in trace: logger.debug(f" in {step.filename}:{step.lineno}/{step.name}:") @@ -167,14 +169,15 @@ def run_sparkles(self): ) return self._dir / "sparkles" except Exception as exc: - logger.debug(f"ProsecoData failed calling sparkles ({type(exc).__name__}): {exc}") + logger.debug( + f"ProsecoData failed calling sparkles ({type(exc).__name__}): {exc}" + ) trace = traceback.extract_tb(exc.__traceback__) for step in trace: logger.debug(f" in {step.filename}:{step.lineno}/{step.name}:") logger.debug(f" {step.line}") raise Exception(f"ProsecoData failed calling sparkles: {exc}") from None - def open_export_proseco_dialog(self): """ Save the star catalog in a pickle file. diff --git a/aperoll/widgets/attitude_widget.py b/aperoll/widgets/attitude_widget.py index e730409..0f0e067 100644 --- a/aperoll/widgets/attitude_widget.py +++ b/aperoll/widgets/attitude_widget.py @@ -331,10 +331,7 @@ def _set_attitude(self, attitude, emit=True): if emit: self.attitude_cleared.emit() return - if ( - not isinstance(attitude, Quat) - and len(attitude) == 4 - ): + if not isinstance(attitude, Quat) and len(attitude) == 4: attitude = normalize(attitude) # this check is to break infinite recursion because in the connections q1 = None if attitude is None else Quat(attitude).q diff --git a/aperoll/widgets/catalog_widget.py b/aperoll/widgets/catalog_widget.py new file mode 100644 index 0000000..3fa95b3 --- /dev/null +++ b/aperoll/widgets/catalog_widget.py @@ -0,0 +1,242 @@ +import numpy as np +from astropy.table import Table +from chandra_aca.attitude import calc_att +from cxotime import CxoTime +from PyQt5 import QtCore as QtC +from PyQt5 import QtWidgets as QtW +from Quaternion import Quat + +from aperoll.utils import log_exception, logger +from aperoll.widgets.attitude_widget import ( + AttitudeWidget, + QuatRepresentation, + hstack, + vstack, +) +from aperoll.widgets.centroid_table import CentroidTable +from aperoll.widgets.line_edit import LineEdit + + +class CatalogWidget(QtW.QWidget): + slot_enabled = QtC.pyqtSignal(int, bool) + track = QtC.pyqtSignal(bool) + attitude_updated = QtC.pyqtSignal(Quat) + + def __init__(self): + super().__init__() + + self.date_edit = LineEdit(self) + self.date_edit.setReadOnly(True) + self.attitude_widget = AttitudeWidget( + self, + columns={ + QuatRepresentation.EQUATORIAL: 0, + QuatRepresentation.QUATERNION: 0, + QuatRepresentation.SUN: 0, + }, + ) + self.attitude_widget.set_read_only(True) + self.catalog_widget = CentroidTable( + columns=["slot", "id", "fid", "yang", "zang", "mag", "enabled"] + ) + self.catalog_widget.set_read_only(True) + self.catalog_widget.setMinimumWidth(500) + self.catalog_widget.setHorizontalScrollBarPolicy(QtC.Qt.ScrollBarAlwaysOff) + + self.track_button = QtW.QPushButton("Start tracking") + self.track_button.setCheckable(True) + self.track_button.clicked.connect(self._track) + + date_box = QtW.QGroupBox("Date") + date_box.setLayout( + vstack( + self.date_edit, + ) + ) + + self.starcat_source_edit = QtW.QComboBox(self) + self.starcat_source_edit.addItems(["Kadi", "Proseco", "Find Attitude"]) + + self.centroid_table = CentroidTable(add_enable=False) + self.centroid_table.setHorizontalScrollBarPolicy(QtC.Qt.ScrollBarAlwaysOff) + self.centroid_table.setMinimumWidth(100) + + source_box = QtW.QGroupBox("Source") + source_box.setLayout( + vstack( + self.starcat_source_edit, + ) + ) + + att_box = QtW.QGroupBox("Attitude") + att_box.setLayout( + vstack( + self.attitude_widget, + ) + ) + + table_box = QtW.QGroupBox("Catalog") + table_box.setLayout( + vstack( + self.catalog_widget, + ) + ) + + centroid_box = QtW.QGroupBox("Centroids") + centroid_box.setLayout( + vstack( + self.centroid_table, + ) + ) + + layout = QtW.QHBoxLayout() + layout.addStretch() + layout.addLayout( + vstack( + hstack(date_box, source_box), + att_box, + table_box, + centroid_box, + hstack(self.track_button, stretch=True), + stretch=True, + ) + ) + layout.addStretch() + self.setLayout(layout) + self.reset() + + self.tracking = False + + self.catalog_widget.slot_enabled.connect(self.slot_enabled) + + def _track(self, checked): + text = "Stop" if checked else "Start" + self.track_button.setText(f"{text} tracking") + + def get_tracking(self): + return self.track_button.isChecked() + + def set_tracking(self, tracking): + self._track(tracking) + self.track_button.setChecked(tracking) + + tracking = property(get_tracking, set_tracking) + + def set_attitude(self, attitude): + self.attitude_widget.set_attitude(attitude) + + def get_attitude(self): + return self.attitude_widget.get_attitude() + + attitude = property(get_attitude, set_attitude) + + def set_date(self, date): + date = CxoTime(date).date if date is not None else "" + if date != self.date_edit.text(): + self.date_edit.setText(date) + + def get_date(self): + text = self.date_edit.text() + if text: + return CxoTime(self.date_edit.text()) + + date = property(get_date, set_date) + + def get_starcat_source(self): + return self.starcat_source_edit.currentText() + + def set_starcat_source(self, source): + self.starcat_source_edit.setCurrentText(source) + + starcat_source = property(get_starcat_source, set_starcat_source) + + def reset(self): + self.set_date(None) + self.attitude = None + self.catalog_widget.reset() + # self.centroid_table.reset() + + def set_catalog(self, catalog): + self.reset() + + try: + logger.debug("Setting catalog") + + self.date = catalog.date + self.attitude = catalog.att + + table = Table() + table["slot"] = np.arange(8) + # table["id"] = np.ma.masked_all(8, dtype=int) + # table["yang"] = np.ma.masked_all(8, dtype=float) + # table["zang"] = np.ma.masked_all(8, dtype=float) + # table["mag"] = np.ma.masked_all(8, dtype=float) + # table["enabled"] = np.zeros(8, dtype=bool) + # table["fid"] = np.ma.masked_all(8, dtype=bool) + # table["type"] = np.ma.masked_all(8, dtype="= 0.2. -target-version = "py311" +target-version = "py312" # fix = true lint.unfixable = [] @@ -31,6 +30,7 @@ lint.extend-select = [ "ARG001", # Unused function argument "RSE102", # Unnecessary parentheses on raised exception "PERF401", # Use a list comprehension to create a transformed list +"S101", # Use of `assert` detected ] lint.ignore = [ @@ -41,10 +41,13 @@ lint.ignore = [ "B028", # No explicit `stacklevel` keyword argument found "PLR0913", # Too many arguments to function call "PLR1730", # Checks for if statements that can be replaced with min() or max() calls + "PLC0415", # `import` should be at the top-level of a file + "PLW1641", # Class implements `__hash__` if `__eq__` is implemented ] extend-exclude = [ "docs", + "build", ] [lint.pycodestyle] @@ -56,4 +59,5 @@ max-line-length = 100 # E501 reports lines that exceed the length of 100. # - D205: Don't worry about test docstrings # - ARG001: Unused function argument false positives for some fixtures # - E501: Line-too-long -"**/tests/test_*.py" = ["D205", "ARG001", "E501"] +# - S101: Do not use assert +"**/tests/test_*.py" = ["D205", "ARG001", "E501", "S101"]