Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QFileSystemWatcher sending only one signal



  • Hello, I'm trying to make a very simple widget to test the QFileSystemWatcher and know how many times a file was saved (even if not changed). I think I got almost everything right, but it seems that once the QFileSystemWatcher sends the signal to the slot, it removes the file from the watcher path, and I have to re-add it.
    For now, the code I have is:

    widget.h

    #ifndef WIDGET_H
    #define WIDGET_H
    
    #include <QWidget>
    #include <QFileSystemWatcher>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class Widget; }
    QT_END_NAMESPACE
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
        Widget(QWidget *parent = nullptr);
        ~Widget();
    
    public slots:
        void showModified(const QString &str);
    
    private slots:
        void on_pushButton_clicked();
    
    private:
        Ui::Widget *ui;
        QFileSystemWatcher *watcher;
        int numChanges;
    };
    #endif // WIDGET_H
    

    widget.cpp

    #include "widget.h"
    #include "ui_widget.h"
    #include <QDebug>
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
        , watcher(new QFileSystemWatcher)
        , numChanges(0)
    {
        ui->setupUi(this);
        QObject::connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(showModified(QString)));
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    void Widget::showModified(const QString &str){
        Q_UNUSED(str);
        this->numChanges++;
        this->ui->fileChangedMonitor->setText("Yes");
        QStringList fileList = watcher->files();
        qDebug() << "Changed " << this->numChanges << " times";
        qDebug() << "Files being wathced:";
        Q_FOREACH(QString file, fileList)
                    qDebug() << "- " << file << " on watch list";
    }
    
    void Widget::on_pushButton_clicked()
    {
        watcher->addPath(this->ui->filePath->text());
    }
    

    If I leave like this, the console writes (even if changing more than once):

    11:32:01: Starting /home/minterciso/Programacao/qt/build-sampleFileWatcher-Desktop-Debug/sampleFileWatcher ...
    qt.qpa.xcb: QXcbConnection: XCB error: 5 (BadAtom), sequence: 481, resource id: 0, major code: 20 (GetProperty), minor code: 0
    Changed  1  times
    Files being wathced:
    11:32:24: /home/minterciso/Programacao/qt/build-sampleFileWatcher-Desktop-Debug/sampleFileWatcher exited with code 0
    

    If however I change the public slot to:

    void Widget::showModified(const QString &str){
        this->numChanges++;
        this->ui->fileChangedMonitor->setText("Yes");
        watcher->removePath(str);
        watcher->addPath(str);
        QStringList fileList = watcher->files();
        qDebug() << "Changed " << this->numChanges << " times";
        qDebug() << "Files being wathced:";
        Q_FOREACH(QString file, fileList)
                    qDebug() << "- " << file << " on watch list";
    }
    

    Then I get 2 signals on every change (I've saved the file 4 times):

    11:33:34: Starting /home/minterciso/Programacao/qt/build-sampleFileWatcher-Desktop-Debug/sampleFileWatcher ...
    qt.qpa.xcb: QXcbConnection: XCB error: 5 (BadAtom), sequence: 481, resource id: 0, major code: 20 (GetProperty), minor code: 0
    Changed  1  times
    Files being wathced:
    -  "/home/minterciso/tst.txt"  on watch list
    Changed  2  times
    Files being wathced:
    -  "/home/minterciso/tst.txt"  on watch list
    Changed  3  times
    Files being wathced:
    -  "/home/minterciso/tst.txt"  on watch list
    Changed  4  times
    Files being wathced:
    -  "/home/minterciso/tst.txt"  on watch list
    Changed  5  times
    Files being wathced:
    -  "/home/minterciso/tst.txt"  on watch list
    Changed  6  times
    Files being wathced:
    -  "/home/minterciso/tst.txt"  on watch list
    Changed  7  times
    Files being wathced:
    -  "/home/minterciso/tst.txt"  on watch list
    Changed  8  times
    Files being wathced:
    -  "/home/minterciso/tst.txt"  on watch list
    11:33:48: /home/minterciso/Programacao/qt/build-sampleFileWatcher-Desktop-Debug/sampleFileWatcher exited with code 0
    

    Is it really like this that I should make? Or is there any other way of making this?

    Best regards


  • Lifetime Qt Champion

    @Mateus-Interciso What Qt version do you use?



  • @jsulm I'm using 5.15.2 on a kubuntu 21.04.

    Looking a little more on the signal documentation, it seems that this may happen from application to application that is writing the file (maybe vi is one of those):

    Note: As a safety measure, many applications save an open file by writing a new file and then deleting the old one. In your slot function, you can check watcher.files().contains(path). If it returns false, check whether the file still exists and then call addPath() to continue watching it.
    

    So I've updated the slot to:

    if(this->watcher->files().contains(str) == false)
            this->watcher->addPath(str);
    

    And of course, it's working. I'm still finding odd however the amount of time the slot is being called, but if the application removes and create a new one, then it may be because:

    1. Remove the file (send one signal)
    2. Create a new file (sent another signal)

    If this is the case, is there any option to check why the file was changed? I mean, without using inotify of course.


  • Lifetime Qt Champion

    @Mateus-Interciso I think your observation is correct. Considering "Note that QFileSystemWatcher stops monitoring files once they have been renamed or removed from disk, and directories once they have been removed from disk." it explains why the path disappears from the watcher in case an editor uses a temp file.
    As far as I know there is no way to get the exact information about what triggered the watcher.


Log in to reply