Athos branch and correction of dipole tilts (rad vs degree issues)

This commit is contained in:
2026-08-05 10:15:40 +02:00
parent 78208a00de
commit c0d630eca4
2 changed files with 162 additions and 1 deletions
+156
View File
@@ -910,3 +910,159 @@ def populate_table_comparison(
header.reset_filters()
table.setUpdatesEnabled(True)
# ── Excel export ──────────────────────────────────────────────────────────────
def export_table_to_excel(
table: QTableWidget,
file_path: str,
sheet_name: str,
include_hidden_rows: bool = False,
) -> bool:
"""
Write the visible contents of *table* to an Excel file, preserving the
row background colours and bold fonts used in the widget.
Nothing is written when the table has no rows (or no visible rows) —
the function returns False in that case and leaves any existing file
untouched.
Parameters
----------
table : QTableWidget
The table to export. Header labels become the first sheet row.
file_path : str
Destination .xlsx path. Created if missing, overwritten if present.
sheet_name : str
Name of the worksheet tab inside the workbook.
include_hidden_rows : bool
When False (default) rows hidden by column filters are skipped, so
the export matches exactly what the user sees. When True every row
is written regardless of filter state.
Returns
-------
bool
True — file written (table had at least one exported row).
False — nothing written (table was empty / fully filtered out).
Raises
------
ImportError
If openpyxl is not installed.
Notes
-----
- Cell background colours are taken from each QTableWidgetItem's
background brush, so the same _GROUP_COLORS palette used on screen
is reproduced in the workbook.
- Cells rendered in bold on screen (e.g. differing values or the
"New Element" / "Difference" / "Unresolved Element" comments) are
written in bold too.
- Numeric-looking strings are written as numbers so Excel can sort
and chart them; everything else is written as text.
"""
try:
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
except ImportError:
raise ImportError(
"openpyxl is required for Excel export: pip install openpyxl"
) from None
n_rows = table.rowCount()
n_cols = table.columnCount()
if n_rows == 0 or n_cols == 0:
return False
# Determine which rows to export
export_rows = [
r for r in range(n_rows)
if include_hidden_rows or not table.isRowHidden(r)
]
if not export_rows:
return False
wb = Workbook()
ws = wb.active
ws.title = sheet_name
# ── header row ────────────────────────────────────────────────────────────
header_font = Font(name="Arial", size=10, bold=True, color="FFFFFFFF")
header_fill = PatternFill("solid", fgColor="FF404040")
thin = Side(style="thin", color="FFBFBFBF")
border = Border(left=thin, right=thin, top=thin, bottom=thin)
for col in range(n_cols):
hdr_item = table.horizontalHeaderItem(col)
label = hdr_item.text() if hdr_item is not None else f"Column {col+1}"
cell = ws.cell(row=1, column=col + 1, value=label)
cell.font = header_font
cell.fill = header_fill
cell.alignment = Alignment(horizontal="center", vertical="center")
cell.border = border
# ── data rows ─────────────────────────────────────────────────────────────
def _argb(qcolor) -> str | None:
"""Convert a QColor to an openpyxl ARGB hex string, or None if white."""
if qcolor is None or not qcolor.isValid():
return None
# Skip pure white / fully transparent — leave the cell unfilled
if qcolor.alpha() == 0:
return None
if (qcolor.red(), qcolor.green(), qcolor.blue()) == (255, 255, 255):
return None
return f"FF{qcolor.red():02X}{qcolor.green():02X}{qcolor.blue():02X}"
for out_row, src_row in enumerate(export_rows, start=2):
for col in range(n_cols):
item = table.item(src_row, col)
text = item.text() if item is not None else ""
# Write numbers as numbers so Excel can sort/plot them
value: Any = text
if text not in ("", "-"):
try:
if "/" not in text: # skip "a / b" diff strings
value = float(text)
if value.is_integer() and "." not in text:
value = int(value)
except (ValueError, TypeError):
value = text
cell = ws.cell(row=out_row, column=col + 1, value=value)
cell.border = border
if item is not None:
# Background colour
argb = _argb(item.background().color())
if argb:
cell.fill = PatternFill("solid", fgColor=argb)
# Bold font
is_bold = item.font().bold()
cell.font = Font(name="Arial", size=10, bold=is_bold,
color="FF000000")
else:
cell.font = Font(name="Arial", size=10, color="FF000000")
# ── column widths ─────────────────────────────────────────────────────────
for col in range(n_cols):
max_len = 0
hdr_item = table.horizontalHeaderItem(col)
if hdr_item is not None:
max_len = len(hdr_item.text())
for src_row in export_rows:
item = table.item(src_row, col)
if item is not None:
max_len = max(max_len, len(item.text()))
ws.column_dimensions[get_column_letter(col + 1)].width = min(
max(10, max_len + 3), 40
)
# Freeze the header row so it stays visible while scrolling
ws.freeze_panes = "A2"
wb.save(file_path)
return True
+6 -1
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_comparison
from beamline_editor.table_utils import populate_table, populate_table_comparison, export_table_to_excel
from beamline_editor.excel_utils import read_excel_sheet
from beamline_editor.checkbox_table import (
populate_checkbox_table,
@@ -54,6 +54,7 @@ class BeamlineEditor(QtWidgets.QMainWindow, Ui_BeamlineGUI):
self.actionLoad.triggered.connect(self.load)
self.actionCompareLayout.triggered.connect(self.compareLayout)
self.actionExport_as_Proto_List.triggered.connect(self.exportLayoutasPL)
self.actionExportOnlineModel.triggered.connect(self.exportOnlineModel)
# elements, lines and branch points
@@ -193,6 +194,10 @@ class BeamlineEditor(QtWidgets.QMainWindow, Ui_BeamlineGUI):
self.exporter.writeDomain(references[sec],sec,domain.LengthRes,subsec)
self.exporter.buildCaseFooter()
def exportLayoutasPL(self):
ok = export_table_to_excel(self.UIProtoList,"Protolist/Export/protolist.xlsx","Proto-HL")
if not ok:
print("Table is empty — nothing exported")
def compareLayout(self):
if len(self.plrecs) == 0: