c++qtconsoleqfilesystemwatcher

QFileSystemWatcher does not emit fileChanged() in console application


in my files console.h/.cpp i have a small class which just asks the user to type in some text and then just prints the text again until the user enters "quit" (see method consoleMain()). However, in main.cpp I also have a QFileSystemWatcher which watches the file MyTextFile.txt and calls Console::slotFileChanged(QString) whenever the text file changes. Unfortunately the QFileSystemWatcher does not work. Console::slotFileChanged(QString) is never executed when I change the text file. As far as I know, QFileSystemWatcher works only if the main event loop has been started and this is also the case in my code. When I disable the QTimer::singlaShot in the main.cpp and replace it by emit console.signalStart() the main event loop will not be entered, but I see the message of the QFileSystemWatcher ("File changed!") after I enter "quit". The question is: Is it possible to let the user interact with the console and let the FileWatcher emit the signal when the text file is changed in parallel? (I've also tried to put the QFileSystemWatcher into the console class and creating it on the heap; unfortunately it didn't change anything)

Here is my code:

console.h

#ifndef CONSOLE_H
#define CONSOLE_H

#include <iostream>
#include <QObject>
#include <QFileSystemWatcher>

class Console: public QObject
{
    Q_OBJECT

public:

    Console(QObject *parent = 0);
    ~Console();

signals:

    void signalStart();
    void signalEnd();

public slots:

    void consoleMain();
    void slotFileChanged(QString text);
    void slotEmit();
};

#endif // CONSOLE_H

console.cpp

#include "console.h"

Console::Console(QObject *parent): QObject(parent)
{
}

Console::~Console()
{
}

void Console::consoleMain()
{
    bool isRunning = true;
    std::string in;

    while (isRunning)
    {
        std::cout << ">" << std::flush;
        std::getline(std::cin, in);

        if (in.compare("quit") == 0)
            isRunning = false;
        else
            std::cout << "You have entered: " << in << std::endl;
    }

    emit signalEnd();
}

void Console::slotFileChanged(QString text)
{
    Q_UNUSED(text);
    std::cout << "File changed!" << std::endl;
}  

void Console::slotEmit()
{
    emit signalStart();
}

main.cpp

#include "console.h"
#include <QCoreApplication>
#include <QTimer>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QFileSystemWatcher watcher(&a);
    watcher.addPath("C:/MyTextFile.txt");

    Console console(&a);

    QObject::connect(&console, SIGNAL(signalStart()), &console, SLOT(consoleMain()));
    QObject::connect(&console, SIGNAL(signalEnd()), &a, SLOT(quit()));
    QObject::connect(&watcher, SIGNAL(fileChanged(QString)), &console, SLOT(slotFileChanged(QString)));

    QTimer::singleShot(0, &console, SLOT(slotEmit()));
    //emit console.signalStart();

    std::cout << "Enter main event loop now" << std::endl;
    return a.exec();
}

Solution

  • Ok, it is solved. I've tried Yakk's idea using different threads (thanks for the idea Yakk). I had to introduce a new subclass of QObject called MyObject. In its constructor I create the Console and a new QThread for the console object. The QFileSystemWatcher is created in main.cpp as well as an instance of MyObjcet. See code below:

    myobject.h

    #ifndef MYOBJECT_H
    #define MYOBJECT_H
    
    #include "console.h"
    #include <QThread>
    #include <QCoreApplication>
    
    class MyObject : public QObject
    {
        Q_OBJECT
    
    public:
    
        MyObject(QObject *parent = 0);
        ~MyObject();
    
    private:
    
        QThread *thread;
        Console *console;
    
    signals:
    
        void signalStart();
    
    public slots:
    
        void slotFileChanged(QString text);
        void slotEnd();
    };
    
    #endif // MYOBJECT_H
    

    myobject.cpp

    #include "myobject.h"
    
    MyObject::MyObject(QObject *parent): QObject(parent)
    {
        console = new Console;
    
        thread = new QThread(this);
        console->moveToThread(thread);
        thread->start();
    
        connect(this, SIGNAL(signalStart()), console, SLOT(consoleMain()));
        connect(console, SIGNAL(signalEnd()), this, SLOT(slotEnd()));
        emit signalStart();
    }
    
    MyObject::~MyObject()
    {
        thread->quit();
        thread->wait();
    }
    
    void MyObject::slotFileChanged(QString text)
    {
        console->displayChangedFileText(text);
    }
    
    void MyObject::slotEnd()
    {
        QCoreApplication::exit(0);
    }
    

    main.cpp

    #include "myobject.h"
    #include <QTimer>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        QFileSystemWatcher *watcher = new QFileSystemWatcher(&a);
        watcher->addPath("C:/MyTextFile.txt");
    
        MyObject *object = new MyObject(&a);
    
        QObject::connect(watcher, SIGNAL(fileChanged(QString)), object, SLOT(slotFileChanged(QString)));
    
        std::cout << "Enter main event loop now" << std::endl;
        return a.exec();
    }
    

    console.h/.cpp are unchnaged, only Console::slotFileChanged(QString) was replaced by Console::displayChangedFileText(QString).