viewer: ROI panel refinements — persist edits, combobox, compact toggles

- Edited ROIs now override the file's for every subsequently loaded image
  and survive settings changes, until a new file is opened (previously a
  delete/add was lost on the next image). Tracked via an roi_override_ that
  is re-applied to curr_experiment and to each loaded image's dataset.
- The ROI selector is a compact combobox instead of a list widget.
- "Show labels" and "Translucent fill" share one row to save space.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 13:56:09 +02:00
co-authored by Claude Opus 4.8
parent 2f8d486b51
commit e49a908862
5 changed files with 47 additions and 29 deletions
+18 -1
View File
@@ -271,6 +271,7 @@ void JFJochImageReadingWorker::LoadFile_i(const QString &filename, qint64 image_
current_file = filename;
if (dataset) {
curr_experiment = dataset->experiment;
roi_override_.reset(); // new file: use its ROIs, forget any earlier edits
curr_experiment.ImportIndexingSettings(indexing_settings);
curr_experiment.ImportAzimuthalIntegrationSettings(azint_settings);
UpdateAzint_i(dataset.get());
@@ -378,6 +379,8 @@ void JFJochImageReadingWorker::LoadImage_i(int64_t image_number, int64_t summati
return;
}
ApplyROIOverrideToImage_i(); // edited ROIs win over the file's for every image
current_image = current_image_ptr->ImageData().number;
current_summation = summation;
@@ -466,6 +469,8 @@ void JFJochImageReadingWorker::UpdateDataset_i(const std::optional<DiffractionEx
}
curr_experiment = dataset->experiment;
if (roi_override_)
curr_experiment.ROI().SetROI(*roi_override_); // keep edited ROIs across settings changes
curr_experiment.ImportIndexingSettings(indexing_settings);
curr_experiment.ImportAzimuthalIntegrationSettings(azint_settings);
UpdateAzint_i(dataset.get());
@@ -473,6 +478,7 @@ void JFJochImageReadingWorker::UpdateDataset_i(const std::optional<DiffractionEx
emit datasetLoaded(dataset);
current_image_ptr = std::make_shared<JFJochReaderImage>(current_image_ptr->ImageData(), dataset);
ApplyROIOverrideToImage_i();
if (auto_reanalyze)
ReanalyzeImage_i();
@@ -596,7 +602,9 @@ void JFJochImageReadingWorker::SetROIDefinition(const ROIDefinition &rois) {
void JFJochImageReadingWorker::SetROIDefinition_i(const ROIDefinition &rois) {
// The worker experiment is the source of truth; the analysis ROI engine is rebuilt
// for it so newly loaded images pick up the change automatically.
// for it so newly loaded images pick up the change automatically. From now on the
// edited ROIs override whatever the file carried.
roi_override_ = rois;
curr_experiment.ROI().SetROI(rois);
if (image_analysis)
image_analysis->RebuildROI();
@@ -623,6 +631,15 @@ void JFJochImageReadingWorker::SetROIDefinition_i(const ROIDefinition &rois) {
emit imageLoaded(current_image_ptr);
}
void JFJochImageReadingWorker::ApplyROIOverrideToImage_i() {
if (!roi_override_ || !current_image_ptr)
return;
auto md = current_image_ptr->CreateMutableDataset();
md->experiment.ROI().SetROI(*roi_override_);
current_image_ptr = std::make_shared<JFJochReaderImage>(
current_image_ptr->ImageData(), std::shared_ptr<const JFJochReaderDataset>(md));
}
void JFJochImageReadingWorker::UpdateUserMask_i(const std::vector<uint32_t> &mask) {
std::shared_ptr<const JFJochReaderDataset> dataset;
if (http_mode) {
+4
View File
@@ -53,6 +53,9 @@ private:
QString current_file;
DiffractionExperiment curr_experiment;
// Once the user edits ROIs they override whatever the file carried, for this and
// every subsequently loaded image, until a new file is opened.
std::optional<ROIDefinition> roi_override_;
IndexingSettings indexing_settings;
AzimuthalIntegrationSettings azint_settings;
std::unique_ptr<IndexerThreadPool> indexing;
@@ -119,6 +122,7 @@ private:
void UpdateAzint_i(const JFJochReaderDataset *dataset);
void UpdateUserMask_i(const std::vector<uint32_t> &mask);
void SetROIDefinition_i(const ROIDefinition &rois);
void ApplyROIOverrideToImage_i(); // rewrite current_image_ptr's dataset with roi_override_
void setAutoLoadMode_i(AutoloadMode mode);
signals:
+7 -4
View File
@@ -113,15 +113,18 @@ JFJochViewerSidePanel::JFJochViewerSidePanel(QWidget *parent) : QWidget(parent)
layout->addWidget(new TitleLabel("ROI", this));
auto roiLabelsCheckBox = new QCheckBox("Show ROI labels", this);
auto roiLabelsCheckBox = new QCheckBox("Show labels", this);
roiLabelsCheckBox->setCheckState(Qt::CheckState::Unchecked);
connect(roiLabelsCheckBox, &QCheckBox::toggled, this, &JFJochViewerSidePanel::showROILabels);
layout->addWidget(roiLabelsCheckBox);
auto roiFillCheckBox = new QCheckBox("Translucent ROI fill", this);
auto roiFillCheckBox = new QCheckBox("Translucent fill", this);
roiFillCheckBox->setCheckState(Qt::CheckState::Unchecked);
connect(roiFillCheckBox, &QCheckBox::toggled, this, &JFJochViewerSidePanel::showROIFill);
layout->addWidget(roiFillCheckBox);
auto roiToggleRow = new QHBoxLayout();
roiToggleRow->addWidget(roiLabelsCheckBox);
roiToggleRow->addWidget(roiFillCheckBox);
layout->addLayout(roiToggleRow);
roi_list = new JFJochViewerROIList(this);
layout->addWidget(roi_list);
+16 -21
View File
@@ -9,15 +9,13 @@
#include <QHBoxLayout>
#include <QPushButton>
#include <QInputDialog>
#include <QListWidgetItem>
JFJochViewerROIList::JFJochViewerROIList(QWidget *parent) : QWidget(parent) {
auto *layout = new QVBoxLayout(this);
list_ = new QListWidget(this);
list_->setMaximumHeight(120);
layout->addWidget(list_);
connect(list_, &QListWidget::itemSelectionChanged, this, &JFJochViewerROIList::OnSelectionChanged);
combo_ = new QComboBox(this);
layout->addWidget(combo_);
connect(combo_, &QComboBox::currentIndexChanged, this, &JFJochViewerROIList::OnSelectionChanged);
auto *buttons = new QHBoxLayout();
type_combo_ = new QComboBox(this);
@@ -42,36 +40,33 @@ void JFJochViewerROIList::loadImage(std::shared_ptr<const JFJochReaderImage> ima
image_ = image;
const QString keep = SelectedName();
rois_ = image_ ? image_->Dataset().experiment.ROI().GetROIDefinition() : ROIDefinition{};
RebuildList(keep);
RebuildCombo(keep);
ShowSelectedResult();
}
void JFJochViewerROIList::RebuildList(const QString &keep_selected) {
void JFJochViewerROIList::RebuildCombo(const QString &keep_selected) {
rebuilding_ = true;
list_->clear();
combo_->clear();
auto add_item = [&](const std::string &name, const char *type) {
auto *item = new QListWidgetItem(QString("%1 · %2").arg(QString::fromStdString(name), type));
item->setData(Qt::UserRole, QString::fromStdString(name)); // name independent of display text
list_->addItem(item);
// display "name · type", with the bare name kept as item data for lookups
combo_->addItem(QString("%1 · %2").arg(QString::fromStdString(name), type),
QString::fromStdString(name));
};
for (const auto &b : rois_.boxes) add_item(b.GetName(), "box");
for (const auto &c : rois_.circles) add_item(c.GetName(), "circle");
for (const auto &a : rois_.azimuthal) add_item(a.GetName(), "azim");
if (!keep_selected.isEmpty()) {
for (int i = 0; i < list_->count(); i++)
if (list_->item(i)->data(Qt::UserRole).toString() == keep_selected) {
list_->setCurrentRow(i);
break;
}
const int idx = combo_->findData(keep_selected);
if (idx >= 0)
combo_->setCurrentIndex(idx);
}
rebuilding_ = false;
}
QString JFJochViewerROIList::SelectedName() const {
auto *item = list_->currentItem();
return item ? item->data(Qt::UserRole).toString() : QString();
return combo_->currentData().toString();
}
void JFJochViewerROIList::ShowSelectedResult() {
@@ -121,7 +116,7 @@ void JFJochViewerROIList::OnAdd() {
default: rois_.azimuthal.emplace_back(name, 2.0f, 4.0f); break;
}
emit roisChanged(rois_);
RebuildList(QString::fromStdString(name));
RebuildCombo(QString::fromStdString(name));
ShowSelectedResult();
}
@@ -137,7 +132,7 @@ void JFJochViewerROIList::OnDelete() {
};
if (erase_by_name(rois_.boxes) || erase_by_name(rois_.circles) || erase_by_name(rois_.azimuthal)) {
emit roisChanged(rois_);
RebuildList(QString());
RebuildCombo(QString());
ShowSelectedResult();
}
}
@@ -168,6 +163,6 @@ void JFJochViewerROIList::OnRename() {
}
emit roisChanged(rois_);
RebuildList(new_name);
RebuildCombo(new_name);
ShowSelectedResult();
}
+2 -3
View File
@@ -6,7 +6,6 @@
#include <memory>
#include <QWidget>
#include <QListWidget>
#include <QComboBox>
#include "JFJochViewerROIResult.h"
@@ -20,7 +19,7 @@
class JFJochViewerROIList : public QWidget {
Q_OBJECT
QListWidget *list_;
QComboBox *combo_;
QComboBox *type_combo_;
JFJochViewerROIResult *result_;
@@ -28,7 +27,7 @@ class JFJochViewerROIList : public QWidget {
std::shared_ptr<const JFJochReaderImage> image_;
bool rebuilding_ = false;
void RebuildList(const QString &keep_selected);
void RebuildCombo(const QString &keep_selected);
void ShowSelectedResult();
[[nodiscard]] QString SelectedName() const;
[[nodiscard]] std::string UniqueName() const;