5#include <QtCore/QFileInfo>
15 , _watcher(new QFileSystemWatcher(this))
16 , _debounceTimer(new QTimer(this))
18 connect(_watcher, &QFileSystemWatcher::fileChanged,
19 this, &QGCFileWatcher::_onFileChanged);
20 connect(_watcher, &QFileSystemWatcher::directoryChanged,
21 this, &QGCFileWatcher::_onDirectoryChanged);
23 _debounceTimer->setSingleShot(
true);
24 connect(_debounceTimer, &QTimer::timeout,
25 this, &QGCFileWatcher::_processPendingChanges);
39 _debounceDelay = qMax(0, milliseconds);
48 if (filePath.isEmpty()) {
49 qCWarning(QGCFileWatcherLog) <<
"watchFile: empty path";
53 const QString canonicalPath = QFileInfo(filePath).absoluteFilePath();
55 if (!QFileInfo::exists(canonicalPath)) {
56 qCWarning(QGCFileWatcherLog) <<
"watchFile: file does not exist:" << filePath;
60 if (_watcher->addPath(canonicalPath)) {
62 _fileCallbacks[canonicalPath] = callback;
64 qCDebug(QGCFileWatcherLog) <<
"Watching file:" << canonicalPath;
69 if (_watcher->files().contains(canonicalPath)) {
71 _fileCallbacks[canonicalPath] = callback;
76 qCWarning(QGCFileWatcherLog) <<
"watchFile: failed to add:" << filePath;
82 const QString canonicalPath = QFileInfo(filePath).absoluteFilePath();
84 _fileCallbacks.remove(canonicalPath);
85 _persistentFiles.remove(canonicalPath);
86 _pendingFileChanges.remove(canonicalPath);
88 if (_watcher->removePath(canonicalPath)) {
89 qCDebug(QGCFileWatcherLog) <<
"Stopped watching file:" << canonicalPath;
98 const QString canonicalPath = QFileInfo(filePath).absoluteFilePath();
99 return _watcher->files().contains(canonicalPath);
104 return _watcher->files();
113 if (directoryPath.isEmpty()) {
114 qCWarning(QGCFileWatcherLog) <<
"watchDirectory: empty path";
118 const QString canonicalPath = QFileInfo(directoryPath).absoluteFilePath();
120 if (!QFileInfo(canonicalPath).isDir()) {
121 qCWarning(QGCFileWatcherLog) <<
"watchDirectory: not a directory:" << directoryPath;
125 if (_watcher->addPath(canonicalPath)) {
127 _directoryCallbacks[canonicalPath] = callback;
129 qCDebug(QGCFileWatcherLog) <<
"Watching directory:" << canonicalPath;
134 if (_watcher->directories().contains(canonicalPath)) {
136 _directoryCallbacks[canonicalPath] = callback;
141 qCWarning(QGCFileWatcherLog) <<
"watchDirectory: failed to add:" << directoryPath;
147 const QString canonicalPath = QFileInfo(directoryPath).absoluteFilePath();
149 _directoryCallbacks.remove(canonicalPath);
150 _pendingDirectoryChanges.remove(canonicalPath);
152 if (_watcher->removePath(canonicalPath)) {
153 qCDebug(QGCFileWatcherLog) <<
"Stopped watching directory:" << canonicalPath;
162 const QString canonicalPath = QFileInfo(directoryPath).absoluteFilePath();
163 return _watcher->directories().contains(canonicalPath);
168 return _watcher->directories();
178 for (
const QString &path : filePaths) {
189 for (
const QString &path : directoryPaths) {
199 const QStringList files = _watcher->files();
200 if (!files.isEmpty()) {
201 _watcher->removePaths(files);
204 const QStringList dirs = _watcher->directories();
205 if (!dirs.isEmpty()) {
206 _watcher->removePaths(dirs);
209 _fileCallbacks.clear();
210 _directoryCallbacks.clear();
211 _persistentFiles.clear();
212 _pendingFileChanges.clear();
213 _pendingDirectoryChanges.clear();
214 _debounceTimer->stop();
216 qCDebug(QGCFileWatcherLog) <<
"Cleared all watches";
225 const QString canonicalPath = QFileInfo(filePath).absoluteFilePath();
228 const QString parentDir = QFileInfo(canonicalPath).absolutePath();
229 if (!_watcher->directories().contains(parentDir)) {
230 _watcher->addPath(parentDir);
233 _persistentFiles.insert(canonicalPath);
235 if (QFileInfo::exists(canonicalPath)) {
236 return watchFile(canonicalPath, callback);
241 _fileCallbacks[canonicalPath] = callback;
244 qCDebug(QGCFileWatcherLog) <<
"Watching file (persistent):" << canonicalPath;
252void QGCFileWatcher::_onFileChanged(
const QString &path)
254 qCDebug(QGCFileWatcherLog) <<
"File changed:" << path;
257 if (_persistentFiles.contains(path) && !QFileInfo::exists(path)) {
259 qCDebug(QGCFileWatcherLog) <<
"Persistent file deleted, waiting for recreation:" << path;
262 _scheduleCallback(path,
false);
265void QGCFileWatcher::_onDirectoryChanged(
const QString &path)
267 qCDebug(QGCFileWatcherLog) <<
"Directory changed:" << path;
270 for (
const QString &persistentPath : std::as_const(_persistentFiles)) {
271 if (persistentPath.startsWith(path) && QFileInfo::exists(persistentPath)) {
272 if (!_watcher->files().contains(persistentPath)) {
273 _watcher->addPath(persistentPath);
274 qCDebug(QGCFileWatcherLog) <<
"Re-added persistent file:" << persistentPath;
277 _scheduleCallback(persistentPath,
false);
282 _scheduleCallback(path,
true);
285void QGCFileWatcher::_scheduleCallback(
const QString &path,
bool isDirectory)
288 _pendingDirectoryChanges.insert(path);
290 _pendingFileChanges.insert(path);
293 if (_debounceDelay > 0) {
294 if (!_debounceTimer->isActive()) {
295 _debounceTimer->start(_debounceDelay);
297 }
else if (!_processingPendingChanges) {
299 _processPendingChanges();
303void QGCFileWatcher::_processPendingChanges()
305 if (_processingPendingChanges) {
309 _processingPendingChanges =
true;
312 const QSet<QString> fileChanges = _pendingFileChanges;
313 const QSet<QString> directoryChanges = _pendingDirectoryChanges;
314 _pendingFileChanges.clear();
315 _pendingDirectoryChanges.clear();
317 for (
const QString &path : fileChanges) {
320 auto it = _fileCallbacks.find(path);
321 if (it != _fileCallbacks.end() && it.value()) {
326 for (
const QString &path : directoryChanges) {
329 auto it = _directoryCallbacks.find(path);
330 if (it != _directoryCallbacks.end() && it.value()) {
334 }
while ((_debounceDelay == 0) && (!_pendingFileChanges.isEmpty() || !_pendingDirectoryChanges.isEmpty()));
336 _processingPendingChanges =
false;
Wrapper around QFileSystemWatcher with callback-based API.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool unwatchDirectory(const QString &directoryPath)
bool isWatchingDirectory(const QString &directoryPath) const
void clear()
Stop watching all files and directories.
int watchFiles(const QStringList &filePaths, ChangeCallback callback)
int watchDirectories(const QStringList &directoryPaths, ChangeCallback callback)
~QGCFileWatcher() override
QStringList watchedFiles() const
void setDebounceDelay(int milliseconds)
bool isWatchingFile(const QString &filePath) const
bool watchFilePersistent(const QString &filePath, ChangeCallback callback)
void directoryChanged(const QString &path)
void fileChanged(const QString &path)
std::function< void(const QString &path)> ChangeCallback
QStringList watchedDirectories() const
bool unwatchFile(const QString &filePath)
bool watchFile(const QString &filePath, ChangeCallback callback)
bool watchDirectory(const QString &directoryPath, ChangeCallback callback)