Build Packages / Create release (push) Successful in 20s
Build Packages / build:rugnux:aarch64 (cross) (push) Successful in 8m48s
Build Packages / build:rugnux-tgz (x86_64) (push) Successful in 10m2s
Build Packages / build:viewer-tgz:cpu (push) Successful in 11m31s
Build Packages / build:viewer-tgz:cuda (push) Successful in 12m17s
Build Packages / build:windows:nocuda (push) Successful in 17m20s
Build Packages / build:windows:cuda (push) Successful in 19m52s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 14m50s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 24m30s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 15m12s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 16m41s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 14m28s
Build Packages / build:rugnux:windows (push) Successful in 11m4s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 17m26s
Build Packages / Generate python client (push) Successful in 37s
Build Packages / build:rpm (rocky8) (push) Successful in 18m50s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 19m10s
Build Packages / Build documentation (push) Successful in 1m59s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 18m38s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 18m36s
Build Packages / build:rpm (rocky9) (push) Successful in 19m20s
Build Packages / Unit tests (push) Successful in 1h36m23s
One row holding both coordinates sat oddly under a PONI split over two, so the direct beam gets the same treatment - and with it a real "From header" column (the header geometry's own direct beam, the file's beam centre while the header is untilted) and a Change column that says how far the beam actually moved, where the combined row could only show dashes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015vRwzF8PLaaa2novAKYorq
106 lines
5.1 KiB
C++
106 lines
5.1 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
||
// SPDX-License-Identifier: GPL-3.0-only
|
||
|
||
#include "JFJochCalibrationResultWindow.h"
|
||
|
||
#include <QFrame>
|
||
#include <QHBoxLayout>
|
||
#include <QHeaderView>
|
||
#include <QLabel>
|
||
#include <QTableWidget>
|
||
#include <QVBoxLayout>
|
||
|
||
#include "../../common/JFJochMath.h" // PI
|
||
|
||
namespace {
|
||
constexpr double RAD_TO_DEG = 180.0 / PI;
|
||
|
||
// A big-number "card" for the hero row, as in the merge-statistics window.
|
||
QWidget *MakeCard(const QString &value, const QString &caption, QWidget *parent) {
|
||
auto *card = new QFrame(parent);
|
||
card->setFrameShape(QFrame::StyledPanel);
|
||
auto *l = new QVBoxLayout(card);
|
||
l->setContentsMargins(12, 8, 12, 8);
|
||
l->setSpacing(0);
|
||
auto *v = new QLabel(value, card);
|
||
QFont f = v->font();
|
||
f.setPointSizeF(f.pointSizeF() * 2.2);
|
||
f.setBold(true);
|
||
v->setFont(f);
|
||
v->setStyleSheet("color: #1F3A5F;");
|
||
v->setAlignment(Qt::AlignCenter);
|
||
auto *c = new QLabel(caption, card);
|
||
c->setStyleSheet("color: gray;");
|
||
c->setAlignment(Qt::AlignCenter);
|
||
l->addWidget(v);
|
||
l->addWidget(c);
|
||
return card;
|
||
}
|
||
}
|
||
|
||
JFJochCalibrationResultWindow::JFJochCalibrationResultWindow(const QString &title,
|
||
const CalibrationResult &calibration,
|
||
const DiffractionGeometry &header,
|
||
const QString &poni_path,
|
||
QWidget *parent)
|
||
: QWidget(parent, Qt::Window) {
|
||
setWindowTitle("Detector calibration — " + title);
|
||
setAttribute(Qt::WA_DeleteOnClose);
|
||
resize(640, 380);
|
||
|
||
const DiffractionGeometry &g = calibration.geometry;
|
||
const double pxl_mm = g.GetPixelSize_mm();
|
||
|
||
auto *layout = new QVBoxLayout(this);
|
||
|
||
auto *hero = new QHBoxLayout();
|
||
hero->addWidget(MakeCard(QString::number(calibration.rms_radial_pxl, 'f', 2), "Radial rms [px]", this));
|
||
hero->addWidget(MakeCard(QString::number(calibration.beam_sigma_pxl, 'f', 3), "Beam σ [px]", this));
|
||
hero->addWidget(MakeCard(QString::number(calibration.ring_points), "Ring points", this));
|
||
layout->addLayout(hero);
|
||
|
||
auto *table = new QTableWidget(7, 4, this);
|
||
table->setHorizontalHeaderLabels({"Quantity", "Fitted", "From header", "Change"});
|
||
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||
table->verticalHeader()->setVisible(false);
|
||
table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
||
|
||
// The PONI is the point of normal incidence, which is what the .poni file stores; the direct beam
|
||
// is where the beam actually lands, which is what every other program calls the beam centre. They
|
||
// part company by distance*tan(rot) as soon as the detector is tilted, so show both.
|
||
const auto [beam_x, beam_y] = g.GetDirectBeam_pxl();
|
||
const auto [header_beam_x, header_beam_y] = header.GetDirectBeam_pxl();
|
||
|
||
const struct { QString name; double fitted; double header; int digits; } rows[] = {
|
||
{"PONI x [px]", g.GetBeamX_pxl(), header.GetBeamX_pxl(), 3},
|
||
{"PONI y [px]", g.GetBeamY_pxl(), header.GetBeamY_pxl(), 3},
|
||
{"Rot1 [°]", g.GetPoniRot1_rad() * RAD_TO_DEG, header.GetPoniRot1_rad() * RAD_TO_DEG, 4},
|
||
{"Rot2 [°]", g.GetPoniRot2_rad() * RAD_TO_DEG, header.GetPoniRot2_rad() * RAD_TO_DEG, 4},
|
||
{"Distance [mm]", g.GetDetectorDistance_mm(), header.GetDetectorDistance_mm(), 4},
|
||
{"Direct beam x [px]", beam_x, header_beam_x, 3},
|
||
{"Direct beam y [px]", beam_y, header_beam_y, 3},
|
||
};
|
||
|
||
for (int i = 0; i < 7; i++) {
|
||
const double change = rows[i].fitted - rows[i].header;
|
||
table->setItem(i, 0, new QTableWidgetItem(rows[i].name));
|
||
table->setItem(i, 1, new QTableWidgetItem(QString::number(rows[i].fitted, 'f', rows[i].digits)));
|
||
table->setItem(i, 2, new QTableWidgetItem(QString::number(rows[i].header, 'f', rows[i].digits)));
|
||
table->setItem(i, 3, new QTableWidgetItem(
|
||
QStringLiteral("%1%2").arg(change >= 0 ? "+" : "").arg(change, 0, 'f', rows[i].digits)));
|
||
}
|
||
layout->addWidget(table);
|
||
|
||
auto *note = new QLabel(this);
|
||
note->setWordWrap(true);
|
||
note->setStyleSheet("color: gray;");
|
||
// In mm these are what the .poni file carries, so they are measured pyFAI's way - from the edge of
|
||
// the sensor, which puts the centre of pixel i at (i + 0.5) * pixel size - not from the centre of
|
||
// the first pixel as the [px] rows above (docs/DETECTOR_GEOMETRY.md).
|
||
note->setText(QStringLiteral("PONI x = %1 mm, PONI y = %2 mm.%3")
|
||
.arg((g.GetBeamX_pxl() + 0.5) * pxl_mm, 0, 'f', 4)
|
||
.arg((g.GetBeamY_pxl() + 0.5) * pxl_mm, 0, 'f', 4)
|
||
.arg(poni_path.isEmpty() ? QString() : " Written to " + poni_path));
|
||
layout->addWidget(note);
|
||
}
|