Sample list: spreadsheet param columns + whole-row mounted highlight #209

Merged
perl_d merged 2 commits from where_I_am_show_more_info into main 2026-09-09 18:12:59 +02:00
2 changed files with 80 additions and 9 deletions
+40 -9
View File
@@ -43,7 +43,19 @@ def get_entry(sample: SampleShortInfo, column: int):
return sample.screening_count
elif column == 10:
return sample.rotation_count
# Data-collection params straight from the AareDB spreadsheet (filled by
# spreadsheetupdater). Nullable: samples without params show blank.
elif column == 11:
return sample.aaredb_params.oscillation if sample.aaredb_params else None
elif column == 12:
return sample.aaredb_params.exposure if sample.aaredb_params else None
elif column == 13:
return sample.aaredb_params.totalangle if sample.aaredb_params else None
elif column == 14:
# Model holds a 0-1 fraction; users think in spreadsheet percent.
t = sample.aaredb_params.transmission if sample.aaredb_params else None
return None if t is None else round(t * 100, 1)
elif column == 15:
return sample.comment
return ""
@@ -74,6 +86,10 @@ class UserSampleSpreadsheet(QAbstractTableModel):
# collected, so Screening sits left of Rotation.
"Screening count",
"Rotation count",
"Oscillation (°)",
"Exposure (s)",
"Total range (°)",
"Transmission (%)",
"Comment",
]
self.current_sample = current_sample
@@ -123,17 +139,25 @@ class UserSampleSpreadsheet(QAbstractTableModel):
elif role == Qt.ItemDataRole.BackgroundRole:
# Status lives in the "#" column, as a full cell fill under the
# row number — rows themselves alternate grey/white (view-level)
# and selection stays the pale blue tint.
# and selection stays the pale blue tint. Exception: the mounted
# sample paints the WHOLE row blue so it is findable at a glance.
sample = self._sorted_samples[index.row()]
if sample.db_id == self.current_sample:
return QBrush(qcolor(SAMPLE_ROW_QUEUED_BG))
if index.column() == COL_STATUS:
color = self._status_color(self._sorted_samples[index.row()])
color = self._status_color(sample)
if color is not None:
return QBrush(qcolor(color))
elif role == Qt.ItemDataRole.ForegroundRole:
# Tinted cells get fixed dark ink: the tints stay light pastel in
# BOTH themes, so Sunset's white theme text would vanish on them.
# Pastel-tinted "#" cells get fixed dark ink: the tints stay light
# in BOTH themes, so Sunset's white theme text would vanish on
# them. The mounted-row blue is mid-tone and readable with the
# theme's own text color, so it keeps the default ink.
sample = self._sorted_samples[index.row()]
if (
index.column() == COL_STATUS
and self._status_color(self._sorted_samples[index.row()]) is not None
and sample.db_id != self.current_sample
and self._status_color(sample) is not None
):
return QBrush(qcolor(SAMPLE_STATUS_TEXT))
elif role == Qt.ItemDataRole.TextAlignmentRole: # Align text to center
@@ -204,10 +228,11 @@ class UserSampleSpreadsheet(QAbstractTableModel):
def _emit_tints_changed(self) -> None:
if self.rowCount() > 0:
# Whole span: the mounted row tints every column, not just "#".
self.dataChanged.emit(
self.index(0, COL_STATUS),
self.index(self.rowCount() - 1, COL_STATUS),
[Qt.ItemDataRole.BackgroundRole],
self.index(self.rowCount() - 1, self.columnCount() - 1),
[Qt.ItemDataRole.BackgroundRole, Qt.ItemDataRole.ForegroundRole],
)
def headerData(self, section, orientation, role=None):
@@ -261,10 +286,16 @@ class UserSampleSpreadsheet(QAbstractTableModel):
), # Reverse for descending order
)
else:
# Sort the samples based on the specified column and order
# Sort the samples based on the specified column and order.
# Nullable columns (params, comment): None sorts last, never
# compared against a real value (None < float raises).
def _key(row):
v = get_entry(row, self._sort_col)
return (v is None, "" if v is None else v)
self._sorted_samples = sorted(
filtered,
key=lambda row: get_entry(row, self._sort_col),
key=_key,
reverse=(
self._sort_order == Qt.SortOrder.DescendingOrder
), # Reverse for descending order
+40
View File
@@ -263,3 +263,43 @@ def test_mime_data_round_trips_for_chip_drops(status_model):
samples = SampleShortInfoList.model_validate_json(payload.text())
assert len(samples.s) == 2
assert samples.s[0].db_id == model.get_id(0).db_id
def test_mounted_row_is_blue_across_all_columns(status_model):
# Whole-row blue (not just "#") so the mounted sample is findable at a
# glance; the blue keeps the theme's own ink (no dark override).
model = status_model
model.updateCurrentSample(current_puck="P1", current_sample=1)
row = _row_of(model, 1)
for col in range(model.columnCount()):
brush = model.data(model.index(row, col), Qt.ItemDataRole.BackgroundRole)
assert brush.color().name().lower() == "#729fcf"
assert model.data(model.index(row, col), Qt.ItemDataRole.ForegroundRole) is None
# Other rows: still "#"-only tint, other columns untouched.
other = _row_of(model, 2)
assert model.data(model.index(other, 1), Qt.ItemDataRole.BackgroundRole) is None
def test_spreadsheet_param_columns(status_model):
from aarecommon.models.models import DataCollectionParameters
model = status_model
hdr = model.header
osc, exp, tot, trans = (
hdr.index("Oscillation (°)"),
hdr.index("Exposure (s)"),
hdr.index("Total range (°)"),
hdr.index("Transmission (%)"),
)
model.get_id(_row_of(model, 1)).aaredb_params = DataCollectionParameters(
oscillation=0.1, exposure=0.01, totalangle=360, transmission=0.125
)
row = _row_of(model, 1)
d = lambda c: model.data(model.index(row, c), Qt.ItemDataRole.DisplayRole)
assert (d(osc), d(exp), d(tot), d(trans)) == (0.1, 0.01, 360, 12.5)
# No params -> blank, and sorting a mostly-None column must not raise;
# the real value sorts first.
assert model.data(model.index(_row_of(model, 2), osc), Qt.ItemDataRole.DisplayRole) is None
model.sort(osc, Qt.SortOrder.AscendingOrder)
assert model.get_id(0).db_id == 1
assert model.headerData(hdr.index("Comment"), Qt.Orientation.Horizontal, 0) == "Comment"