During a collection every dataset tick ran JFJochViewerDatasetInfo::UpdateLabels(), which clear()ed the plot-type combo and re-added every item, so an open dropdown lost its hovered row and scroll position on each tick and picking a plot type was a race against the next update. The same clear-and-refill ran per tick or per image in the calibration window's calibration combo, the ROI list combo and the powder calibrant combo; the last two also threw away the user's choice (the calibrant was reset to the first entry on every image). New LiveComboBox::SetItems() takes the full item list and leaves the combo untouched when the list is unchanged. When it does change while the popup is open, the change is held back and applied (queued) once the popup window hides, so a click that closes the popup lands first. A replacement keeps the selection by item data, blocks signals, and a held-back one emits itemsReplaced() so the owner can re-plot. The calibration window now re-shows the selected calibration on a dataset tick instead of always reloading the pixel mask over it. Tested offscreen: a timer fires datasetLoaded() every 25 ms while the dropdown is open; the popup and hovered row survive, newly available indexing metrics are held back until the popup closes, and the picked type sticks and is what the chart plots. The rc175 code fails the hover checks of the same harness. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
134 lines
5.2 KiB
C++
134 lines
5.2 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include "JFJochCalibrationWindow.h"
|
|
#include <QCloseEvent>
|
|
#include <QGridLayout>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
|
|
|
|
JFJochCalibrationWindow::JFJochCalibrationWindow(QWidget *parent) : JFJochHelperWindow(parent) {
|
|
QWidget *centralWidget = new QWidget(this);
|
|
setWindowTitle("Calibration image viewer");
|
|
setCentralWidget(centralWidget);
|
|
|
|
auto grid_layout = new QGridLayout();
|
|
|
|
viewer = new JFJochSimpleImage(this);
|
|
|
|
calibration_option = new LiveComboBox(this);
|
|
color_map_select = new QComboBox(this);
|
|
|
|
color_map_select->addItem("Viridis", static_cast<int>(ColorScaleEnum::Viridis));
|
|
color_map_select->addItem("Heat", static_cast<int>(ColorScaleEnum::Heat));
|
|
color_map_select->addItem("Indigo", static_cast<int>(ColorScaleEnum::Indigo));
|
|
color_map_select->addItem("B/W", static_cast<int>(ColorScaleEnum::BW));
|
|
color_map_select->setCurrentIndex(static_cast<int>(ColorScaleEnum::Indigo));
|
|
|
|
background_slider = new SliderPlusBox(1, 32768, 1.0, 0, this, SliderPlusBox::ScaleType::Linear);
|
|
foreground_slider = new SliderPlusBox(1, 32768, 1.0, 0, this, SliderPlusBox::ScaleType::Linear);
|
|
|
|
auto background_row = new QHBoxLayout();
|
|
auto foreground_row = new QHBoxLayout();
|
|
background_row->addWidget(new QLabel("Background:"));
|
|
background_row->addWidget(background_slider);
|
|
foreground_row->addWidget(new QLabel("Foreground:"));
|
|
foreground_row->addWidget(foreground_slider);
|
|
|
|
grid_layout->addWidget(calibration_option, 0, 0);
|
|
grid_layout->addWidget(color_map_select, 0, 1);
|
|
grid_layout->addLayout(background_row, 1, 0, 1, 2);
|
|
grid_layout->addLayout(foreground_row, 2, 0, 1, 2);
|
|
grid_layout->addWidget(viewer, 3, 0, 1, 2);
|
|
|
|
connect(viewer, &JFJochSimpleImage::backgroundChanged,
|
|
[this] (float val) {
|
|
QSignalBlocker blocker(background_slider);
|
|
background_slider->setValue(val);
|
|
});
|
|
|
|
connect(viewer, &JFJochSimpleImage::foregroundChanged,
|
|
[this] (float val) {
|
|
QSignalBlocker blocker(foreground_slider);
|
|
foreground_slider->setValue(val);
|
|
});
|
|
connect(background_slider, &SliderPlusBox::valueChanged, viewer, &JFJochSimpleImage::changeBackground);
|
|
connect(foreground_slider, &SliderPlusBox::valueChanged, viewer, &JFJochSimpleImage::changeForeground);
|
|
|
|
connect(color_map_select, &QComboBox::currentIndexChanged, this, [this](int index) {
|
|
viewer->setColorMap(index);
|
|
});
|
|
|
|
connect(calibration_option, &QComboBox::currentIndexChanged, this, &JFJochCalibrationWindow::CalibrationSelected);
|
|
connect(calibration_option, &LiveComboBox::itemsReplaced, this, [this] {
|
|
CalibrationSelected(calibration_option->currentIndex());
|
|
});
|
|
centralWidget->setLayout(grid_layout);
|
|
|
|
statusBar = new QStatusBar(this);
|
|
setStatusBar(statusBar);
|
|
|
|
connect(viewer, &JFJochSimpleImage::writeStatusBar,
|
|
statusBar, &QStatusBar::showMessage);
|
|
}
|
|
|
|
void JFJochCalibrationWindow::datasetLoaded(std::shared_ptr<const JFJochReaderDataset> in_dataset) {
|
|
dataset = std::move(in_dataset);
|
|
|
|
// Arrives on every dataset tick of a live acquisition; the combo only changes when the list
|
|
// does, and keeps the selection by name.
|
|
QVector<LiveComboBox::Item> items;
|
|
if (dataset) {
|
|
items.push_back({"Pixel mask", "Pixel mask"});
|
|
for (auto &calibration : dataset->calibration_data)
|
|
items.push_back({calibration.c_str(), calibration.c_str()});
|
|
}
|
|
calibration_option->SetItems(items);
|
|
|
|
// Rebuilding the mask is a full-detector copy plus a full-resolution recolour on the GUI
|
|
// thread, and this arrives on every dataset tick when following a live acquisition. Do not pay
|
|
// it for a window nobody has open.
|
|
if (!isVisible()) {
|
|
pending_mask_reload = true;
|
|
return;
|
|
}
|
|
CalibrationSelected(calibration_option->currentIndex()); // the user's pick, not always the mask
|
|
}
|
|
|
|
void JFJochCalibrationWindow::showEvent(QShowEvent *event) {
|
|
JFJochHelperWindow::showEvent(event);
|
|
if (pending_mask_reload) {
|
|
pending_mask_reload = false;
|
|
CalibrationSelected(calibration_option->currentIndex());
|
|
}
|
|
}
|
|
|
|
void JFJochCalibrationWindow::calibrationLoaded(std::shared_ptr<const SimpleImage> image) {
|
|
viewer->setImage(image);
|
|
}
|
|
|
|
void JFJochCalibrationWindow::LoadMask() {
|
|
if (dataset) {
|
|
auto mask = std::make_shared<SimpleImage>();
|
|
const auto &tmp = dataset->pixel_mask->GetMask();
|
|
mask->buffer.resize(tmp.size() * sizeof(uint32_t));
|
|
memcpy(mask->buffer.data(), tmp.data(), tmp.size() * sizeof(uint32_t));
|
|
mask->image = CompressedImage(mask->buffer.data(), mask->buffer.size(),
|
|
dataset->experiment.GetXPixelsNum(), dataset->experiment.GetYPixelsNum(), CompressedImageMode::Uint32);
|
|
viewer->setImage(mask);
|
|
}
|
|
}
|
|
|
|
void JFJochCalibrationWindow::CalibrationSelected(int val) {
|
|
if (val <= 0)
|
|
LoadMask();
|
|
else
|
|
emit loadCalibration(calibration_option->itemText(val));
|
|
}
|
|
|
|
void JFJochCalibrationWindow::setFeatureColor(QColor input) {
|
|
viewer->setFeatureColor(input);
|
|
}
|
|
|