Viewer: every panel section starts folded, and calibration carries the azimuthal settings
Build Packages / build:viewer-tgz:cpu (push) Successful in 19m17s
Build Packages / build:viewer-tgz:cuda (push) Successful in 20m58s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 21m57s
Build Packages / build:windows:nocuda (push) Successful in 23m25s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 24m14s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 28m25s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 28m26s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 29m26s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 20m52s
Build Packages / XDS test (durin plugin) (push) Successful in 12m1s
Build Packages / build:rpm (rocky9) (push) Successful in 21m4s
Build Packages / Generate python client (push) Successful in 40s
Build Packages / Build documentation (push) Successful in 1m12s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (rocky8) (push) Successful in 25m9s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 20m53s
Build Packages / DIALS test (push) Successful in 21m14s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 25m44s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 10m32s
Build Packages / XDS test (neggia plugin) (push) Successful in 9m13s
Build Packages / build:windows:cuda (push) Successful in 32m13s
Build Packages / build:viewer-tgz:cpu (pull_request) Successful in 16m9s
Build Packages / Unit tests (push) Successful in 1h30m48s
Build Packages / build:viewer-tgz:cuda (pull_request) Successful in 18m19s
Build Packages / build:rpm (ubuntu2404_nocuda) (pull_request) Successful in 20m42s
Build Packages / build:rpm (rocky9_nocuda) (pull_request) Successful in 21m5s
Build Packages / build:rpm (rocky8_nocuda) (pull_request) Successful in 24m42s
Build Packages / build:rpm (ubuntu2204_nocuda) (pull_request) Successful in 24m50s
Build Packages / build:rpm (rocky9_sls9) (pull_request) Successful in 22m6s
Build Packages / build:rpm (rocky8_sls9) (pull_request) Successful in 27m44s
Build Packages / build:rpm (rocky9) (pull_request) Successful in 24m45s
Build Packages / DIALS test (pull_request) Successful in 21m17s
Build Packages / build:rpm (rocky8) (pull_request) Successful in 28m21s
Build Packages / Generate python client (pull_request) Successful in 39s
Build Packages / Create release (pull_request) Skipped
Build Packages / Build documentation (pull_request) Successful in 1m38s
Build Packages / build:rpm (ubuntu2404) (pull_request) Successful in 23m38s
Build Packages / build:rpm (ubuntu2204) (pull_request) Successful in 27m43s
Build Packages / XDS test (durin plugin) (pull_request) Successful in 10m5s
Build Packages / XDS test (JFJoch plugin) (pull_request) Successful in 8m48s
Build Packages / XDS test (neggia plugin) (pull_request) Successful in 7m39s
Build Packages / Unit tests (pull_request) Successful in 1h55m24s
Build Packages / build:windows:nocuda (pull_request) Successful in 21m3s
Build Packages / build:windows:cuda (pull_request) Successful in 21m25s

A CollapsibleSection was born expanded and each caller folded it again, so the three that
never got round to it - geometry, unit cell, goniometer - greeted every start with three
open accordions and the rest of the settings pushed below the fold. The default is now
folded, both panels open as a list of headers, and the ten setExpanded(false) calls that
existed only to undo the old default are gone. The one remaining call is the ROI section
opening itself when ROIs appear, which is a real behaviour and still works.

Azimuthal integration was a page of the MX/AzInt/Calib stack, which left a calibration run
unable to reach the settings it depends on: calibrating from powder rings integrates the run
in azimuthal sectors and over the same Q range and spacing as any other integration, and the
Calib page could only report what the AzInt page had been set to. The section now lives
beside geometry, outside the stack, shown for both pages and hidden on MX - one set of
widgets over one AzimuthalIntegrationSettings, so there is nothing to keep in sync. The
too-few-sectors note moves into the powder section and appears only when the count is below
the four the rings fit needs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-13 15:21:41 +02:00
co-authored by Claude Opus 5
parent cac669495d
commit 93093c5f21
5 changed files with 33 additions and 38 deletions
-3
View File
@@ -134,7 +134,6 @@ JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent)
image_feature_grid->addLayout(legendLayout, 5, 0, 1, 2);
featuresSection->setContentLayout(image_feature_grid);
featuresSection->setExpanded(false);
layout->addWidget(featuresSection);
auto *ringsSection = new CollapsibleSection("Resolution rings", this);
@@ -152,7 +151,6 @@ JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent)
ringsLayout->addWidget(res_rings);
ringsSection->setContentLayout(ringsLayout);
ringsSection->setExpanded(false);
layout->addWidget(ringsSection);
// The per-image statistics plot now lives in the dataset-info dock (its "Per-image" toggle),
@@ -186,7 +184,6 @@ JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent)
connect(roi_list, &JFJochViewerROIList::maskFromROI, this, &JFJochViewerSidePanel::maskFromROI);
connect(roi_list, &JFJochViewerROIList::roisChanged, this, [this] { roiSection->setExpanded(true); });
roiSection->setContentLayout(roiLayout);
roiSection->setExpanded(false);
layout->addWidget(roiSection);
layout->addStretch();
+2 -2
View File
@@ -14,9 +14,8 @@ CollapsibleSection::CollapsibleSection(const QString &title, QWidget *parent) :
header_ = new QToolButton(this);
header_->setText(title);
header_->setCheckable(true);
header_->setChecked(true);
header_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
header_->setArrowType(Qt::DownArrow);
header_->setArrowType(Qt::RightArrow);
header_->setCursor(Qt::PointingHandCursor);
header_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
header_->setStyleSheet(
@@ -25,6 +24,7 @@ CollapsibleSection::CollapsibleSection(const QString &title, QWidget *parent) :
" border-left:3px solid #FA7268; border-bottom:1px solid #FA7268; background:transparent; }");
content_ = new QWidget(this);
content_->setVisible(false);
v->addWidget(header_);
v->addWidget(content_);
+1
View File
@@ -10,6 +10,7 @@ class QLayout;
// A slim collapsible section: a clickable navy header with a coral accent rule and a chevron,
// over a content area that folds away. Used to keep the side and settings panels compact.
// Starts folded, so a panel opens as a list of headers and the user unfolds what they need.
class CollapsibleSection : public QWidget {
Q_OBJECT
public:
+26 -32
View File
@@ -123,17 +123,23 @@ JFJochViewerSettingsDock::JFJochViewerSettingsDock(const SpotFindingSettings &sp
auto *stack = new QStackedWidget(this);
stack->addWidget(BuildMXPage());
stack->addWidget(BuildAzIntPage());
stack->addWidget(new QWidget(stack)); // AzInt: the shared section below is the whole page
stack->addWidget(BuildCalibrationPage());
connect(group, &QButtonGroup::idClicked, this, [this, stack](int id) {
mode_ = id == 1 ? ProcessMode::AzimuthalIntegration
: id == 2 ? ProcessMode::Calibration : ProcessMode::FullAnalysis;
stack->setCurrentIndex(id);
azintSection_->setVisible(id != 0);
});
// Geometry is common to both communities, so it lives above the toggle rather than per page.
// Geometry is common to all three, so it lives above the toggle rather than per page. Azimuthal
// integration is common to two of them - a calibration by rings integrates the run in azimuthal
// sectors, and needs the same Q range and spacing - so it is shared the same way and hidden on MX.
layout->addLayout(toggleRow);
layout->addWidget(BuildGeometrySection());
azintSection_ = BuildAzIntSection();
azintSection_->setVisible(false); // MX is the page selected on start
layout->addWidget(azintSection_);
layout->addWidget(stack);
layout->addStretch();
}
@@ -301,7 +307,6 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() {
spot->addRow("Min pixels/spot", minPix);
spot->addRow("Max spots/image", maxSpots);
spotSection->setContentLayout(spot);
spotSection->setExpanded(false);
layout->addWidget(spotSection);
connect(snr, &SliderPlusBox::valueChanged, this, [this](double v) {
spot_.signal_to_noise_threshold = static_cast<float>(v); EmitSpotFinding(); });
@@ -372,7 +377,6 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() {
idx->addRow("", algoDesc_);
idx->addRow("Refinement", refine);
idxSection->setContentLayout(idx);
idxSection->setExpanded(false);
layout->addWidget(idxSection);
connect(algo_, &QComboBox::currentIndexChanged, this, [this] {
indexing_.Algorithm(static_cast<IndexingAlgorithmEnum>(algo_->currentData().toInt()));
@@ -580,7 +584,6 @@ QWidget *JFJochViewerSettingsDock::BuildReferenceSection() {
form->addRow(refSummary_);
form->addRow(refWarning_);
section->setContentLayout(form);
section->setExpanded(false); // folded on start (only geometry + unit cell start open)
connect(refButton_, &QPushButton::clicked, this, [this] {
const QString path = QFileDialog::getOpenFileName(this, "Reference MTZ", refPath_,
@@ -599,25 +602,21 @@ QWidget *JFJochViewerSettingsDock::BuildReferenceSection() {
return section;
}
QWidget *JFJochViewerSettingsDock::BuildAzIntPage() {
auto *page = new QWidget(this);
auto *layout = new QVBoxLayout(page);
layout->setContentsMargins(0, 0, 0, 0);
auto *azSection = new CollapsibleSection("Azimuthal integration", page);
QWidget *JFJochViewerSettingsDock::BuildAzIntSection() {
auto *azSection = new CollapsibleSection("Azimuthal integration", this);
auto *az = new QFormLayout();
az->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
auto *lowQ = new SliderPlusBox(1e-5, 10.0, 0.001, 4, page);
auto *lowQ = new SliderPlusBox(1e-5, 10.0, 0.001, 4, this);
lowQ->setValue(azint_.GetLowQ_recipA());
auto *highQ = new SliderPlusBox(2e-5, 10.0, 0.001, 4, page);
auto *highQ = new SliderPlusBox(2e-5, 10.0, 0.001, 4, this);
highQ->setValue(azint_.GetHighQ_recipA());
auto *autoHighQ = new QCheckBox("To detector edge", page);
auto *autoHighQ = new QCheckBox("To detector edge", this);
autoHighQ->setChecked(!azint_.GetRequestedHighQ_recipA().has_value());
autoHighQ->setToolTip("Integrate out to the highest Q the detector reaches. The high-Q value is not "
"used while this is on.");
auto *spacing = new SliderPlusBox(1e-5, 1.0, 0.001, 5, page, SliderPlusBox::ScaleType::Logarithmic);
auto *spacing = new SliderPlusBox(1e-5, 1.0, 0.001, 5, this, SliderPlusBox::ScaleType::Logarithmic);
spacing->setValue(azint_.GetQSpacing_recipA());
auto *azimBins = new QComboBox(page);
auto *azimBins = new QComboBox(this);
for (int b : {1, 2, 4, 8, 16, 32, 64, 128})
azimBins->addItem(QString::number(b), b);
azimBins->setCurrentIndex(azimBins->findData(azint_.GetAzimuthalBinCount()));
@@ -627,8 +626,6 @@ QWidget *JFJochViewerSettingsDock::BuildAzIntPage() {
az->addRow("Q spacing [Å⁻¹]", spacing);
az->addRow("Azimuthal bins", azimBins);
azSection->setContentLayout(az);
azSection->setExpanded(false);
layout->addWidget(azSection);
auto emitAz = [=, this] {
azint_.QRange_recipA(static_cast<float>(lowQ->value()),
@@ -648,8 +645,7 @@ QWidget *JFJochViewerSettingsDock::BuildAzIntPage() {
});
highQ->setEnabled(!autoHighQ->isChecked());
layout->addStretch(); // anchor sections to the top so expanding an accordion grows downward
return page;
return azSection;
}
QWidget *JFJochViewerSettingsDock::BuildCalibrationPage() {
@@ -667,22 +663,22 @@ QWidget *JFJochViewerSettingsDock::BuildCalibrationPage() {
connect(powder_, &PowderCalibrationWidget::findBeamCenter, this, &JFJochViewerSettingsDock::findBeamCenter);
connect(powder_, &PowderCalibrationWidget::ringsFromCalibration, this, &JFJochViewerSettingsDock::ringsFromCalibration);
powderLayout->addWidget(powder_);
powderSection->setContentLayout(powderLayout);
layout->addWidget(powderSection);
// The rings method reads the ring's position at every azimuth, so the run has to be integrated in
// azimuthal sectors; one sector is a plain radial profile and cannot locate the ring. The bin count
// is the AzInt page's single setting - say here what it is set to rather than duplicating it.
// azimuthal sectors; one sector is a plain radial profile and cannot locate the ring. The sector
// count is the azimuthal-integration section above - say here when it is set too low to fit with.
auto *bins = new QLabel(page);
bins->setWordWrap(true);
bins->setStyleSheet("color: gray;");
layout->addWidget(bins);
powderLayout->addWidget(bins);
powderSection->setContentLayout(powderLayout);
layout->addWidget(powderSection);
auto refreshBins = [this, bins] {
bins->setText(QStringLiteral("Rings method: azimuthal bins (AzInt page) = %1%2")
.arg(azint_.GetAzimuthalBinCount())
.arg(azint_.GetAzimuthalBinCount() < 4
? QStringLiteral(" — too few to locate a ring; the run will use 32")
: QString()));
bins->setText(azint_.GetAzimuthalBinCount() < 4
? QStringLiteral("Rings method: too few azimuthal bins (%1) to locate a ring;"
" the run will use 32.").arg(azint_.GetAzimuthalBinCount())
: QString());
};
refreshBins();
connect(this, &JFJochViewerSettingsDock::azintChanged, this, [refreshBins] { refreshBins(); });
@@ -765,7 +761,6 @@ QWidget *JFJochViewerSettingsDock::BuildBraggSection() {
form->addRow("Radii r1/r2/r3", radii);
form->addRow("", trimRow);
section->setContentLayout(form);
section->setExpanded(false); // folded on start (only geometry + unit cell start open)
auto emitBragg = [=, this] {
bragg_.Integrator(gaussian->isChecked() ? IntegratorMode::ProfileGaussian : IntegratorMode::BoxSum);
@@ -833,7 +828,6 @@ QWidget *JFJochViewerSettingsDock::BuildScalingSection() {
lowResRow->addWidget(lowRes, 1);
form->addRow("", lowResRow);
section->setContentLayout(form);
section->setExpanded(false); // folded on start (only geometry + unit cell start open)
auto emitScaling = [=, this] {
scaling_.MergeFriedel(friedel->isChecked());
+4 -1
View File
@@ -133,6 +133,9 @@ private:
QString refPath_;
PowderCalibrationWidget *powder_ = nullptr;
// Azimuthal integration: one section, shared by the AzInt and Calib pages (a calibration by rings
// integrates the run in azimuthal sectors), so it sits outside the page stack and is hidden on MX.
QWidget *azintSection_ = nullptr;
QWidget *BuildGeometrySection();
QWidget *BuildMXPage();
@@ -140,7 +143,7 @@ private:
QWidget *BuildBraggSection();
QWidget *BuildScalingSection();
QWidget *BuildReferenceSection();
QWidget *BuildAzIntPage();
QWidget *BuildAzIntSection();
QWidget *BuildCalibrationPage();
void SyncMinPix();
void EmitSpotFinding();