// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #pragma once #include #include #include #include #include "JFJochViewerMenu.h" #include "../reader/JFJochHDF5Reader.h" #include "JFJochImageReadingWorker.h" #include "JFJochViewerDatasetInfo.h" #include "JFJochViewerStatusBar.h" #include "toolbar/JFJochViewerToolbarDisplay.h" class JFJochProcessingJobsWindow; class JFJochDiffractionImage; class JFJochHelperWindow; class JFJochViewerMagnifier; class QDockWidget; class QScrollArea; class JFJochViewerWindow : public QMainWindow { Q_OBJECT // Theming is done via the application palette in main() (salmon panels, white entry fields, // navy accent) so it applies consistently to every widget, including dialogs. public: explicit JFJochViewerWindow(QWidget *parent = nullptr, bool dbus = true, const QString &file = ""); ~JFJochViewerWindow() override; private: JFJochViewerMenu *menuBar; JFJochImageReadingWorker *reading_worker; JFJochDiffractionImage *viewer; // central diffraction image JFJochViewerToolbarDisplay *toolBarDisplay; JFJochViewerStatusBar *statusbar; JFJochProcessingJobsWindow *processingJobsWindow; std::shared_ptr lastDataset; // added std::shared_ptr lastImage; // added // Remote-display frame throttle (see OnImageReady): the newest frame not yet fanned out, // delivered at most every kRemoteRepaintIntervalMs while a fast producer runs. std::shared_ptr pendingImage; QElapsedTimer image_rate_; QTimer *image_tail_timer_ = nullptr; void DeliverPendingImage(); QVector lastRuns; // for dataset-info docks opened later QString lastActiveRunId; QProgressDialog *retryDialog = nullptr; QDockWidget *lastDatasetInfoDock = nullptr; // most recent dataset-info dock, for docking layout 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 QDockWidget *fileBrowserDock = nullptr; // data-root directory tree, tabbed with settingsDock QDockWidget *processingDock = nullptr; // processing jobs panel JFJochViewerMagnifier *magnifier = nullptr; QDockWidget *magnifierDock = nullptr; // where the magnifier normally sits JFJochHelperWindow *magnifierWindow = nullptr; // ... and its window when popped out void PopOutMagnifier(); QByteArray defaultLayoutState; // captured after construction, for "Reset layout" int datasetInfoCounter = 0; // gives each dataset-info dock a unique objectName void ResetAllSettings(); void UpdateInspectorWidth(); // A narrow window folds the inspector away (with the magnifier, when it is docked in the same // column) so the image keeps its room, and brings it back once the window is wide enough again. QList autoHiddenDocks; // hidden by the window, not by the user int autoHiddenWidth = 0; // the inspector column's width when it was folded away void resizeEvent(QResizeEvent *event) override; // Both ways of opening a dataset from outside the file manager - the File menu and D-Bus - // come through here, so the browser follows the same way for either. void FollowInFileBrowser(const QString &filename); QThread *reading_thread; // Named layouts. Image = just the image + inspector; Processing = also the plots + jobs panel. enum class Perspective { Image, Processing }; void ApplyPerspective(Perspective p); // Keyboard shortcuts (F/B/A, Home/End/PageUp/PageDown) are handled in an application-wide // event filter rather than keyPressEvent, so they work wherever the focus happens to be, // stepping aside when the focused widget genuinely uses the key (text entry, item views). bool eventFilter(QObject *obj, QEvent *event) override; void closeEvent(QCloseEvent *event) override; void changeEvent(QEvent *event) override; private slots: // Single cross-thread sink for the worker's imageLoaded: fans out to every GUI consumer // (synchronously, same thread) via imageReady, then acks the worker so it may produce the next. void OnImageReady(std::shared_ptr image); // Single cross-thread sink for the worker's datasetLoaded, mirroring OnImageReady: fans out to // every GUI consumer synchronously via datasetReady, then acks the worker (datasetConsumed) so the // worker can cap how many datasets are in flight - without this the queued datasetLoaded backlog // (each event pinning a full dataset) grew without bound in live follow (100 GB OOM). void OnDatasetReady(std::shared_ptr dataset); public slots: void LoadFile(const QString &filename, qint64 image_number, qint64 summation, bool retry); void LoadImage(qint64 image_number, qint64 summation); // The bearer token for a protected broker dataset. Sources, each overriding the previous: // JUNGFRAUJOCH_HTTP_TOKEN at start-up, D-Bus, the Open HTTP Connection dialog. void SetHttpToken(const QString &token); void NewDatasetInfo(); void OnFileLoadError(QString title, QString message); void OnFileLoadRetryStatus(bool active, QString message); signals: // Internal GUI-thread fan-out of the newest frame: OnImageReady re-emits this to every GUI // consumer synchronously, then acks via imageConsumed (see OnImageReady). void imageReady(std::shared_ptr image); void imageConsumed(); // ack to the worker that one frame has been consumed (backpressure) // Same GUI-thread fan-out + ack for the dataset stream (see OnDatasetReady). void datasetReady(std::shared_ptr dataset); void datasetConsumed(); // ack to the worker that one dataset has been consumed (backpressure) void LoadFileRequest(const QString &filename, qint64 image_number, qint64 summation, bool retry); void LoadImageRequest(int64_t image_number, int64_t summation); void HttpTokenRequest(QString token); void adjustForegroundButton(bool input); void adjustBackgroundButton(bool input); void setAutoForeground(bool val); void oneShotAutoForeground(); // Re-emitted by the shortcut filter; the navigation toolbar applies the step, clamped. void stepImage(int steps); };