The Files panel never showed files written after a directory was first listed when they came from another host (a detector writer on NFS/GPFS/ Lustre), because QFileSystemModel only learns about changes through its inotify watching, and there was no way to refresh it by hand. F5 (window-wide, only while the panel is visible) and a refresh button next to the root's browse button re-list the root and every expanded directory. A timer does the same every 3 s while the panel is visible and no mouse button is held, but re-lists only directories whose mtime changed since the last listing, so a steady tick costs one stat per expanded directory. QFileSystemModel has no rescan call; index() of a path it does not know adds that row in place, so only the names missing from the model are passed (index() is ~10 us a call; a CBF sweep is thousands of files). Selection and expansion are untouched, new rows are sorted into place, and the first visible row is kept where it was when rows land above it. Entries deleted by another host still stay listed; local deletes are seen by the model's own watching as before. Measured offscreen on a 5000-file directory: unchanged tick ~1 ms, re-listing the changed 5000-file directory ~5 ms. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
67 lines
2.9 KiB
C++
67 lines
2.9 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
#include <QWidget>
|
|
#include <QFileSystemModel>
|
|
#include <QSortFilterProxyModel>
|
|
#include <QHash>
|
|
#include <QDateTime>
|
|
|
|
class QLineEdit;
|
|
class QTreeView;
|
|
class QModelIndex;
|
|
|
|
// Left-side dock alternative to the Processing (reprocessing settings) panel: a directory tree rooted at a
|
|
// configurable data root, filtered down to subdirectories and *_master.h5 / *_process.h5 / *.cbf
|
|
// files, for browsing to and opening a dataset without caring about reprocessing. Backed by QFileSystemModel,
|
|
// which only lists a directory once it is expanded - so a root with hundreds of sibling directories
|
|
// (e.g. an SLS beamline's /sls/mx/data/) costs one readdir at each level actually opened, never a
|
|
// recursive scan of the whole tree.
|
|
//
|
|
// New files show up by themselves: F5 (or the refresh button) re-lists the root and every expanded
|
|
// directory at once, and a timer does the same every few seconds while the panel is visible. The model's
|
|
// own inotify watching covers a local disk, but not files written by another host onto NFS/GPFS/Lustre,
|
|
// which is exactly how a detector writer fills a beamline's data directory - hence the polling.
|
|
class JFJochViewerFileBrowser : public QWidget {
|
|
Q_OBJECT
|
|
|
|
QLineEdit *root_edit_;
|
|
QLineEdit *filter_edit_;
|
|
QTreeView *tree_;
|
|
QFileSystemModel *model_;
|
|
QSortFilterProxyModel *proxy_;
|
|
// Modification time of each directory when it was last listed, so a poll re-lists only those that changed.
|
|
QHash<QString, QDateTime> listed_mtime_;
|
|
|
|
void SetRoot(const QString &path);
|
|
void CollectExpanded(const QModelIndex &parent, QStringList &dirs) const;
|
|
[[nodiscard]] static QString DefaultRoot();
|
|
// An SLS account eNNNNN browses its proposal group's raw data, which is pNNNNN with the
|
|
// same five digits; empty for any other user name, on any platform.
|
|
[[nodiscard]] static QString SlsDataRoot(const QString &user);
|
|
|
|
public slots:
|
|
// "Reset all settings to defaults": back to the root a fresh start would pick.
|
|
void ResetToDefaultRoot();
|
|
// Re-list the root and the expanded directories; unless forced, only those modified since last time.
|
|
void Refresh(bool force = true);
|
|
|
|
private slots:
|
|
void onBrowseClicked();
|
|
void onRootEditingFinished();
|
|
void onDoubleClicked(const QModelIndex &index);
|
|
|
|
signals:
|
|
// Mirrors JFJochViewerMenu::fileOpenSelected, so the window wires it to LoadFile the same way.
|
|
void openDataset(const QString &filename, qint64 image_number, qint64 summation, bool retry);
|
|
|
|
public:
|
|
explicit JFJochViewerFileBrowser(QWidget *parent = nullptr);
|
|
|
|
// A dataset opened elsewhere (menu, D-Bus) and lying inside the current root moves the tree's
|
|
// selection to it; one outside the root leaves the browser exactly as it was.
|
|
void FollowPath(const QString &path);
|
|
};
|