viewer: panel polish — accordions, shared geometry, detector tilt, SG name
Declutter the side and settings panels (review items a–f): - New CollapsibleSection widget (slim navy header + coral rule + chevron). - Inspector: Image features / Resolution rings / ROI are now collapsible and start folded (ROI auto-expands when ROIs change); drop the Data-analysis and Powder-calibration sections (they live in the hero buttons and settings dock). - Settings dock: move Geometry to a shared section above the MX/AzInt toggle (both communities need it); add Detector tilt (PONI rot1/rot2, deg) and rename "Beam center" -> "Beam origin" with PONI/XDS tooltips; show the space-group number and resolved Hermann–Mauguin symbol on one line; wrap sections in accordions, anchored top with a bottom stretch so expanding one does not shift the others. - Toolbar: drop the redundant "Image number" label. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,8 @@ ADD_EXECUTABLE(jfjoch_viewer jfjoch_viewer.cpp JFJochViewerWindow.cpp JFJochView
|
||||
widgets/JFJochViewerSidePanelChart.h
|
||||
widgets/JFJochViewerSettingsDock.cpp
|
||||
widgets/JFJochViewerSettingsDock.h
|
||||
widgets/CollapsibleSection.cpp
|
||||
widgets/CollapsibleSection.h
|
||||
windows/JFJochViewerSpotListWindow.cpp
|
||||
windows/JFJochViewerSpotListWindow.h
|
||||
image_viewer/JFJochAzIntImage.cpp
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#include "JFJochViewerSidePanel.h"
|
||||
|
||||
#include "widgets/TitleLabel.h"
|
||||
#include "widgets/CollapsibleSection.h"
|
||||
#include "widgets/JFJochViewerImageStatistics.h"
|
||||
#include "widgets/PowderCalibrationWidget.h"
|
||||
|
||||
JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent) {
|
||||
|
||||
@@ -21,7 +21,7 @@ JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent)
|
||||
layout->addWidget(stats);
|
||||
connect(this, &JFJochViewerSidePanel::imageLoaded, stats, &JFJochViewerImageStatistics::loadImage);
|
||||
|
||||
layout->addWidget(new TitleLabel("Image features", this));
|
||||
auto *featuresSection = new CollapsibleSection("Image features", this);
|
||||
|
||||
auto spotToggleCheckBox = new QCheckBox("Show spots", this);
|
||||
spotToggleCheckBox->setCheckState(Qt::CheckState::Unchecked);
|
||||
@@ -84,9 +84,13 @@ JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent)
|
||||
image_feature_grid->addWidget(colorSelectButton, 3, 0);
|
||||
image_feature_grid->addWidget(spotColorSelectButton, 3, 1);
|
||||
|
||||
layout->addLayout(image_feature_grid);
|
||||
featuresSection->setContentLayout(image_feature_grid);
|
||||
featuresSection->setExpanded(false);
|
||||
layout->addWidget(featuresSection);
|
||||
|
||||
layout->addWidget(new TitleLabel("Resolution rings", this));
|
||||
auto *ringsSection = new CollapsibleSection("Resolution rings", this);
|
||||
auto *ringsLayout = new QVBoxLayout();
|
||||
ringsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
res_rings = new ResolutionRingWidget(this);
|
||||
|
||||
connect(res_rings, &ResolutionRingWidget::resRingsSet, [this] (const QVector<float> &v) {
|
||||
@@ -97,12 +101,18 @@ JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent)
|
||||
emit ringModeSet(mode);
|
||||
});
|
||||
|
||||
layout->addWidget(res_rings);
|
||||
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),
|
||||
// so it is no longer duplicated here.
|
||||
// and powder calibration / full analysis live in the settings dock and the hero buttons,
|
||||
// so they are no longer duplicated here.
|
||||
|
||||
layout->addWidget(new TitleLabel("ROI", this));
|
||||
roiSection = new CollapsibleSection("ROI", this);
|
||||
auto *roiLayout = new QVBoxLayout();
|
||||
roiLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
auto roiLabelsCheckBox = new QCheckBox("Show labels", this);
|
||||
roiLabelsCheckBox->setCheckState(Qt::CheckState::Unchecked);
|
||||
@@ -115,35 +125,20 @@ JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent)
|
||||
auto roiToggleRow = new QHBoxLayout();
|
||||
roiToggleRow->addWidget(roiLabelsCheckBox);
|
||||
roiToggleRow->addWidget(roiFillCheckBox);
|
||||
layout->addLayout(roiToggleRow);
|
||||
roiLayout->addLayout(roiToggleRow);
|
||||
|
||||
roi_list = new JFJochViewerROIList(this);
|
||||
layout->addWidget(roi_list);
|
||||
roiLayout->addWidget(roi_list);
|
||||
connect(this, &JFJochViewerSidePanel::imageLoaded, roi_list, &JFJochViewerROIList::loadImage);
|
||||
connect(roi_list, &JFJochViewerROIList::roisChanged, this, &JFJochViewerSidePanel::roisChanged);
|
||||
connect(roi_list, &JFJochViewerROIList::selectedROIChanged, this, &JFJochViewerSidePanel::selectedROIChanged);
|
||||
connect(roi_list, &JFJochViewerROIList::downloadROIs, this, &JFJochViewerSidePanel::downloadROIs);
|
||||
connect(roi_list, &JFJochViewerROIList::uploadROIs, this, &JFJochViewerSidePanel::uploadROIs);
|
||||
connect(roi_list, &JFJochViewerROIList::maskFromROI, this, &JFJochViewerSidePanel::maskFromROI);
|
||||
|
||||
layout->addWidget(new TitleLabel("Data analysis", this));
|
||||
|
||||
auto analyzeButton = new QPushButton("Full analysis", this);
|
||||
connect(analyzeButton, &QPushButton::clicked,[this] {emit analyze();});
|
||||
layout->addWidget(analyzeButton);
|
||||
|
||||
// Calibrant selection combo box
|
||||
layout->addWidget(new TitleLabel("Powder geometry calibration", this));
|
||||
|
||||
auto power_calibration = new PowderCalibrationWidget(this);
|
||||
layout->addWidget(power_calibration);
|
||||
|
||||
connect(this, &JFJochViewerSidePanel::imageLoaded, power_calibration, &PowderCalibrationWidget::loadImage);
|
||||
connect(power_calibration, &PowderCalibrationWidget::ringsFromCalibration, res_rings, &ResolutionRingWidget::setRings);
|
||||
connect(power_calibration, &PowderCalibrationWidget::findBeamCenter,
|
||||
[this] (const UnitCell& input, bool guess) {
|
||||
emit findBeamCenter(input, guess);
|
||||
});
|
||||
connect(roi_list, &JFJochViewerROIList::roisChanged, this, [this] { roiSection->setExpanded(true); });
|
||||
roiSection->setContentLayout(roiLayout);
|
||||
roiSection->setExpanded(false);
|
||||
layout->addWidget(roiSection);
|
||||
|
||||
layout->addStretch();
|
||||
setLayout(layout); // Set the layout to the widget
|
||||
|
||||
@@ -13,11 +13,14 @@
|
||||
#include "charts/JFJochSimpleChartView.h"
|
||||
#include "widgets/ResolutionRingWidget.h"
|
||||
|
||||
class CollapsibleSection;
|
||||
|
||||
class JFJochViewerSidePanel : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
JFJochViewerROIList *roi_list = nullptr;
|
||||
ResolutionRingWidget *res_rings = nullptr;
|
||||
CollapsibleSection *roiSection = nullptr; // auto-expands when ROIs change
|
||||
|
||||
signals:
|
||||
void showSpots(bool input);
|
||||
|
||||
@@ -8,9 +8,6 @@
|
||||
JFJochViewerToolbarImage::JFJochViewerToolbarImage(QWidget *parent) : QToolBar(parent) {
|
||||
image_count_in_dataset = 0;
|
||||
|
||||
auto title = new QLabel("<b>Image number</b> ", this);
|
||||
addWidget(title);
|
||||
|
||||
leftmost_button = new QPushButton(this);
|
||||
leftmost_button->setIcon(style()->standardIcon(QStyle::SP_MediaSkipBackward));
|
||||
leftmost_button->setToolTip("First image");
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include "CollapsibleSection.h"
|
||||
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
CollapsibleSection::CollapsibleSection(const QString &title, QWidget *parent) : QWidget(parent) {
|
||||
auto *v = new QVBoxLayout(this);
|
||||
v->setContentsMargins(0, 0, 0, 0);
|
||||
v->setSpacing(0);
|
||||
|
||||
header_ = new QToolButton(this);
|
||||
header_->setText(title);
|
||||
header_->setCheckable(true);
|
||||
header_->setChecked(true);
|
||||
header_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
header_->setArrowType(Qt::DownArrow);
|
||||
header_->setCursor(Qt::PointingHandCursor);
|
||||
header_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
header_->setStyleSheet(
|
||||
"QToolButton { color:#1F3A5F; font-weight:bold; text-align:left;"
|
||||
" padding:3px 6px 3px 6px; border:none;"
|
||||
" border-left:3px solid #FA7268; border-bottom:1px solid #FA7268; background:transparent; }");
|
||||
|
||||
content_ = new QWidget(this);
|
||||
|
||||
v->addWidget(header_);
|
||||
v->addWidget(content_);
|
||||
|
||||
connect(header_, &QToolButton::toggled, this, [this](bool on) {
|
||||
content_->setVisible(on);
|
||||
header_->setArrowType(on ? Qt::DownArrow : Qt::RightArrow);
|
||||
});
|
||||
}
|
||||
|
||||
void CollapsibleSection::setContentLayout(QLayout *layout) {
|
||||
content_->setLayout(layout);
|
||||
}
|
||||
|
||||
void CollapsibleSection::setExpanded(bool on) {
|
||||
header_->setChecked(on);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QToolButton;
|
||||
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.
|
||||
class CollapsibleSection : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CollapsibleSection(const QString &title, QWidget *parent = nullptr);
|
||||
void setContentLayout(QLayout *layout);
|
||||
void setExpanded(bool on);
|
||||
|
||||
private:
|
||||
QToolButton *header_;
|
||||
QWidget *content_;
|
||||
};
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QPushButton>
|
||||
#include <QButtonGroup>
|
||||
#include <QStackedWidget>
|
||||
@@ -19,7 +18,9 @@
|
||||
#include "SliderPlusBox.h"
|
||||
#include "NumberLineEdit.h"
|
||||
#include "PowderCalibrationWidget.h"
|
||||
#include "TitleLabel.h"
|
||||
#include "CollapsibleSection.h"
|
||||
#include "../../common/JFJochMath.h"
|
||||
#include "../../gemmi_gph/gemmi/symmetry.hpp"
|
||||
|
||||
JFJochViewerSettingsDock::JFJochViewerSettingsDock(const SpotFindingSettings &spot,
|
||||
const IndexingSettings &indexing,
|
||||
@@ -50,39 +51,63 @@ JFJochViewerSettingsDock::JFJochViewerSettingsDock(const SpotFindingSettings &sp
|
||||
stack->addWidget(BuildAzIntPage());
|
||||
connect(group, &QButtonGroup::idClicked, stack, &QStackedWidget::setCurrentIndex);
|
||||
|
||||
// Geometry is common to both communities, so it lives above the toggle rather than per page.
|
||||
auto *layout = new QVBoxLayout(this);
|
||||
layout->addLayout(toggleRow);
|
||||
layout->addWidget(BuildGeometrySection());
|
||||
layout->addWidget(stack);
|
||||
layout->addStretch();
|
||||
}
|
||||
|
||||
QWidget *JFJochViewerSettingsDock::BuildGeometrySection() {
|
||||
auto *section = new CollapsibleSection("Geometry", this);
|
||||
auto *geom = new QFormLayout();
|
||||
|
||||
energy_ = new NumberLineEdit(1.0, 200.0, 12.4, 4, "keV", this);
|
||||
distance_ = new NumberLineEdit(10.0, 5000.0, 100.0, 2, "mm", this);
|
||||
beamX_ = new NumberLineEdit(-20000.0, 20000.0, 0.0, 1, "px", this);
|
||||
beamY_ = new NumberLineEdit(-20000.0, 20000.0, 0.0, 1, "px", this);
|
||||
rot1_ = new NumberLineEdit(-180.0, 180.0, 0.0, 3, "°", this);
|
||||
rot2_ = new NumberLineEdit(-180.0, 180.0, 0.0, 3, "°", this);
|
||||
|
||||
// The detector origin is the PONI (PyFAI) point; in MX (XDS-style) terms it is the beam origin,
|
||||
// which coincides with the beam center only when the detector is untilted.
|
||||
const QString beamTip =
|
||||
"Beam origin (XDS convention): the PONI point where the un-tilted beam meets the detector. "
|
||||
"Equals the beam center only when the detector tilt is zero.";
|
||||
beamX_->setToolTip(beamTip);
|
||||
beamY_->setToolTip(beamTip);
|
||||
const QString tiltTip = "Detector tilt about the two in-plane axes (PyFAI PONI rot1 / rot2), in degrees.";
|
||||
rot1_->setToolTip(tiltTip);
|
||||
rot2_->setToolTip(tiltTip);
|
||||
|
||||
auto *beam = new QHBoxLayout();
|
||||
beam->addWidget(beamX_);
|
||||
beam->addWidget(beamY_);
|
||||
auto *tilt = new QHBoxLayout();
|
||||
tilt->addWidget(rot1_);
|
||||
tilt->addWidget(rot2_);
|
||||
|
||||
geom->addRow("Photon energy", energy_);
|
||||
geom->addRow("Detector distance", distance_);
|
||||
geom->addRow("Beam origin", beam);
|
||||
geom->addRow("Detector tilt", tilt);
|
||||
|
||||
for (auto *f : {energy_, distance_, beamX_, beamY_, rot1_, rot2_})
|
||||
connect(f, &NumberLineEdit::newValue, this, [this] { EmitExperiment(); });
|
||||
|
||||
section->setContentLayout(geom);
|
||||
return section;
|
||||
}
|
||||
|
||||
QWidget *JFJochViewerSettingsDock::BuildMXPage() {
|
||||
auto *page = new QWidget(this);
|
||||
auto *layout = new QVBoxLayout(page);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
// --- Geometry ---
|
||||
layout->addWidget(new TitleLabel("Geometry", page));
|
||||
auto *geomGroup = new QGroupBox(page);
|
||||
auto *geom = new QFormLayout(geomGroup);
|
||||
energy_ = new NumberLineEdit(1.0, 200.0, 12.4, 4, "keV", page);
|
||||
distance_ = new NumberLineEdit(10.0, 5000.0, 100.0, 2, "mm", page);
|
||||
beamX_ = new NumberLineEdit(0.0, 20000.0, 0.0, 1, "px", page);
|
||||
beamY_ = new NumberLineEdit(0.0, 20000.0, 0.0, 1, "px", page);
|
||||
geom->addRow("Photon energy", energy_);
|
||||
geom->addRow("Detector distance", distance_);
|
||||
auto *beam = new QHBoxLayout();
|
||||
beam->addWidget(beamX_);
|
||||
beam->addWidget(beamY_);
|
||||
geom->addRow("Beam center", beam);
|
||||
layout->addWidget(geomGroup);
|
||||
for (auto *f : {energy_, distance_, beamX_, beamY_})
|
||||
connect(f, &NumberLineEdit::newValue, this, [this] { EmitExperiment(); });
|
||||
|
||||
// --- Unit cell + space group (new: no input existed before) ---
|
||||
layout->addWidget(new TitleLabel("Unit cell", page));
|
||||
auto *cellGroup = new QGroupBox(page);
|
||||
auto *cell = new QFormLayout(cellGroup);
|
||||
auto *cellSection = new CollapsibleSection("Unit cell", page);
|
||||
auto *cell = new QFormLayout();
|
||||
cellKnown_ = new QCheckBox("Known unit cell", page);
|
||||
cell->addRow("", cellKnown_);
|
||||
cellA_ = new NumberLineEdit(1.0, 2000.0, 79.0, 3, "Å", page);
|
||||
@@ -92,14 +117,21 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() {
|
||||
cellBeta_ = new NumberLineEdit(1.0, 179.0, 90.0, 2, "°", page);
|
||||
cellGamma_ = new NumberLineEdit(1.0, 179.0, 90.0, 2, "°", page);
|
||||
spaceGroup_ = new NumberLineEdit(0.0, 230.0, 0.0, 0, "", page);
|
||||
spaceGroup_->setToolTip("Space group number (1–230); 0 = unset.");
|
||||
spaceGroupName_ = new QLabel("—", page);
|
||||
auto *abc = new QHBoxLayout();
|
||||
abc->addWidget(cellA_); abc->addWidget(cellB_); abc->addWidget(cellC_);
|
||||
auto *angles = new QHBoxLayout();
|
||||
angles->addWidget(cellAlpha_); angles->addWidget(cellBeta_); angles->addWidget(cellGamma_);
|
||||
// Number and symbol split the line evenly, like the beam-origin row.
|
||||
auto *sgRow = new QHBoxLayout();
|
||||
sgRow->addWidget(spaceGroup_, 1);
|
||||
sgRow->addWidget(spaceGroupName_, 1);
|
||||
cell->addRow("a, b, c", abc);
|
||||
cell->addRow("α, β, γ", angles);
|
||||
cell->addRow("Space group", spaceGroup_);
|
||||
layout->addWidget(cellGroup);
|
||||
cell->addRow("Space group", sgRow);
|
||||
cellSection->setContentLayout(cell);
|
||||
layout->addWidget(cellSection);
|
||||
|
||||
auto enableCellFields = [this](bool on) {
|
||||
for (auto *f : {cellA_, cellB_, cellC_, cellAlpha_, cellBeta_, cellGamma_, spaceGroup_})
|
||||
@@ -108,15 +140,19 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() {
|
||||
enableCellFields(false);
|
||||
connect(cellKnown_, &QCheckBox::toggled, this, [this, enableCellFields](bool on) {
|
||||
enableCellFields(on);
|
||||
UpdateSpaceGroupName();
|
||||
EmitExperiment();
|
||||
});
|
||||
for (auto *f : {cellA_, cellB_, cellC_, cellAlpha_, cellBeta_, cellGamma_, spaceGroup_})
|
||||
for (auto *f : {cellA_, cellB_, cellC_, cellAlpha_, cellBeta_, cellGamma_})
|
||||
connect(f, &NumberLineEdit::newValue, this, [this] { EmitExperiment(); });
|
||||
connect(spaceGroup_, &NumberLineEdit::newValue, this, [this] {
|
||||
UpdateSpaceGroupName();
|
||||
EmitExperiment();
|
||||
});
|
||||
|
||||
// --- Spot finding ---
|
||||
layout->addWidget(new TitleLabel("Spot finding", page));
|
||||
auto *spotGroup = new QGroupBox(page);
|
||||
auto *spot = new QFormLayout(spotGroup);
|
||||
auto *spotSection = new CollapsibleSection("Spot finding", page);
|
||||
auto *spot = new QFormLayout();
|
||||
auto *snr = new SliderPlusBox(1.0, 10.0, 0.1, 1, page);
|
||||
snr->setValue(spot_.signal_to_noise_threshold);
|
||||
auto *count = new SliderPlusBox(0.0, 100.0, 1.0, 0, page);
|
||||
@@ -126,7 +162,8 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() {
|
||||
spot->addRow("Signal/noise", snr);
|
||||
spot->addRow("Photon count", count);
|
||||
spot->addRow("Min pixels/spot", minPix);
|
||||
layout->addWidget(spotGroup);
|
||||
spotSection->setContentLayout(spot);
|
||||
layout->addWidget(spotSection);
|
||||
connect(snr, &SliderPlusBox::valueChanged, this, [this](double v) {
|
||||
spot_.signal_to_noise_threshold = static_cast<float>(v); EmitSpotFinding(); });
|
||||
connect(count, &SliderPlusBox::valueChanged, this, [this](double v) {
|
||||
@@ -135,9 +172,8 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() {
|
||||
spot_.min_pix_per_spot = std::lround(v); EmitSpotFinding(); });
|
||||
|
||||
// --- Indexing ---
|
||||
layout->addWidget(new TitleLabel("Indexing", page));
|
||||
auto *idxGroup = new QGroupBox(page);
|
||||
auto *idx = new QFormLayout(idxGroup);
|
||||
auto *idxSection = new CollapsibleSection("Indexing", page);
|
||||
auto *idx = new QFormLayout();
|
||||
auto *algo = new QComboBox(page);
|
||||
algo->addItem("Auto", static_cast<int>(IndexingAlgorithmEnum::Auto));
|
||||
algo->addItem("FFBIDX (GPU, known cell)", static_cast<int>(IndexingAlgorithmEnum::FFBIDX));
|
||||
@@ -153,7 +189,8 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() {
|
||||
refine->setCurrentIndex(refine->findData(static_cast<int>(indexing_.GetGeomRefinementAlgorithm())));
|
||||
idx->addRow("Algorithm", algo);
|
||||
idx->addRow("Refinement", refine);
|
||||
layout->addWidget(idxGroup);
|
||||
idxSection->setContentLayout(idx);
|
||||
layout->addWidget(idxSection);
|
||||
connect(algo, &QComboBox::currentIndexChanged, this, [this, algo] {
|
||||
indexing_.Algorithm(static_cast<IndexingAlgorithmEnum>(algo->currentData().toInt()));
|
||||
EmitSpotFinding();
|
||||
@@ -163,6 +200,7 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() {
|
||||
EmitSpotFinding();
|
||||
});
|
||||
|
||||
layout->addStretch(); // anchor sections to the top so expanding an accordion grows downward
|
||||
return page;
|
||||
}
|
||||
|
||||
@@ -171,9 +209,8 @@ QWidget *JFJochViewerSettingsDock::BuildAzIntPage() {
|
||||
auto *layout = new QVBoxLayout(page);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
layout->addWidget(new TitleLabel("Azimuthal integration", page));
|
||||
auto *azGroup = new QGroupBox(page);
|
||||
auto *az = new QFormLayout(azGroup);
|
||||
auto *azSection = new CollapsibleSection("Azimuthal integration", page);
|
||||
auto *az = new QFormLayout();
|
||||
auto *lowQ = new SliderPlusBox(1e-5, 10.0, 0.001, 4, page);
|
||||
lowQ->setValue(azint_.GetLowQ_recipA());
|
||||
auto *highQ = new SliderPlusBox(2e-5, 10.0, 0.001, 4, page);
|
||||
@@ -188,7 +225,8 @@ QWidget *JFJochViewerSettingsDock::BuildAzIntPage() {
|
||||
az->addRow("High Q [Å⁻¹]", highQ);
|
||||
az->addRow("Q spacing [Å⁻¹]", spacing);
|
||||
az->addRow("Azimuthal bins", azimBins);
|
||||
layout->addWidget(azGroup);
|
||||
azSection->setContentLayout(az);
|
||||
layout->addWidget(azSection);
|
||||
|
||||
auto emitAz = [=, this] {
|
||||
azint_.QRange_recipA(static_cast<float>(lowQ->value()), static_cast<float>(highQ->value()));
|
||||
@@ -202,12 +240,17 @@ QWidget *JFJochViewerSettingsDock::BuildAzIntPage() {
|
||||
connect(azimBins, &QComboBox::currentIndexChanged, this, [emitAz] { emitAz(); });
|
||||
|
||||
// Powder calibration (calibrant rings + geometry refinement) - reuse the existing widget.
|
||||
layout->addWidget(new TitleLabel("Powder calibration", page));
|
||||
auto *powderSection = new CollapsibleSection("Powder calibration", page);
|
||||
auto *powderLayout = new QVBoxLayout();
|
||||
powderLayout->setContentsMargins(0, 0, 0, 0);
|
||||
powder_ = new PowderCalibrationWidget(page);
|
||||
connect(powder_, &PowderCalibrationWidget::findBeamCenter, this, &JFJochViewerSettingsDock::findBeamCenter);
|
||||
connect(powder_, &PowderCalibrationWidget::ringsFromCalibration, this, &JFJochViewerSettingsDock::ringsFromCalibration);
|
||||
layout->addWidget(powder_);
|
||||
powderLayout->addWidget(powder_);
|
||||
powderSection->setContentLayout(powderLayout);
|
||||
layout->addWidget(powderSection);
|
||||
|
||||
layout->addStretch(); // anchor sections to the top so expanding an accordion grows downward
|
||||
return page;
|
||||
}
|
||||
|
||||
@@ -222,6 +265,8 @@ void JFJochViewerSettingsDock::EmitExperiment() {
|
||||
experiment_.DetectorDistance_mm(static_cast<float>(distance_->value()));
|
||||
experiment_.BeamX_pxl(static_cast<float>(beamX_->value()));
|
||||
experiment_.BeamY_pxl(static_cast<float>(beamY_->value()));
|
||||
experiment_.PoniRot1_rad(static_cast<float>(rot1_->value() * PI / 180.0));
|
||||
experiment_.PoniRot2_rad(static_cast<float>(rot2_->value() * PI / 180.0));
|
||||
if (cellKnown_->isChecked()) {
|
||||
experiment_.SetUnitCell(UnitCell{
|
||||
static_cast<float>(cellA_->value()), static_cast<float>(cellB_->value()),
|
||||
@@ -243,6 +288,8 @@ void JFJochViewerSettingsDock::RefreshGeometryFields() {
|
||||
distance_->setValue(experiment_.GetDetectorDistance_mm());
|
||||
beamX_->setValue(experiment_.GetBeamX_pxl());
|
||||
beamY_->setValue(experiment_.GetBeamY_pxl());
|
||||
rot1_->setValue(experiment_.GetPoniRot1_rad() * 180.0 / PI);
|
||||
rot2_->setValue(experiment_.GetPoniRot2_rad() * 180.0 / PI);
|
||||
|
||||
const auto cell = experiment_.GetUnitCell();
|
||||
QSignalBlocker blockKnown(cellKnown_);
|
||||
@@ -254,6 +301,25 @@ void JFJochViewerSettingsDock::RefreshGeometryFields() {
|
||||
cellAlpha_->setValue(cell->alpha); cellBeta_->setValue(cell->beta); cellGamma_->setValue(cell->gamma);
|
||||
spaceGroup_->setValue(experiment_.GetSpaceGroupNumber().value_or(0));
|
||||
}
|
||||
UpdateSpaceGroupName();
|
||||
}
|
||||
|
||||
void JFJochViewerSettingsDock::UpdateSpaceGroupName() {
|
||||
if (!cellKnown_->isChecked()) {
|
||||
spaceGroupName_->setText("—");
|
||||
return;
|
||||
}
|
||||
const int n = static_cast<int>(std::lround(spaceGroup_->value()));
|
||||
if (n >= 1 && n <= 230) {
|
||||
try {
|
||||
const auto &sg = gemmi::get_spacegroup_by_number(n); // Hermann–Mauguin short symbol
|
||||
spaceGroupName_->setText(QString::fromStdString(sg.short_name()));
|
||||
} catch (...) {
|
||||
spaceGroupName_->setText("invalid");
|
||||
}
|
||||
} else {
|
||||
spaceGroupName_->setText(n == 0 ? "—" : "invalid");
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochViewerSettingsDock::datasetLoaded(std::shared_ptr<const JFJochReaderDataset> dataset) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
class QStackedWidget;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QLabel;
|
||||
class SliderPlusBox;
|
||||
class NumberLineEdit;
|
||||
class PowderCalibrationWidget;
|
||||
@@ -48,21 +49,27 @@ private:
|
||||
bool have_experiment_ = false; // geometry edits only take effect once a dataset is loaded
|
||||
int64_t max_spots_ = 1000;
|
||||
|
||||
// MX geometry + crystal fields
|
||||
// Geometry fields (shared by both MX and AzInt, since both need the same geometry)
|
||||
NumberLineEdit *energy_ = nullptr;
|
||||
NumberLineEdit *distance_ = nullptr;
|
||||
NumberLineEdit *beamX_ = nullptr;
|
||||
NumberLineEdit *beamY_ = nullptr;
|
||||
NumberLineEdit *rot1_ = nullptr; // detector tilt (PONI rot1), deg
|
||||
NumberLineEdit *rot2_ = nullptr; // detector tilt (PONI rot2), deg
|
||||
// Crystal fields
|
||||
QCheckBox *cellKnown_ = nullptr;
|
||||
NumberLineEdit *cellA_ = nullptr, *cellB_ = nullptr, *cellC_ = nullptr;
|
||||
NumberLineEdit *cellAlpha_ = nullptr, *cellBeta_ = nullptr, *cellGamma_ = nullptr;
|
||||
NumberLineEdit *spaceGroup_ = nullptr;
|
||||
QLabel *spaceGroupName_ = nullptr; // resolved Hermann–Mauguin symbol
|
||||
|
||||
PowderCalibrationWidget *powder_ = nullptr;
|
||||
|
||||
QWidget *BuildGeometrySection();
|
||||
QWidget *BuildMXPage();
|
||||
QWidget *BuildAzIntPage();
|
||||
void EmitSpotFinding();
|
||||
void EmitExperiment();
|
||||
void RefreshGeometryFields();
|
||||
void UpdateSpaceGroupName();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user