Working on comparison with protoliste

This commit is contained in:
reiche committed 2026-07-10 16:45:13 +02:00
1 parent b0434d2b0c
commit cbeec3c9ac
4 files changed
+230 -4

No files matched your search

Binary file not shown.
Binary file not shown.
+226
View File
@@ -606,3 +606,229 @@ def populate_table_dynamic(
header.reset_filters()
table.setUpdatesEnabled(True)
# ── Comparison helpers ────────────────────────────────────────────────────────
# Columns that uniquely identify an element across both lists
_MATCH_KEYS = ("Domain", "Prefix", "Suffix", "Index")
# Columns that are compared but never diffed (they define identity)
_SKIP_DIFF = set(_MATCH_KEYS)
def _match_key(record: dict[str, Any]) -> tuple:
"""Return the identity tuple used to match records across both lists."""
def _norm(val: Any) -> str:
if val is None:
return ""
# Normalise Index the same way it is displayed
return _format_value("Index", val) if False else str(val).strip()
return tuple(_norm(record.get(k)) for k in _MATCH_KEYS)
def _values_equal(a: Any, b: Any) -> bool:
"""True when two cell values should be considered identical."""
if a is None and b is None:
return True
if a is None or b is None:
return False
# Numeric: compare with a small tolerance for floats
try:
fa, fb = float(a), float(b)
import math as _math
if _math.isfinite(fa) and _math.isfinite(fb):
return abs(fa - fb) < 1e-5
except (TypeError, ValueError):
pass
return str(a).strip() == str(b).strip()
def _diff_text(key: str, val_a: Any, val_b: Any) -> str:
"""
Return the display string for a differing cell.
For numeric columns: 'formatted_a / formatted_b'
For text columns: 'a / b'
"""
ta = _format_value(key, val_a) if val_a is not None else ""
tb = _format_value(key, val_b) if val_b is not None else ""
return f"{ta} / {tb}"
def _make_cell(text: str, bg_brush: QBrush, bold: bool = False) -> QTableWidgetItem:
cell = QTableWidgetItem(text)
cell.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
cell.setBackground(bg_brush)
cell.setForeground(QBrush(_FONT_COLOR))
if bold:
f = cell.font()
f.setBold(True)
cell.setFont(f)
return cell
def populate_table_comparison(
table: QTableWidget,
list_a: list[dict[str, Any]],
list_b: list[dict[str, Any]],
) -> None:
"""
Compare two lists of element dicts and display the result in *table*.
Identity match
--------------
Two records are considered the same element when they agree on ALL of:
Domain, Prefix, Suffix, Index
(Index comparison is string-based after stripping whitespace so that
"001" and 1 both match.)
Row rendering rules
-------------------
Element only in list_a (not found in list_b):
→ All columns filled from list_a; Comment = "New Element" in bold.
Element in both lists, all remaining columns identical:
→ Values from list_a; normal font.
Element in both lists, some columns differ:
→ Identity columns (Domain/Prefix/Suffix/Index): value from list_a.
→ Identical columns: value from list_a, normal font.
→ Differing columns:
• Numeric (int/float): "list_a_value / list_b_value" in bold
• Text: "list_a_value / list_b_value" in bold
→ Comment column: "Difference" in bold (unless already set by list_a).
Column set
----------
Uses the fixed COLUMNS list (same as populate_table), with filter
buttons on the filterable columns. Row background follows the
"Group" value from list_a.
Parameters
----------
table : QTableWidget
list_a : list[dict] — primary / reference list (fixed column names)
list_b : list[dict] — secondary list (e.g. from read_excel_sheet)
"""
# Index list_b by identity key for O(1) lookup
index_b: dict[tuple, dict[str, Any]] = {}
for rec in list_b:
k = _match_key(rec)
index_b[k] = rec # last-wins if duplicates
# ── table setup ───────────────────────────────────────────────────────────
table.setUpdatesEnabled(False)
table.clearContents()
table.setRowCount(len(list_a))
table.setColumnCount(len(COLUMNS))
existing = table.horizontalHeader()
if not isinstance(existing, _FilterHeader):
header = _FilterHeader(table)
table.setHorizontalHeader(header)
else:
header = existing
table.setHorizontalHeaderLabels(COLUMNS)
header.setSectionResizeMode(QHeaderView.ResizeToContents)
header.setStretchLastSection(True)
header.setMinimumSectionSize(60)
col_value_sets: dict[int, set[str]] = {}
for col, key in enumerate(COLUMNS):
if key in FILTER_COLS:
col_value_sets[col] = set()
# ── pre-filter: drop rows that are identical in both lists ───────────────
def _row_has_diff(rec_a, rec_b):
"""True when at least one non-identity column differs."""
if rec_b is None:
return True # only in list_a → always show
return any(
not _values_equal(rec_a.get(c), rec_b.get(c))
for c in COLUMNS
if c not in _SKIP_DIFF and c != "Comment"
)
display_rows = [
(rec_a, index_b.get(_match_key(rec_a)))
for rec_a in list_a
if _row_has_diff(rec_a, index_b.get(_match_key(rec_a)))
]
table.setRowCount(len(display_rows))
# ── fill rows ─────────────────────────────────────────────────────────────
for row, (rec_a, rec_b) in enumerate(display_rows):
group_value = rec_a.get("Group", "") or ""
bg_hex = _GROUP_COLORS.get(str(group_value), _DEFAULT_COLOR)
bg_brush = QBrush(QColor(bg_hex))
for col, col_name in enumerate(COLUMNS):
val_a = rec_a.get(col_name)
if rec_b is None:
# ── element only in list_a ────────────────────────────────────
if col_name == "Comment":
cell = _make_cell("New Element", bg_brush, bold=True)
else:
cell = _make_cell(_format_value(col_name, val_a),
bg_brush, bold=False)
else:
# ── element in both lists ─────────────────────────────────────
if col_name in _SKIP_DIFF:
# Identity columns — always from list_a, no diff
cell = _make_cell(_format_value(col_name, val_a),
bg_brush, bold=False)
elif col_name == "Comment":
# Comment: check if anything differed in this row
# We compute it at the end; use a sentinel for now
cell = None # filled in below
else:
val_b = rec_b.get(col_name)
if _values_equal(val_a, val_b):
cell = _make_cell(_format_value(col_name, val_a),
bg_brush, bold=False)
else:
text = _diff_text(col_name, val_a, val_b)
cell = _make_cell(text, bg_brush, bold=True)
# Fill the Comment column
if col_name == "Comment" and rec_b is not None:
# Check whether any non-identity column differs
has_diff = any(
not _values_equal(rec_a.get(c), rec_b.get(c))
for c in COLUMNS
if c not in _SKIP_DIFF and c != "Comment"
)
if has_diff:
comment_text = "Difference"
cell = _make_cell(comment_text, bg_brush, bold=True)
else:
comment_text = _format_value("Comment",
rec_a.get("Comment"))
cell = _make_cell(comment_text, bg_brush, bold=False)
table.setItem(row, col, cell)
if col in col_value_sets and cell is not None:
col_value_sets[col].add(cell.text())
# ── header filter widths ──────────────────────────────────────────────────
table.setUpdatesEnabled(True)
table.setUpdatesEnabled(False)
BTN_EXTRA = _FilterHeader._BTN_W + 8
for col, key in enumerate(COLUMNS):
if key in FILTER_COLS:
current = header.sectionSize(col)
header.resizeSection(col, current + BTN_EXTRA)
header.setSectionResizeMode(col, QHeaderView.Interactive)
for col, values_set in col_value_sets.items():
sorted_vals = sorted(values_set, key=lambda v: (v == "", v.lower()))
header.set_column_values(col, sorted_vals)
header.reset_filters()
table.setUpdatesEnabled(True)
+4 -4
View File
@@ -8,7 +8,7 @@ from PyQt5 import QtWidgets
from ui.BeamlineGUI import Ui_BeamlineGUI
from beamline_editor import BeamlineEditorWidget
from beamline_editor.table_utils import populate_table, populate_table_dynamic
from beamline_editor.table_utils import populate_table, populate_table_comparison
from beamline_editor.excel_utils import read_excel_sheet
from elements import *
@@ -111,8 +111,8 @@ class BeamlineEditor(QtWidgets.QMainWindow, Ui_BeamlineGUI):
return
rows = read_excel_sheet(fileName, "Proto-HL")
populate_table(self.UIProtoList,rows)
print(rows[0])
populate_table_comparison(self.UIProtoList,self.plrecs,rows)
def saveTemplates(self):
template=self.editor.get_templates()
@@ -275,7 +275,7 @@ class BeamlineEditor(QtWidgets.QMainWindow, Ui_BeamlineGUI):
self.PL.generateLayout(self.lines)
self.plrecs = [self.PL.info[ele] for ele in self.PL.order]
populate_table(self.UIProtoList,self.plrecs)
print(self.plrecs[0])
def unwrap(self, name, node_id,line,case_num):