Source code for aca_view.widgets.table_widget

import logging

import numpy as np
from PyQt5 import QtCore as QtC
from PyQt5 import QtGui as QtG
from PyQt5 import QtWidgets as QtW

from aca_view import config, limits, utils

logger = logging.getLogger("aca_view.widgets")

_default_rows = [
    "IMGNUM",
    "IMGFUNC",
    "IMGROW0",
    "IMGCOL0",
    "IMGFID",
    "IMGSTAT",
    "",
    "BGDAVG",
    "BGDRMS",
    "BGDSTAT",
    "IMGSCALE",
    "",
    "YAGS",
    "ZAGS",
    "DYAGS",
    "DZAGS",
    "AOACMAG",
    "MAG_EST",
    "AGE",
]

_default_fmt = {
    k: (lambda x: f"{x:7.1f} C") for k in ["TEMPCCD", "TEMPHOUS", "TEMPPRIM", "TEMPSEC"]
}
_default_fmt.update({k: (lambda x: f"{x:7.1f}") for k in ["YAGS", "ZAGS"]})
_default_fmt.update({k: (lambda x: f"{x:8.2f}") for k in ["DYAGS", "DZAGS"]})
_default_fmt.update({k: (lambda x: f"{x:8.2f}") for k in ["AOACMAG", "MAG_EST"]})

IMGSTAT_TOOLTIP = """S: Saturated Pixel
D: Defective Pixel
Q: Quadrant boundary
C: Common Column
M: Multiple stars
I: Ionizing Radiation"""


[docs] class TableWidget(QtW.QTableWidget): """ Widget to display slot telemetry in table format. """ def __init__(self, timeline, rows=_default_rows, fmt=None): super().__init__(len(rows), 9) fmt = {} if fmt is None else fmt # A comment about sizes: # We want to have a single size that determines all the other sizes. # That could be, in principle, the table size, the row size, the fint size... # We also want to that GeneralInfoWidget, TableWidget and CmdWidget have a consistent # size. For this reason I chose the font size as the yardstick. # The row height is set according to the font, # then the column widths are set to make sure the contents fit # Finally, the ideal_size method gives the size of the table. self.font = QtG.QFontDatabase.systemFont(QtG.QFontDatabase.FixedFont) self.font.setPointSize(18) fm = QtG.QFontMetricsF(self.font) row_height = 1.3 * fm.height() self.format = {r: (lambda x: f"{x:>8}") for r in rows} self.format.update(_default_fmt) self.format.update(fmt) self.timeline = timeline self.rows = rows self.horizontalHeader().setDefaultSectionSize( max([int(1.2 * fm.horizontalAdvance(s)) for s in ["-2020.22", "------"]]) ) self.verticalHeader().setDefaultSectionSize(int(row_height)) self.horizontalHeader().hide() self.verticalHeader().hide() self.tooltips = {"IMGSTAT": IMGSTAT_TOOLTIP} # self.horizontalHeader().setSectionResizeMode(QtW.QHeaderView.Stretch) self.set_rows(rows) self.rows_fixed = bool(rows) @utils.log_time("table_widget.set_rows") def set_rows(self, rows): import copy self.setRowCount(len(rows)) if self.rows: for r in rows: if r not in self.rows: self.rows.append(r) else: self.rows = copy.deepcopy(rows) for j, r in enumerate(self.rows): if self.item(j, 0): continue self.setItem(j, 0, QtW.QTableWidgetItem()) item = self.item(j, 0) if self.rows[j] in self.tooltips: item.setToolTip(self.tooltips[self.rows[j]]) item.setFlags(QtC.Qt.ItemIsEditable | QtC.Qt.ItemIsEnabled) item.setForeground( QtG.QColor(config.SETTINGS["palette"]["color_foreground_normal"]) ) item.setText(r) item.setFont(self.font) for j, _r in enumerate(self.rows): for i in range(1, 9): if self.item(j, i): continue self.setItem(j, i, QtW.QTableWidgetItem()) item = self.item(j, i) if self.rows[j] in self.tooltips: item.setToolTip(self.tooltips[self.rows[j]]) item.setFlags(QtC.Qt.NoItemFlags) if self.rows[j]: item.setBackground( QtG.QColor( config.SETTINGS["palette"]["color_background_normal"] ) ) item.setForeground( QtG.QColor(config.SETTINGS["palette"]["color_foreground_normal"]) ) item.setFont(self.font) self.resizeColumnToContents(0) def ideal_size(self): n_cols = self.columnCount() col_widths = [self.columnWidth(i) for i in range(n_cols)] width = self.verticalHeader().width() + sum(col_widths) n_rows = self.rowCount() height = sum([self.rowHeight(i) for i in range(n_rows)]) return width, height @utils.log_time("table_widget.update_slot") def update_slot(self, idx, slot, data_row): if not self.rows_fixed and not set(data_row.dtype.names).issubset(self.rows): self.set_rows(data_row.dtype.names) for j, r in enumerate(self.rows): item = self.item(j, slot + 1) status = limits.OK if r == "AGE": age = ( int(self.timeline.table["VCDUCTR"][idx]) - int(data_row["VCDUCTR"]) ) / 4 item.setText(f"{age:8.0f}") # this is commented out until we have decided whether we want it or not. # allowed_age = max( # {"8X8": 3, "6X6": 1, "4X4": 0}[data_row["IMGSIZE"]], # int(np.ceil(data_row["INTEG"] / 1.025)) - 1, # ) # status = limits.OK if age <= allowed_age else limits.WARNING elif r in list(data_row.dtype.names): if type(data_row[r]) is np.ma.core.MaskedConstant: text = " --" else: text = self.format[r](data_row[r]) if item.text() != text: item.setText(text) status = limits.check_limits(r, data_row) if status == limits.OK: item.setBackground( QtG.QColor(config.SETTINGS["palette"]["color_background_normal"]) ) elif status == limits.CAUTION: item.setBackground(QtG.QColor(255, 140, 0, 128)) elif status == limits.WARNING: item.setBackground(QtG.QColor(255, 0, 0, 128)) @utils.log_time("table_widget.reset_slot") def reset_slot(self, slot): for j, r in enumerate(self.rows): if not r: continue item = self.item(j, slot + 1) if item.text() != "" and self.rows[j]: item.setText("") item.setBackground( QtG.QColor(config.SETTINGS["palette"]["color_background_normal"]) ) @utils.log_time("table_widget.update") def update(self, idx, *entries): logger.debug("Update") for slot, index in enumerate(entries): if ( index >= self.timeline[slot].image.shape[0] or index is None or index < -1 or len(self.timeline[slot].table) == 0 ): self.reset_slot(slot) else: self.update_slot(idx, slot, self.timeline[slot].table[index]) if self.rows: self.setMinimumHeight(self.viewportSizeHint().height() + 2)