From 93093c5f218ce3440cd009d64c8e92567248f527 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Thu, 13 Aug 2026 15:21:41 +0200 Subject: [PATCH] Viewer: every panel section starts folded, and calibration carries the azimuthal settings 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) --- viewer/JFJochViewerSidePanel.cpp | 3 -- viewer/widgets/CollapsibleSection.cpp | 4 +- viewer/widgets/CollapsibleSection.h | 1 + viewer/widgets/JFJochViewerSettingsDock.cpp | 58 +++++++++------------ viewer/widgets/JFJochViewerSettingsDock.h | 5 +- 5 files changed, 33 insertions(+), 38 deletions(-) diff --git a/viewer/JFJochViewerSidePanel.cpp b/viewer/JFJochViewerSidePanel.cpp index b3c60a86..63cca255 100644 --- a/viewer/JFJochViewerSidePanel.cpp +++ b/viewer/JFJochViewerSidePanel.cpp @@ -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(); diff --git a/viewer/widgets/CollapsibleSection.cpp b/viewer/widgets/CollapsibleSection.cpp index 86748d00..f6060208 100644 --- a/viewer/widgets/CollapsibleSection.cpp +++ b/viewer/widgets/CollapsibleSection.cpp @@ -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_); diff --git a/viewer/widgets/CollapsibleSection.h b/viewer/widgets/CollapsibleSection.h index 5bbf746a..b66720ea 100644 --- a/viewer/widgets/CollapsibleSection.h +++ b/viewer/widgets/CollapsibleSection.h @@ -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: diff --git a/viewer/widgets/JFJochViewerSettingsDock.cpp b/viewer/widgets/JFJochViewerSettingsDock.cpp index 6bd9ef5c..8a7e52f0 100644 --- a/viewer/widgets/JFJochViewerSettingsDock.cpp +++ b/viewer/widgets/JFJochViewerSettingsDock.cpp @@ -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(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(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(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()); diff --git a/viewer/widgets/JFJochViewerSettingsDock.h b/viewer/widgets/JFJochViewerSettingsDock.h index fb7f658f..00761a4f 100644 --- a/viewer/widgets/JFJochViewerSettingsDock.h +++ b/viewer/widgets/JFJochViewerSettingsDock.h @@ -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();