Running rotation indexing on a sub-range (e.g. images 60-120) segfaulted: the first pass passed the *global* image number to RotationIndexer::ProcessImage, which indexes v_ (sized to the run's image count) -> out-of-bounds write. - ProcessImage now takes an explicit mid-exposure angle (optional; falls back to the goniometer at the image index), so the indexer no longer assumes its slot index equals the goniometer image index. IndexAndRefine supplies it via RotationAngle(), matching the angle used for prediction. Added a bounds guard in ProcessImage so a bad index can never corrupt memory. - JFJochProcess feeds the rotation indexer the local ordinal (not the global index), and shifts the goniometer (start += start_image*incr, incr *= stride, per-image wedge preserved) so local index i maps to the angle of original image start+i*stride - fixing rotation angles for the whole sub-range pipeline (prediction, refinement, output), not just the indexer. - Expose "Rotation images" (number used for the first pass) in the job dialog, enabled when rotation indexing is on. (Count > available is already clamped by select_equally_spaced_image_ordinals.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
89 lines
3.5 KiB
C++
89 lines
3.5 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
#include <QElapsedTimer>
|
|
#include <QString>
|
|
#include <QWidget>
|
|
#include <vector>
|
|
|
|
#include "../JFJochProcessController.h"
|
|
#include "../JFJochImageReadingWorker.h" // ReprocessingInputs
|
|
|
|
class QTableWidget;
|
|
class QProgressBar;
|
|
class QStackedWidget;
|
|
class QToolBar;
|
|
|
|
// Makes processing a first-class GUI activity: a dockable panel with a table of processing jobs run
|
|
// on the current dataset. A job can be run locally (off the GUI thread, via JFJochProcessController)
|
|
// or its jfjoch_process command line copied for a cluster. A finished local run is registered as a
|
|
// reader snapshot so its results become a selectable view of the dataset. Re-processing reads a
|
|
// stored HDF5 file, so the panel shows an explanatory message while connected to a live HTTP stream.
|
|
class JFJochProcessingJobsWindow : public QWidget {
|
|
Q_OBJECT
|
|
public:
|
|
explicit JFJochProcessingJobsWindow(JFJochImageReadingWorker *worker, QWidget *parent = nullptr);
|
|
|
|
signals:
|
|
void registerSnapshot(QString id, QString label, QString master_path);
|
|
void activateSnapshot(QString id);
|
|
void renameRun(QString id, QString label);
|
|
void removeRun(QString id);
|
|
void writeStatusBar(QString message, int timeout_ms = 0);
|
|
// Live per-image results while a job runs, for the dataset-info plots (nullptr clears it).
|
|
void liveDataset(std::shared_ptr<const JFJochReaderDataset> dataset);
|
|
|
|
public slots:
|
|
void onHttpConnectionChanged(bool connected, QString addr);
|
|
void clearJobs(); // reset the table on a new file (re-adds the Original row)
|
|
void setActiveRun(QString active_id); // bold the row of the run currently shown in the plots
|
|
|
|
private slots:
|
|
void newJob();
|
|
void cancelJob();
|
|
void removeResult();
|
|
void viewResults();
|
|
void onPhase(QString phase);
|
|
void onProgress(quint64 done, quint64 total);
|
|
void onFinished(ProcessResult result);
|
|
void onFailed(QString error);
|
|
|
|
private:
|
|
struct JobInfo {
|
|
QString id; // stable reader-snapshot key
|
|
QString label; // editable display name (legend / table)
|
|
QString snapshot_path; // empty unless saved
|
|
bool has_result = false;
|
|
};
|
|
struct JobSpec {
|
|
ProcessMode mode = ProcessMode::FullAnalysis;
|
|
int start_image = 0;
|
|
int end_image = 0; // 0 == to the end
|
|
int threads = 4;
|
|
bool save = true;
|
|
QString prefix;
|
|
bool rotation = false;
|
|
int rotation_images = 30; // images used to find the rotation lattice (first pass)
|
|
bool scaling = false;
|
|
};
|
|
|
|
// Returns 0 = cancel, 1 = run locally, 2 = copy command line; fills spec from the dialog.
|
|
int askJob(const ReprocessingInputs &inputs, JobSpec &spec);
|
|
ProcessConfig buildConfig(const JobSpec &spec, const ReprocessingInputs &inputs) const;
|
|
void setStatus(int row, const QString &text);
|
|
void addOriginalRow(); // the file's own data as the first, always-present run
|
|
|
|
JFJochImageReadingWorker *worker_;
|
|
JFJochProcessController *controller_;
|
|
QToolBar *toolbar_;
|
|
QStackedWidget *stack_; // page 0: jobs table, page 1: HTTP-mode message
|
|
QTableWidget *table_;
|
|
QProgressBar *running_bar_ = nullptr; // lives in the running row's Status cell
|
|
QElapsedTimer job_timer_; // running job wall-clock, for rate + ETA
|
|
int running_row_ = -1;
|
|
int job_counter_ = 0;
|
|
std::vector<JobInfo> jobs_;
|
|
};
|