[Solved] QFileSystemWatcher parallel to console application



  • Hi everybody,
    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 alos 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 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?
    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();
    }
    
    

  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Since you're starting your infinite loop in the main thread, it is blocking the event loop so there won't be any signal/slot communication



  • But when starting the program I see the message "Enter main event loop now". That would mean that also a.exec() is executed and we enter the main event loop? Furthermore the signal/slot in QObject::connect(&console, SIGNAL(signalEnd()), &a, SLOT(quit())); works perfectly and the program ends correctly without errors or crash. However, as you suggested, the problem seems to be that I only use one thread. The problem is finally solved by creating a second thread. So I have a new subclass of QObject called MyObject where I create a new thread and the console object. The QFileSystemWatcher is still created in main() as well as an instance of MyObject. Here comes the new code which solved the problem:
    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).


  • Lifetime Qt Champion

    @Varius said:

    thread = new QThread(this);
    console->moveToThread(thread);

    IIRC, these lines are doing more than you think they do. thread's parent is your object, then you move your MyObject instance to thread, so all objects who are child of your MyObject instance are moved the thread. So thread will be moved to itself.

    All in all, you should rather have thread in your main as a stack object



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.