[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 methodconsoleMain()
). However, in main.cpp I also have a
QFileSystemWatcher
which watches the file MyTextFile.txt and callsConsole::slotFileChanged(QString)
whenever the text file changes. Unfortunately theQFileSystemWatcher
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 theQTimer::singlaShot
in the main.cpp and replace it byemit console.signalStart()
the main event loop will not be entered, but I see the message of theQFileSystemWatcher
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(); }
-
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 inQObject::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 ofQObject
calledMyObject
where I create a new thread and the console object. TheQFileSystemWatcher
is still created inmain()
as well as an instance ofMyObject
. 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 byConsole::displayChangedFileText(QString)
. -
@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