Merge viewer-ui into rc181: Files/Processing stacked, rejected frames shaded on the dataset plots, image range picked on the plot for processing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HVeDQoEh4AZruktuKZQdrB
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-10-28 12:01:58 +01:00
commit d0ff8bf38f
10 files changed
+282 -63

No files matched your search

+12
View File
@@ -99,6 +99,13 @@ JFJochViewerDatasetInfo::JFJochViewerDatasetInfo(QWidget *parent) : QWidget(pare
connect(chart_view2, &JFJochDatasetInfoChartView::imageSelected,
this, &JFJochViewerDatasetInfo::imageSelectedInChart);
// An image range picked on either plot is shown on both and handed up, for a processing job.
for (auto *view: {chart_view, chart_view2}) {
connect(view, &JFJochDatasetInfoChartView::rangeSelected, this, &JFJochViewerDatasetInfo::rangeSelected);
connect(view, &JFJochDatasetInfoChartView::processRangeRequested,
this, &JFJochViewerDatasetInfo::processRangeRequested);
}
connect(chart_view2, &JFJochDatasetInfoChartView::writeStatusBar,
[this](QString string, int timeout_ms) {
emit writeStatusBar(string, timeout_ms);
@@ -366,6 +373,11 @@ void JFJochViewerDatasetInfo::imageLoaded(std::shared_ptr<const JFJochReaderImag
image_chart->loadImage(image); // per-image profile follows the displayed image
}
void JFJochViewerDatasetInfo::setSelectedRange(int64_t first, int64_t last) {
chart_view->setSelectedRange(first, last);
chart_view2->setSelectedRange(first, last);
}
void JFJochViewerDatasetInfo::imageSelectedInChart(int64_t number) {
emit imageSelected(number, 1);
}
+4
View File
@@ -71,6 +71,8 @@ signals:
void imageSelected(int64_t number, int64_t summation);
void writeStatusBar(QString string, int timeout_ms = 0);
void addPlot(); // request another dataset-info plot dock
void rangeSelected(int64_t first, int64_t last); // an image range picked on the plot; -1, -1 = cleared
void processRangeRequested();
public:
explicit JFJochViewerDatasetInfo(QWidget *parent = nullptr);
@@ -90,6 +92,8 @@ public slots:
void setHttpConnection(bool connected, QString addr = QString());
// "Reset all settings to defaults": back to the state a first load of this dataset would show.
void resetPlotDefaults();
// Show an image range picked on this or another plot; -1, -1 clears it.
void setSelectedRange(int64_t first, int64_t last);
};
+24 -14
View File
@@ -65,7 +65,7 @@
// Dock-layout version for saveState/restoreState: bump whenever the dock set / structure changes so a
// stale saved layout (which can restore a dock as a broken, non-responsive floating window) is rejected
// and the default layout applies instead.
static constexpr int kLayoutVersion = 5;
static constexpr int kLayoutVersion = 6;
JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString &file) : QMainWindow(parent) {
image_tail_timer_ = new QTimer(this);
@@ -589,20 +589,18 @@ JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString
settingsDock->setObjectName("settingsDock");
settingsDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
settingsDock->setWidget(settingsScroll); // scrollable: don't force the window taller than the screen
addDockWidget(Qt::LeftDockWidgetArea, settingsDock);
menuBar->AddDockEntry(settingsDock, "Processing");
// Files panel: an alternative to the processing panel in the same left-hand slot, tabbed together (the
// canonical Qt way to offer two panels one on top of the other without doubling the width) - most
// beamline users care about opening images, not reprocessing settings, so this one starts on top.
// Files panel: in the same left-hand column as the processing panel, Files on top and Processing
// below with a splitter between them, so both are in view from the start without doubling the width.
auto *fileBrowser = new JFJochViewerFileBrowser(this);
fileBrowserDock = new QDockWidget("Files", this);
fileBrowserDock->setObjectName("fileBrowserDock");
fileBrowserDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
fileBrowserDock->setWidget(fileBrowser);
addDockWidget(Qt::LeftDockWidgetArea, fileBrowserDock);
tabifyDockWidget(settingsDock, fileBrowserDock);
fileBrowserDock->raise();
addDockWidget(Qt::LeftDockWidgetArea, fileBrowserDock, Qt::Vertical);
addDockWidget(Qt::LeftDockWidgetArea, settingsDock, Qt::Vertical);
resizeDocks({fileBrowserDock, settingsDock}, {350, 650}, Qt::Vertical);
menuBar->AddDockEntry(fileBrowserDock, "Files");
connect(fileBrowser, &JFJochViewerFileBrowser::openDataset,
reading_worker, &JFJochImageReadingWorker::LoadFile);
@@ -620,6 +618,16 @@ JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString
reading_worker, &JFJochImageReadingWorker::ReanalyzeImages);
connect(settingsPanel, &JFJochViewerSettingsDock::analyzeDataset,
processingJobsWindow, &JFJochProcessingJobsWindow::newJob);
connect(this, &JFJochViewerWindow::imageRangeSelected, this, [this](int64_t first, int64_t last) {
rangeFirst = first;
rangeLast = last;
});
connect(this, &JFJochViewerWindow::imageRangeSelected,
processingJobsWindow, &JFJochProcessingJobsWindow::setImageRange);
// A range belongs to the file it was picked on.
connect(reading_worker, &JFJochImageReadingWorker::fileOpened, this, [this] { emit imageRangeSelected(-1, -1); });
connect(this, &JFJochViewerWindow::processRangeRequested,
settingsPanel, &JFJochViewerSettingsDock::requestAnalyzeDataset);
// Grey out "Analyze dataset" on a live HTTP source (re-processing needs a stored file).
connect(reading_worker, &JFJochImageReadingWorker::httpConnectionChanged,
settingsPanel, &JFJochViewerSettingsDock::setHttpConnection);
@@ -720,14 +728,12 @@ JFJochViewerWindow::~JFJochViewerWindow() {
}
void JFJochViewerWindow::ApplyPerspective(Perspective p) {
// Image: just the image + inspector (+ file manager, on top of its tab with Processing).
// Processing: also dataset-info plots and the jobs panel, with the processing panel raised instead.
// Image: just the image + inspector (+ the file manager and the processing panel).
// Processing: also the dataset-info plots.
const bool processing = (p == Perspective::Processing);
if (inspectorDock) inspectorDock->setVisible(true);
// Processing and the file manager stay tabbed together either way; only which one is on top changes.
if (settingsDock) settingsDock->setVisible(true);
if (fileBrowserDock) fileBrowserDock->setVisible(true);
(processing ? settingsDock : fileBrowserDock)->raise();
if (processingDock && !processing) processingDock->hide(); // shows only when a job starts
for (auto *d : findChildren<QDockWidget *>())
if (d->objectName().startsWith("datasetInfoDock"))
@@ -900,13 +906,13 @@ void JFJochViewerWindow::NewDatasetInfo() {
info->datasetLoaded(lastDataset);
info->imageLoaded(lastImage);
info->runsChanged(lastRuns, lastActiveRunId);
info->setSelectedRange(rangeFirst, rangeLast);
auto dock = new QDockWidget(QString("Dataset info"), this);
dock->setObjectName(QStringLiteral("datasetInfoDock%1").arg(datasetInfoCounter++));
dock->setAllowedAreas(Qt::BottomDockWidgetArea);
dock->setFeatures(
QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable |
QDockWidget::DockWidgetVerticalTitleBar);
QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable);
dock->setAttribute(Qt::WA_DeleteOnClose);
dock->setWidget(info);
addDockWidget(Qt::BottomDockWidgetArea, dock);
@@ -932,6 +938,10 @@ void JFJochViewerWindow::NewDatasetInfo() {
connect(info, &JFJochViewerDatasetInfo::imageSelected,
reading_worker, &JFJochImageReadingWorker::LoadImage);
connect(info, &JFJochViewerDatasetInfo::addPlot, this, &JFJochViewerWindow::NewDatasetInfo);
// An image range picked on any plot shows on every plot and becomes the range of the next job.
connect(info, &JFJochViewerDatasetInfo::rangeSelected, this, &JFJochViewerWindow::imageRangeSelected);
connect(this, &JFJochViewerWindow::imageRangeSelected, info, &JFJochViewerDatasetInfo::setSelectedRange);
connect(info, &JFJochViewerDatasetInfo::processRangeRequested, this, &JFJochViewerWindow::processRangeRequested);
connect(toolBarDisplay, &JFJochViewerToolbarDisplay::colorMapChanged,
info, &JFJochViewerDatasetInfo::setColorMap);
connect(info, &JFJochViewerDatasetInfo::writeStatusBar,
+4
View File
@@ -53,6 +53,7 @@ private:
QProgressDialog *retryDialog = nullptr;
QDockWidget *lastDatasetInfoDock = nullptr; // most recent dataset-info dock, for docking layout
int64_t rangeFirst = -1, rangeLast = -1; // image range picked on a plot, for a plot opened later
QDockWidget *inspectorDock = nullptr; // image inspector (former right-hand side panel)
QScrollArea *inspectorScroll = nullptr; // its scroll area; width bounds follow the font
QDockWidget *settingsDock = nullptr; // inline MX/AzInt settings panel
@@ -131,5 +132,8 @@ signals:
void oneShotAutoForeground();
// Re-emitted by the shortcut filter; the navigation toolbar applies the step, clamped.
void stepImage(int steps);
// An image range picked on a plot (-1, -1 = cleared), and a request to process it.
void imageRangeSelected(int64_t first, int64_t last);
void processRangeRequested();
};
+190 -45
View File
@@ -45,6 +45,12 @@ JFJochDatasetInfoChartView::JFJochDatasetInfoChartView(QWidget *parent)
m_hoverLoadTimer = new QTimer(this);
m_hoverLoadTimer->setSingleShot(true);
connect(m_hoverLoadTimer, &QTimer::timeout, this, &JFJochDatasetInfoChartView::onHoverLoadTimeout);
// The shaded bands and the picked range are drawn in chart coordinates: follow the plot area.
connect(chart(), &QChart::plotAreaChanged, this, [this](const QRectF &) {
placeBands();
placeSelection();
});
}
@@ -101,54 +107,147 @@ void JFJochDatasetInfoChartView::setImage(int64_t val) {
}
void JFJochDatasetInfoChartView::mousePressEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton) {
if (values.empty()) {
QChartView::mousePressEvent(event);
return;
}
int64_t JFJochDatasetInfoChartView::imageAt(const QPoint &pos) const {
if (!series)
return -1;
const double x = chart()->mapToValue(pos, series).x();
if (!std::isfinite(x))
return -1;
const int64_t n = std::max<int64_t>(full_range_, static_cast<int64_t>(values.size()));
return std::clamp<int64_t>(std::llround(x), 0, std::max<int64_t>(n - 1, 0));
}
const QPointF clickedPoint = event->pos();
const QPointF chartCoord = chart()->mapToValue(clickedPoint, series);
const double xVal = chartCoord.x();
void JFJochDatasetInfoChartView::setSelectedRange(int64_t first, int64_t last) {
sel_first_ = std::min(first, last);
sel_last_ = std::max(first, last);
placeSelection();
}
if (!std::isfinite(xVal) || xVal < 0.0 ||
xVal > static_cast<double>(values.size() - 1)) {
QChartView::mousePressEvent(event);
void JFJochDatasetInfoChartView::placeSelection() {
if (sel_first_ < 0 || !series || m_showFFT) {
if (selection_item_)
selection_item_->hide();
return;
}
if (!selection_item_) {
selection_item_ = new QGraphicsRectItem(chart());
selection_item_->setZValue(1.6); // above the disposition bands, below the grid and the curves
selection_item_->setPen(Qt::NoPen);
}
// A clear light blue - the navy accent at low alpha read as grey over the light background
selection_item_->setBrush(DarkTheme() ? QColor(90, 150, 230, 70) : QColor(60, 140, 240, 55));
const QRectF plot = chart()->plotArea();
const double x0 = std::max(plot.left(), chart()->mapToPosition(QPointF(sel_first_ - 0.5, 0), series).x());
const double x1 = std::min(plot.right(), chart()->mapToPosition(QPointF(sel_last_ + 0.5, 0), series).x());
selection_item_->setRect(QRectF(x0, plot.top(), std::max(0.0, x1 - x0), plot.height()));
selection_item_->show();
}
void JFJochDatasetInfoChartView::fillBands(const JFJochReaderDataset *dataset) {
bands_.clear();
if (dataset == nullptr)
return;
const auto &disposition = dataset->frame_disposition;
const auto &codes = dataset->frame_disposition_codes;
// 0 = merged, 1 = downgraded, 2 = rejected
auto kind = [&codes](uint8_t code) {
if (code >= codes.size()) return 0;
if (codes[code] == "rejected") return 2;
if (codes[code] == "downgraded") return 1;
return 0;
};
auto xpos = [this](size_t i) {
return i < primary_x_.size() ? static_cast<double>(primary_x_[i]) : static_cast<double>(i);
};
for (size_t i = 0; i < disposition.size();) {
const int k = kind(disposition[i]);
size_t j = i + 1;
while (j < disposition.size() && kind(disposition[j]) == k)
j++;
if (k > 0)
bands_.push_back({xpos(i), xpos(j - 1), k == 2});
i = j;
}
}
void JFJochDatasetInfoChartView::placeBands() {
if (!series)
return;
const QRectF plot = chart()->plotArea();
for (size_t i = 0; i < band_items_.size() && i < bands_.size(); i++) {
const double x0 = std::max(plot.left(), chart()->mapToPosition(QPointF(bands_[i].first - 0.5, 0), series).x());
const double x1 = std::min(plot.right(), chart()->mapToPosition(QPointF(bands_[i].last + 0.5, 0), series).x());
band_items_[i]->setRect(QRectF(x0, plot.top(), std::max(0.0, x1 - x0), plot.height()));
}
}
QString JFJochDatasetInfoChartView::bandText(int64_t image) const {
for (const auto &b: bands_)
if (image >= b.first - 0.5 && image <= b.last + 0.5)
return b.rejected ? QStringLiteral(" (rejected from the merge)")
: QStringLiteral(" (down-weighted in the merge)");
return {};
}
void JFJochDatasetInfoChartView::mousePressEvent(QMouseEvent *event) {
// The left button picks an image on release; a drag in between picks a range of images instead.
if (event->button() == Qt::LeftButton && series && !m_showFFT && !values.empty()) {
drag_anchor_pos_ = event->pos();
drag_anchor_x_ = static_cast<double>(imageAt(event->pos()));
dragging_ = false;
}
QChartView::mousePressEvent(event);
}
void JFJochDatasetInfoChartView::mouseReleaseEvent(QMouseEvent *event) {
if (event->button() == Qt::LeftButton && std::isfinite(drag_anchor_x_)) {
drag_anchor_x_ = NAN;
if (dragging_) {
dragging_ = false;
emit rangeSelected(sel_first_, sel_last_);
emit writeStatusBar(QStringLiteral("Images %1-%2 selected - right-click to process them")
.arg(sel_first_).arg(sel_last_), 6000);
} else
selectImageAt(event->pos());
}
QChartView::mouseReleaseEvent(event);
}
void JFJochDatasetInfoChartView::selectImageAt(const QPoint &pos) {
const QPointF chartCoord = chart()->mapToValue(pos, series);
const double xVal = chartCoord.x();
if (!std::isfinite(xVal) || xVal < 0.0 ||
xVal > static_cast<double>(values.size() - 1))
return;
int64_t selectedIdx = 0;
if (binning <= 1) {
// Original behavior: pick nearest frame index
selectedIdx = std::lround(xVal);
} else {
// Binned mode: pick bin index from x, then representative frame
const int64_t nBins =
static_cast<int64_t>(values.size()) / binning;
if (nBins <= 0)
return;
}
int64_t selectedIdx = 0;
if (binning <= 1) {
// Original behavior: pick nearest frame index
selectedIdx = std::lround(xVal);
} else {
// Binned mode: pick bin index from x, then representative frame
const int64_t nBins =
static_cast<int64_t>(values.size()) / binning;
if (nBins <= 0) {
QChartView::mousePressEvent(event);
return;
}
int64_t binIdx =
static_cast<int64_t>(std::floor(xVal / static_cast<double>(binning)));
binIdx = std::clamp<int64_t>(binIdx, 0, nBins - 1);
int64_t centerIdx = binIdx * binning + binning / 2;
if (centerIdx >= static_cast<int64_t>(values.size()))
centerIdx = static_cast<int64_t>(values.size()) - 1;
selectedIdx = centerIdx;
}
if (selectedIdx >= 0 &&
selectedIdx < static_cast<int64_t>(values.size())) {
emit imageSelected(selectedIdx);
}
int64_t binIdx =
static_cast<int64_t>(std::floor(xVal / static_cast<double>(binning)));
binIdx = std::clamp<int64_t>(binIdx, 0, nBins - 1);
int64_t centerIdx = binIdx * binning + binning / 2;
if (centerIdx >= static_cast<int64_t>(values.size()))
centerIdx = static_cast<int64_t>(values.size()) - 1;
selectedIdx = centerIdx;
}
if (selectedIdx >= 0 &&
selectedIdx < static_cast<int64_t>(values.size())) {
emit imageSelected(selectedIdx);
}
QChartView::mousePressEvent(event); // Call the base implementation
}
void JFJochDatasetInfoChartView::resetZoom() {
@@ -190,6 +289,8 @@ void JFJochDatasetInfoChartView::loadValues(const std::vector<float> &input, int
image_time_s = {};
}
fillBands(dataset);
curr_image = image;
updateChart();
}
@@ -255,6 +356,10 @@ void JFJochDatasetInfoChartView::updateChart() {
m_hoverLineHorizontal = nullptr;
}
for (auto *item: band_items_)
delete item;
band_items_.clear();
#ifdef JFJOCH_USE_FFTW
if (m_showFFT)
buildFFTChart();
@@ -264,6 +369,20 @@ void JFJochDatasetInfoChartView::updateChart() {
buildTimeDomainChart();
#endif
// Light red behind the images the merge rejected, light yellow behind the ones it down-weighted.
// Above the plot background, below the grid and the curves.
if (series && !m_showFFT) {
for (const auto &band: bands_) {
auto *item = new QGraphicsRectItem(chart());
item->setZValue(1.5);
item->setPen(Qt::NoPen);
item->setBrush(band.rejected ? QColor(230, 40, 40, 45) : QColor(240, 190, 0, 60));
band_items_.push_back(item);
}
}
placeBands();
placeSelection();
// Qt Charts elides any axis label that does not fit its allotted box down to "..." (the default
// since 6.2). In a dock this narrow that is every label, so show them in full instead.
for (auto *axis: chart()->axes())
@@ -714,8 +833,22 @@ void JFJochDatasetInfoChartView::contextMenuEvent(QContextMenuEvent *event) {
actShowFFT->setEnabled(!values.empty() && image_time_s > 0.0);
#endif
menu.addSeparator();
const bool has_range = sel_first_ >= 0;
QAction *actProcess = menu.addAction(has_range
? QStringLiteral("Process images %1-%2...").arg(sel_first_).arg(sel_last_)
: QStringLiteral("Process images... (drag over the plot to pick a range)"));
actProcess->setEnabled(has_range);
QAction *actClearRange = menu.addAction("Clear image range");
actClearRange->setEnabled(has_range);
QAction *chosen = menu.exec(event->globalPos());
if (chosen == copyXY) {
if (chosen == actProcess) {
emit processRangeRequested();
} else if (chosen == actClearRange) {
setSelectedRange(-1, -1);
emit rangeSelected(-1, -1);
} else if (chosen == copyXY) {
QString out;
out.reserve(static_cast<int>(values.size() * 16)); // rough prealloc
for (size_t i = 0; i < values.size(); ++i) {
@@ -753,6 +886,17 @@ void JFJochDatasetInfoChartView::mouseMoveEvent(QMouseEvent *event) {
if (!series || values.empty())
return;
if (std::isfinite(drag_anchor_x_) && (event->buttons() & Qt::LeftButton)) {
if (!dragging_ && std::abs(event->pos().x() - drag_anchor_pos_.x()) >= QApplication::startDragDistance())
dragging_ = true;
if (dragging_) {
setSelectedRange(static_cast<int64_t>(drag_anchor_x_), imageAt(event->pos()));
emit writeStatusBar(QStringLiteral("Images %1-%2 (%3 images)")
.arg(sel_first_).arg(sel_last_).arg(sel_last_ - sel_first_ + 1), 6000);
return;
}
}
#ifdef JFJOCH_USE_FFTW
if (m_showFFT && !m_fftFrequenciesHz.empty()) {
// FFT mode: x is frequency in Hz
@@ -909,6 +1053,7 @@ void JFJochDatasetInfoChartView::mouseMoveEvent(QMouseEvent *event) {
text = QString("image = %1, no value").arg(idx);
}
}
text += bandText(idx);
emit writeStatusBar(text, 6000);
// Debounced image load on hover when Shift is pressed
@@ -10,6 +10,7 @@
#include <QMouseEvent>
#include <QGraphicsLineItem>
#include <QGraphicsSimpleTextItem>
#include <QGraphicsRectItem>
#include <QTimer>
#include <QPointer>
#include <QColor>
@@ -63,6 +64,27 @@ class JFJochDatasetInfoChartView : public QChartView {
// already within a few pixels of the curve. A non-finite snap_to_y never snaps.
void setHoverLineY(double y, double snap_to_y);
// Images the merge rejected or down-weighted (the run's frame disposition), as runs of consecutive
// images in x, shaded behind the curve.
struct DispositionBand { double first; double last; bool rejected; };
std::vector<DispositionBand> bands_;
std::vector<QGraphicsRectItem *> band_items_;
void fillBands(const JFJochReaderDataset *dataset);
void placeBands();
QString bandText(int64_t image) const;
// An image range picked by dragging over the plot, for processing; -1 = none. The drag anchor is
// where the left button went down (NaN = no drag), so a press-and-release in place stays a click.
int64_t sel_first_ = -1;
int64_t sel_last_ = -1;
double drag_anchor_x_ = NAN;
QPoint drag_anchor_pos_;
bool dragging_ = false;
QGraphicsRectItem *selection_item_ = nullptr;
int64_t imageAt(const QPoint &pos) const; // clamped to the dataset; -1 off the plot
void selectImageAt(const QPoint &pos); // a click: the image (or bin) under it
void placeSelection();
std::optional<GoniometerAxis> goniometer_axis;
std::optional<double> image_time_s;
@@ -96,6 +118,9 @@ private:
signals:
void imageSelected(int64_t number);
// A range of images [first, last] picked on the plot (-1, -1 = cleared), and a request to process it.
void rangeSelected(int64_t first, int64_t last);
void processRangeRequested();
void writeStatusBar(QString string, int timeout_ms = 0);
private slots:
@@ -106,6 +131,7 @@ private:
void changeEvent(QEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void leaveEvent(QEvent *event) override;
void contextMenuEvent(QContextMenuEvent *event) override;
@@ -124,6 +150,8 @@ public:
public slots:
void resetZoom();
// Shows a range picked here or on another plot; -1, -1 clears it.
void setSelectedRange(int64_t first, int64_t last);
public:
// Load the primary (active) run's values plus any overlay runs. one_over_d2 applies the same
+6 -2
View File
@@ -94,8 +94,7 @@ JFJochViewerSettingsDock::JFJochViewerSettingsDock(const SpotFindingSettings &sp
layout->addLayout(analyzeRow);
layout->addSpacing(10);
connect(analyzeImageBtn, &QPushButton::toggled, this, &JFJochViewerSettingsDock::reanalyzeImage);
connect(analyzeDatasetBtn_, &QPushButton::clicked, this,
[this] { emit analyzeDataset(mode_, powder_->Selection()); });
connect(analyzeDatasetBtn_, &QPushButton::clicked, this, &JFJochViewerSettingsDock::requestAnalyzeDataset);
// Segmented MX / AzInt / Calib toggle: each community picks its page; pages never share a screen.
// The page is also the mode "Analyze dataset" runs, so there is one control, not two.
@@ -1013,6 +1012,11 @@ void JFJochViewerSettingsDock::UpdateSpaceGroupName() {
}
}
void JFJochViewerSettingsDock::requestAnalyzeDataset() {
if (analyzeDatasetBtn_->isEnabled())
emit analyzeDataset(mode_, powder_->Selection());
}
void JFJochViewerSettingsDock::datasetLoaded(std::shared_ptr<const JFJochReaderDataset> dataset) {
if (!dataset)
return;
@@ -41,6 +41,8 @@ public:
const ScalingSettings &scaling, QWidget *parent = nullptr);
public slots:
// "Analyze dataset", as if the button were pressed (asked for from a plot's image range).
void requestAnalyzeDataset();
void datasetLoaded(std::shared_ptr<const JFJochReaderDataset> dataset);
// A new file, or a run's results shown: the crystal they store is shown but not used until "Known
// unit cell" is ticked, as a bare `rugnux` forgets it (ClearStoredCrystal).
@@ -153,13 +153,15 @@ int JFJochProcessingJobsWindow::askJob(const ReprocessingInputs &inputs, JobSpec
dlg.setWindowTitle(calibration ? "New detector-calibration job"
: azint ? "New azimuthal-integration job" : "New full-analysis job");
// A range picked on the plot is inclusive; end_image is one past the last image.
const bool has_range = range_first_ >= 0;
auto *start_image = new QSpinBox(&dlg);
start_image->setRange(0, 1'000'000'000);
start_image->setValue(0);
start_image->setValue(has_range ? static_cast<int>(range_first_) : 0);
auto *end_image = new QSpinBox(&dlg);
end_image->setRange(0, 1'000'000'000);
end_image->setValue(0);
end_image->setValue(has_range ? static_cast<int>(range_last_ + 1) : 0);
end_image->setSpecialValueText("end"); // 0 => to the last image
auto *threads = new QSpinBox(&dlg);
@@ -402,6 +404,11 @@ ProcessConfig JFJochProcessingJobsWindow::buildConfig(const JobSpec &spec, const
return config;
}
void JFJochProcessingJobsWindow::setImageRange(int64_t first, int64_t last) {
range_first_ = first;
range_last_ = last;
}
void JFJochProcessingJobsWindow::newJob(ProcessMode mode, CalibrationSelection calibration) {
const ReprocessingInputs inputs = worker_->GetReprocessingInputs();
if (!inputs.valid) {
@@ -48,6 +48,7 @@ public slots:
// The panel's "Analyze dataset" action; the mode is the panel's selected page. The calibrant
// selection is only used by ProcessMode::Calibration.
void newJob(ProcessMode mode = ProcessMode::FullAnalysis, CalibrationSelection calibration = {});
void setImageRange(int64_t first, int64_t last);
private slots:
void cancelJob();
@@ -120,5 +121,7 @@ private:
QElapsedTimer job_timer_; // running job wall-clock, for rate + ETA
int running_row_ = -1;
int job_counter_ = 0;
// The image range picked on a plot, offered as the next job's start/end image; -1 = none.
int64_t range_first_ = -1, range_last_ = -1;
std::vector<JobInfo> jobs_;
};