From f2e93d847c56d09d65729edcf4e5ee993ff40de3 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Tue, 20 Oct 2026 23:41:57 +0200 Subject: [PATCH] jfjoch_viewer: opens the serial XFEL inputs, and the files of one dataset together The viewer reads what rugnux reads: HDF5 files with a /metadata panel table, Cheetah CXI single-panel stacks and LCLS-I XTC streams, by their content as the raw sweeps are. The open dialog takes several files; they are one dataset (as on rugnux's command line), and a CrystFEL geometry file among them is their module geometry. The images are shown in detector units, at the geometry the files state. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu --- viewer/JFJochImageReadingWorker.cpp | 33 ++++++++++++++++++++++++++++- viewer/JFJochImageReadingWorker.h | 8 +++++++ viewer/JFJochViewerMenu.cpp | 23 +++++++++++++++----- viewer/JFJochViewerMenu.h | 2 ++ viewer/JFJochViewerWindow.cpp | 2 ++ 5 files changed, 62 insertions(+), 6 deletions(-) diff --git a/viewer/JFJochImageReadingWorker.cpp b/viewer/JFJochImageReadingWorker.cpp index e78e44c1b..e826608eb 100644 --- a/viewer/JFJochImageReadingWorker.cpp +++ b/viewer/JFJochImageReadingWorker.cpp @@ -290,8 +290,34 @@ void JFJochImageReadingWorker::LoadFile_i(const QString &filename, qint64 image_ raw_runs_.clear(); raw_active_ = "Original"; const std::string path = filename.toStdString(); + // The files of one serial XFEL dataset, where the open dialog gave several: this one and + // the rest, a CrystFEL geometry file among them taken as their module geometry. + std::vector files; + std::string geometry; + if (pending_files_.contains(filename)) + for (const auto &f: pending_files_) { + if (f.endsWith(".geom")) + geometry = f.toStdString(); + else + files.push_back(f.toStdString()); + } + else + files.push_back(path); + pending_files_.clear(); cbf_mode = true; - if (JFJochCBFReader::CanRead(path)) { + if (!geometry.empty()) { + file_reader = &xfel_reader; + xfel_reader.ReadFiles(files, geometry); + } else if (JFJochXFELReader::CanReadPanelTable(path)) { + file_reader = &xfel_reader; + xfel_reader.ReadPanelTable(files); + } else if (JFJochXFELReader::CanReadCXI(path)) { + file_reader = &xfel_reader; + xfel_reader.ReadCXI(files); + } else if (JFJochXTCReader::CanRead(path)) { + file_reader = &xtc_reader; + xtc_reader.ReadFiles(files); + } else if (JFJochCBFReader::CanRead(path)) { file_reader = &cbf_reader; cbf_reader.ReadFiles(path); } else if (JFJochMarCCDReader::CanRead(path)) { @@ -365,6 +391,11 @@ void JFJochImageReadingWorker::LoadFile_i(const QString &filename, qint64 image_ } } +void JFJochImageReadingWorker::SetFileList(const QStringList &files) { + QMutexLocker ul(&m); + pending_files_ = files; +} + void JFJochImageReadingWorker::LoadFile(const QString &filename, qint64 image_number, qint64 summation, bool retry) { QMutexLocker ul(&m); diff --git a/viewer/JFJochImageReadingWorker.h b/viewer/JFJochImageReadingWorker.h index 5981908a7..19c990f39 100644 --- a/viewer/JFJochImageReadingWorker.h +++ b/viewer/JFJochImageReadingWorker.h @@ -15,6 +15,8 @@ #include "../reader/JFJochCBFReader.h" #include "../reader/JFJochMarCCDReader.h" #include "../reader/JFJochSMVReader.h" +#include "../reader/JFJochXFELReader.h" +#include "../reader/JFJochXTCReader.h" #include "../common/Logger.h" #include "JFJochHttpReader.h" #include "../image_analysis/MXAnalysisWithoutFPGA.h" @@ -89,6 +91,9 @@ private: JFJochCBFReader cbf_reader; JFJochMarCCDReader marccd_reader; JFJochSMVReader smv_reader; + JFJochXFELReader xfel_reader; + JFJochXTCReader xtc_reader; + QStringList pending_files_; JFJochReader *file_reader = &hdf5_reader; bool cbf_mode = false; // "a raw sweep is open", whichever of the three formats it is @@ -249,6 +254,9 @@ private slots: public slots: void LoadFile(const QString &filename, qint64 image_number, qint64 summation, bool retry); + // The files of one dataset the next LoadFile opens together (the open dialog's selection); used + // only if that LoadFile names one of them. + void SetFileList(const QStringList &files); void CloseFile(); void LoadImage(int64_t image_number, int64_t summation); diff --git a/viewer/JFJochViewerMenu.cpp b/viewer/JFJochViewerMenu.cpp index 809c27471..ac5699c89 100644 --- a/viewer/JFJochViewerMenu.cpp +++ b/viewer/JFJochViewerMenu.cpp @@ -259,19 +259,32 @@ void JFJochViewerMenu::acknowledgementsSelected() { } void JFJochViewerMenu::openSelected() { - QString fileName = QFileDialog::getOpenFileName( + // Several files are one dataset: the files of a serial XFEL run (SACLA HDF5, CXI, XTC), opened as + // rugnux opens them, with a CrystFEL geometry file among them taken as their module geometry. + const QStringList fileNames = QFileDialog::getOpenFileNames( this, - "Open File", // Dialog title + "Open File(s)", // Dialog title "", // Default folder // A CBF is one frame per file: picking any frame opens the sweep it belongs to. - "HDF5 Master Files (*_master.h5 *_process.h5);; HDF5 Files (*.h5);; CBF Images (*.cbf *.cbf.gz);; CCD Images (*.img *.mccd *.smv);;All Files (*)" + "HDF5 Master Files (*_master.h5 *_process.h5);; HDF5 Files (*.h5);; CBF Images (*.cbf *.cbf.gz);; CCD Images (*.img *.mccd *.smv);; XFEL Stills (*.h5 *.cxi *.xtc *.geom);;All Files (*)" ); - if (!fileName.isEmpty()) + if (!fileNames.isEmpty()) { // Qt returns '/'-separated paths even on Windows, but HDF5's H5Fopen needs native separators // to recognise a UNC path (\\host\share, e.g. the WSL2 filesystem): '//host/share' is rejected, // while 'C:/...' still works, which is exactly the reported symptom. No-op on Linux. - emit fileOpenSelected(QDir::toNativeSeparators(fileName), 0, 1, false); + QStringList native; + for (const auto &f: fileNames) + native << QDir::toNativeSeparators(f); + emit fileListSelected(native); + QString first = native.front(); + for (const auto &f: native) + if (!f.endsWith(".geom")) { + first = f; + break; + } + emit fileOpenSelected(first, 0, 1, false); + } } void JFJochViewerMenu::closeSelected() { diff --git a/viewer/JFJochViewerMenu.h b/viewer/JFJochViewerMenu.h index d7fb5f523..9b7b40dbe 100644 --- a/viewer/JFJochViewerMenu.h +++ b/viewer/JFJochViewerMenu.h @@ -54,6 +54,8 @@ public slots: signals: void fileOpenSelected(const QString &filename, qint64 image_number, qint64 summation, bool retry); + // Emitted just before fileOpenSelected when the open dialog gave the files of one dataset. + void fileListSelected(const QStringList &files); void httpTokenSelected(const QString &token); // emitted before fileOpenSelected for an HTTP URL void fileCloseSelected(); diff --git a/viewer/JFJochViewerWindow.cpp b/viewer/JFJochViewerWindow.cpp index 6fb7d2481..b8e80e822 100644 --- a/viewer/JFJochViewerWindow.cpp +++ b/viewer/JFJochViewerWindow.cpp @@ -256,6 +256,8 @@ JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString connect(this, &JFJochViewerWindow::LoadImageRequest, reading_worker, &JFJochImageReadingWorker::LoadImage); + connect(menuBar, &JFJochViewerMenu::fileListSelected, + reading_worker, &JFJochImageReadingWorker::SetFileList); connect(menuBar, &JFJochViewerMenu::fileOpenSelected, reading_worker, &JFJochImageReadingWorker::LoadFile); connect(menuBar, &JFJochViewerMenu::fileOpenSelected,