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)