From 0ca6b251e59acc58bd83c07f2616f0b4e955e2ab Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Sat, 12 Sep 2026 07:31:54 +0200 Subject: [PATCH] jfjoch_viewer: no settings section is wider than the panel it sits in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Measured every section's content minimum width against the 330 px the settings dock gives it (the powder one, fixed yesterday, sat at 278): geometry 346, unit cell 355, goniometer 322, spot finding 370, indexing 321, bragg 287, scaling 297, azint 243, reference 149 Five over. Three causes, none of them about the section: - addRow("", w) reserves the label column and puts the widget in the field column, so a checkbox's whole width was added to the widest label. addRow(w) spans both columns instead, which is what a label-less row means anyway. Spot finding 370 -> 251. - A spin box is never narrower than its longest possible value, suffix included, and these rows hold two or three abreast. The unit moves to the row label, which is how the panel names units elsewhere ("High resolution [Å]"): "a, b, c [Å]", "Beam origin [px]", "Detector tilt [°]", "Step x, y [μm]", "Radii r1/r2/r3 [px]". A cell length can still be four digits and three decimals, so those six fields also divide the row rather than demand their own width. - A combo is never narrower than its longest entry - the same thing that pinned the powder panel. "Flex (best per-image refinement)" set the indexing section's width; it elides now, as the calibrant does. Every section is now 291 or less, and none of them moves the dock. Two inspector changes in the same panel: - An axis that is present but does not turn covers no angular range, so the image angle is stated as one number rather than "-145° + 0°". - Sample-head angles that stay put during a run - an SLS Smargon writes chi and phi - get a row of their own, "chi 0° / phi 0°". Both its labels stay empty for a file that carries none, so it costs nothing where it means nothing. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011oVDk7aukYqfPZBLHn5nGg --- docs/CHANGELOG.md | 3 + .../widgets/JFJochViewerImageStatistics.cpp | 27 +++++- viewer/widgets/JFJochViewerImageStatistics.h | 2 + viewer/widgets/JFJochViewerSettingsDock.cpp | 96 +++++++++++-------- 4 files changed, 85 insertions(+), 43 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f57d7eaa3..c0b76fab4 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -8,6 +8,9 @@ * The viewer plots spot count and background together on one plot, which is what a connection to a live source now shows by default. * The viewer's reprocessing panel is called Processing, and the jobs panel it shared that name with is called Jobs. * Expanding the viewer's powder-calibration section no longer widens the left-hand panel past the space it has, which used to push half of it out of sight. +* No section of the viewer's processing panel asks for more width than the panel has: geometry, unit cell, goniometer, spot finding and indexing all used to widen it when opened. +* The viewer states the angle of a rotation axis that does not turn as a single number, instead of adding a zero wedge to it. +* The viewer's image statistics show the sample-head chi and phi angles of a dataset that records them (an SLS Smargon), and nothing for one that does not. * Switching the viewer's plot between per-dataset and per-image no longer leaves the other mode's quantities in the selector, and each mode remembers what was last shown in it. * Re-initialising the broker after a DECTRIS run that never started no longer freezes it with no way to cancel. * Re-initialising the broker disconnects the previous run's stream reader, so a DECTRIS collection no longer starts and then stays empty because half its stream - the start message included - went to a reader nobody was listening to. diff --git a/viewer/widgets/JFJochViewerImageStatistics.cpp b/viewer/widgets/JFJochViewerImageStatistics.cpp index e254940f7..0b386dc66 100644 --- a/viewer/widgets/JFJochViewerImageStatistics.cpp +++ b/viewer/widgets/JFJochViewerImageStatistics.cpp @@ -123,6 +123,12 @@ JFJochViewerImageStatistics::JFJochViewerImageStatistics(QWidget *parent) : QWid rotation_angle = new QLabel(this); layout->addRow(rotation_angle_label, rotation_angle); + // Sample-head angles that do not move during a run (an SLS Smargon writes chi and phi). Both + // labels stay empty for a file that carries none, so the row costs nothing where it means nothing. + smargon_label = new QLabel(this); + smargon_angles = new QLabel(this); + layout->addRow(smargon_label, smargon_angles); + valid_values = new QLabel(this); layout->addRow(new QLabel("Valid values:"), valid_values); @@ -178,6 +184,8 @@ void JFJochViewerImageStatistics::loadImage(std::shared_ptrsetText(""); rotation_angle->setToolTip(""); rotation_angle_label->setText(""); + smargon_label->setText(""); + smargon_angles->setText(""); valid_values->setText(""); valid_values->setToolTip(""); spots->setText(""); @@ -235,10 +243,13 @@ void JFJochViewerImageStatistics::loadImage(std::shared_ptrsetToolTip(mkSampleTooltip(exp)); if (exp.GetGoniometer()) { + // An axis that is present but does not turn has no wedge to add: state the angle it stands at. + const float wedge = exp.GetGoniometer()->GetWedge_deg(); + const QString angle = TrimZeros(exp.GetGoniometer()->GetAngle_deg(image->ImageData().number), 3); rotation_angle_label->setText("Image angle:"); - rotation_angle->setText(QString("%1° + %2°") - .arg(TrimZeros(exp.GetGoniometer()->GetAngle_deg(image->ImageData().number), 3)) - .arg(TrimZeros(exp.GetGoniometer()->GetWedge_deg(), 3))); + rotation_angle->setText(wedge == 0.0f + ? QString("%1°").arg(angle) + : QString("%1° + %2°").arg(angle).arg(TrimZeros(wedge, 3))); rotation_angle->setToolTip(QString("Start angle: %1°
Increment: %2°") .arg(TrimZeros(exp.GetGoniometer()->GetStart_deg(), 3)) .arg(TrimZeros(exp.GetGoniometer()->GetIncrement_deg(), 3)) @@ -260,6 +271,16 @@ void JFJochViewerImageStatistics::loadImage(std::shared_ptrsetToolTip(""); } + if (const auto head = exp.GetDatasetSettings().GetSmargonPosition()) { + smargon_label->setText("Smargon:"); + smargon_angles->setText(QString("chi %1° / phi %2°") + .arg(TrimZeros(head->chi_deg, 3)) + .arg(TrimZeros(head->phi_deg, 3))); + } else { + smargon_label->setText(""); + smargon_angles->setText(""); + } + exposure_time->setText(QString("%1 s").arg(FormatTime(exp.GetImageTime()))); exposure_time->setToolTip(QString("Count time: %1 s
").arg(FormatTime(exp.GetImageCountTime()))); diff --git a/viewer/widgets/JFJochViewerImageStatistics.h b/viewer/widgets/JFJochViewerImageStatistics.h index bb7d721e6..090724023 100644 --- a/viewer/widgets/JFJochViewerImageStatistics.h +++ b/viewer/widgets/JFJochViewerImageStatistics.h @@ -21,6 +21,8 @@ class JFJochViewerImageStatistics : public QWidget { QLabel *exposure_time; QLabel *rotation_angle_label; QLabel *rotation_angle; + QLabel *smargon_label; + QLabel *smargon_angles; QLabel *spots; QLabel *valid_values; QLabel *indexed; diff --git a/viewer/widgets/JFJochViewerSettingsDock.cpp b/viewer/widgets/JFJochViewerSettingsDock.cpp index 8a7e52f02..2539599d6 100644 --- a/viewer/widgets/JFJochViewerSettingsDock.cpp +++ b/viewer/widgets/JFJochViewerSettingsDock.cpp @@ -142,6 +142,7 @@ JFJochViewerSettingsDock::JFJochViewerSettingsDock(const SpotFindingSettings &sp layout->addWidget(azintSection_); layout->addWidget(stack); layout->addStretch(); + } void JFJochViewerSettingsDock::setHttpConnection(bool connected, QString) { @@ -158,10 +159,13 @@ QWidget *JFJochViewerSettingsDock::BuildGeometrySection() { 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); + // Fields that share a row carry their unit in the row label instead of in a suffix: a spin box + // asks to be as wide as its longest possible value, and two or three of those abreast are wider + // than the dock. The panel already names units this way ("High resolution [Å]"). + beamX_ = new NumberLineEdit(-20000.0, 20000.0, 0.0, 1, "", this); + beamY_ = new NumberLineEdit(-20000.0, 20000.0, 0.0, 1, "", this); + rot1_ = new NumberLineEdit(-180.0, 180.0, 0.0, 3, "", this); + rot2_ = new NumberLineEdit(-180.0, 180.0, 0.0, 3, "", this); // Both are overwritten from the experiment as soon as a dataset is open; this is what they show // until then. polarizationOn_ = new QCheckBox("Polarization correction", this); @@ -197,9 +201,9 @@ QWidget *JFJochViewerSettingsDock::BuildGeometrySection() { geom->addRow("Photon energy", energy_); geom->addRow("Detector distance", distance_); - geom->addRow("Beam origin", beam); - geom->addRow("Detector tilt", tilt); - geom->addRow("", polarization); + geom->addRow("Beam origin [px]", beam); + geom->addRow("Detector tilt [°]", tilt); + geom->addRow(polarization); for (auto *f : {energy_, distance_, beamX_, beamY_, rot1_, rot2_, polarization_}) connect(f, &NumberLineEdit::newValue, this, [this] { EmitExperiment(); }); @@ -222,16 +226,22 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() { auto *cell = new QFormLayout(); cell->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); cellKnown_ = new QCheckBox("Known unit cell", page); - cell->addRow("", cellKnown_); - cellA_ = new NumberLineEdit(1.0, 2000.0, 79.0, 3, "Å", page); - cellB_ = new NumberLineEdit(1.0, 2000.0, 79.0, 3, "Å", page); - cellC_ = new NumberLineEdit(1.0, 2000.0, 38.0, 3, "Å", page); - cellAlpha_ = new NumberLineEdit(1.0, 179.0, 90.0, 2, "°", page); - cellBeta_ = new NumberLineEdit(1.0, 179.0, 90.0, 2, "°", page); - cellGamma_ = new NumberLineEdit(1.0, 179.0, 90.0, 2, "°", page); + cell->addRow(cellKnown_); + cellA_ = new NumberLineEdit(1.0, 2000.0, 79.0, 3, "", page); + cellB_ = new NumberLineEdit(1.0, 2000.0, 79.0, 3, "", page); + cellC_ = new NumberLineEdit(1.0, 2000.0, 38.0, 3, "", page); + cellAlpha_ = new NumberLineEdit(1.0, 179.0, 90.0, 2, "", page); + 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); + // Three fields to a row, and a cell length can be four digits: let the row divide the width it + // has instead of each field demanding the width of its longest possible value, which is what + // made this the one section that still pushed the dock wider than it is. + for (auto *f : {cellA_, cellB_, cellC_, cellAlpha_, cellBeta_, cellGamma_}) + f->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + auto *abc = new QHBoxLayout(); abc->addWidget(cellA_); abc->addWidget(cellB_); abc->addWidget(cellC_); auto *angles = new QHBoxLayout(); @@ -240,8 +250,8 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() { auto *sgRow = new QHBoxLayout(); sgRow->addWidget(spaceGroup_, 1); sgRow->addWidget(spaceGroupName_, 1); - cell->addRow("a, b, c", abc); - cell->addRow("α, β, γ", angles); + cell->addRow("a, b, c [Å]", abc); + cell->addRow("α, β, γ [°]", angles); cell->addRow("Space group", sgRow); cellSection->setContentLayout(cell); layout->addWidget(cellSection); @@ -298,12 +308,12 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() { // the value rejected further down. auto *maxSpots = new NumberLineEdit(10.0f, static_cast(MAX_SPOT_COUNT), static_cast(max_spots_), 0, "", page); - spot->addRow("", adaptive); + spot->addRow(adaptive); spot->addRow("Signal/noise", snr); spot->addRow("Photon count", count); - spot->addRow("", autoHighResSpot); + spot->addRow(autoHighResSpot); spot->addRow("High resolution [Å]", highResSpot); - spot->addRow("", adaptiveMinPix); + spot->addRow(adaptiveMinPix); spot->addRow("Min pixels/spot", minPix); spot->addRow("Max spots/image", maxSpots); spotSection->setContentLayout(spot); @@ -355,6 +365,10 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() { auto *idx = new QFormLayout(); idx->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); algo_ = new QComboBox(page); + // As in the calibrant combo: a combo is otherwise never narrower than its longest entry, which + // for these descriptions is wider than the dock. The current entry elides instead. + algo_->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + algo_->setMinimumContentsLength(12); algo_->addItem("Auto", static_cast(IndexingAlgorithmEnum::Auto)); algo_->addItem("FFBIDX (GPU, known cell)", static_cast(IndexingAlgorithmEnum::FFBIDX)); algo_->addItem("FFT (GPU, de-novo)", static_cast(IndexingAlgorithmEnum::FFT)); @@ -365,6 +379,8 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() { algoDesc_->setWordWrap(true); algoDesc_->setStyleSheet("color: gray;"); auto *refine = new QComboBox(page); + refine->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + refine->setMinimumContentsLength(12); refine->addItem("None", static_cast(GeomRefinementAlgorithmEnum::None)); refine->addItem("Orientation only", static_cast(GeomRefinementAlgorithmEnum::OrientationOnly)); refine->addItem("Beam center + lattice", static_cast(GeomRefinementAlgorithmEnum::BeamCenter)); @@ -374,7 +390,7 @@ QWidget *JFJochViewerSettingsDock::BuildMXPage() { refine->addItem("Flex (best per-image refinement)", static_cast(GeomRefinementAlgorithmEnum::Flex)); refine->setCurrentIndex(refine->findData(static_cast(indexing_.GetGeomRefinementAlgorithm()))); idx->addRow("Algorithm", algo_); - idx->addRow("", algoDesc_); + idx->addRow(algoDesc_); idx->addRow("Refinement", refine); idxSection->setContentLayout(idx); layout->addWidget(idxSection); @@ -442,8 +458,8 @@ QWidget *JFJochViewerSettingsDock::BuildGoniometerSection() { gridNFast_ = new NumberLineEdit(1.0, 100000.0, DEFAULT_GRID_N_FAST, 0, "", this); gridNFast_->setToolTip("Points along the fast axis. The number of slow rows follows from the " "number of images."); - gridStepX_ = new NumberLineEdit(-10000.0, 10000.0, DEFAULT_GRID_STEP_UM, 1, "μm", this); - gridStepY_ = new NumberLineEdit(-10000.0, 10000.0, DEFAULT_GRID_STEP_UM, 1, "μm", this); + gridStepX_ = new NumberLineEdit(-10000.0, 10000.0, DEFAULT_GRID_STEP_UM, 1, "", this); + gridStepY_ = new NumberLineEdit(-10000.0, 10000.0, DEFAULT_GRID_STEP_UM, 1, "", this); const QString stepTip = "Distance between neighbouring points, in x and y. Negative = the scan " "runs towards decreasing x / y."; gridStepX_->setToolTip(stepTip); @@ -464,17 +480,17 @@ QWidget *JFJochViewerSettingsDock::BuildGoniometerSection() { auto *gridFlags = new QHBoxLayout(); gridFlags->addWidget(gridVertical_); gridFlags->addWidget(gridSnake_); - form->addRow("", modeStill_); - form->addRow("", modeRotation_); + form->addRow(modeStill_); + form->addRow(modeRotation_); form->addRow("Axis name", axisName_); form->addRow("Axis vector", axis); form->addRow("Start angle", rotStart_); form->addRow("Increment", rotIncrement_); - form->addRow("", modeGrid_); + form->addRow(modeGrid_); form->addRow("Points (fast)", gridNFast_); - form->addRow("Step x, y", step); - form->addRow("", gridFlags); - form->addRow("", gridSummary_); + form->addRow("Step x, y [μm]", step); + form->addRow(gridFlags); + form->addRow(gridSummary_); section->setContentLayout(form); EnableRotationFields(false); // datasetLoaded picks the mode the file describes @@ -621,7 +637,7 @@ QWidget *JFJochViewerSettingsDock::BuildAzIntSection() { azimBins->addItem(QString::number(b), b); azimBins->setCurrentIndex(azimBins->findData(azint_.GetAzimuthalBinCount())); az->addRow("Low Q [Å⁻¹]", lowQ); - az->addRow("", autoHighQ); + az->addRow(autoHighQ); az->addRow("High Q [Å⁻¹]", highQ); az->addRow("Q spacing [Å⁻¹]", spacing); az->addRow("Azimuthal bins", azimBins); @@ -737,9 +753,9 @@ QWidget *JFJochViewerSettingsDock::BuildBraggSection() { auto *gaussian = new QCheckBox("Gaussian profile fit", this); gaussian->setChecked(bragg_.GetIntegrator() != IntegratorMode::BoxSum); gaussian->setToolTip("Profile-fit the spots (more accurate intensities); off = classical box-sum."); - auto *r1 = new NumberLineEdit(1.0f, 30.0f, bragg_.GetR1(), 1, "px", this); - auto *r2 = new NumberLineEdit(1.0f, 30.0f, bragg_.GetR2(), 1, "px", this); - auto *r3 = new NumberLineEdit(1.0f, 30.0f, bragg_.GetR3(), 1, "px", this); + auto *r1 = new NumberLineEdit(1.0f, 30.0f, bragg_.GetR1(), 1, "", this); + auto *r2 = new NumberLineEdit(1.0f, 30.0f, bragg_.GetR2(), 1, "", this); + auto *r3 = new NumberLineEdit(1.0f, 30.0f, bragg_.GetR3(), 1, "", this); auto *radii = new QHBoxLayout(); radii->addWidget(r1); radii->addWidget(r2); radii->addWidget(r3); // Background trim: use the old symmetric trimmed mean for the r2..r3 ring (drop the lowest and @@ -757,9 +773,9 @@ QWidget *JFJochViewerSettingsDock::BuildBraggSection() { auto *trimRow = new QHBoxLayout(); trimRow->addWidget(bkgTrim); trimRow->addWidget(bkgTrimFrac, 1); - form->addRow("", gaussian); - form->addRow("Radii r1/r2/r3", radii); - form->addRow("", trimRow); + form->addRow(gaussian); + form->addRow("Radii r1/r2/r3 [px]", radii); + form->addRow(trimRow); section->setContentLayout(form); auto emitBragg = [=, this] { @@ -814,19 +830,19 @@ QWidget *JFJochViewerSettingsDock::BuildScalingSection() { auto *lowRes = new NumberLineEdit(5.0f, 500.0f, scaling_.GetLowResolutionLimit_A().value_or(50.0), 1, "Å", this); lowRes->setEnabled(limitLowRes->isChecked()); - form->addRow("", friedel); - form->addRow("", corrections); - form->addRow("", partRefine); + form->addRow(friedel); + form->addRow(corrections); + form->addRow(partRefine); // Compact, and aligned with the checkboxes above: the limit checkbox + value sit together in the // field column (not as a row label, which would indent it differently). auto *resRow = new QHBoxLayout(); resRow->addWidget(limitRes); resRow->addWidget(highRes, 1); - form->addRow("", resRow); + form->addRow(resRow); auto *lowResRow = new QHBoxLayout(); lowResRow->addWidget(limitLowRes); lowResRow->addWidget(lowRes, 1); - form->addRow("", lowResRow); + form->addRow(lowResRow); section->setContentLayout(form); auto emitScaling = [=, this] {