In an effort to create a Qt event loop in a separate thread, from within a DLL which is called by a main application written in Java, I have done the following, based on a suggestion I read here at http://stackoverflow.com/questions/2...qt-application, which works rather well:
// Define a global namespace. We need to do this because the parameters passed to QCoreApplication
// must have a lifetime exceeding that of the QCoreApplication object
namespace ToolThreadGlobal
{
static int argc = 1;
static char * argv[] = { "MyVirtualMainApplication.exe", NULL };
static ToolThread *toolThread = nullptr;
};
//! The ToolThread class differs from a standard QThread only in its run() method
{
//! Override QThread's run() method so that it calls the QCoreApplication's exec() method
//! rather than the QThread's own
void run() { ToolThreadGlobal::coreApp -> exec(); }
};
class ThreadStarter
: public QObject {
Q_OBJECT
public:
//! Constructor
ThreadStarter()
{
// Set up the tool thread:
if (!ToolThreadGlobal::toolThread)
{
ToolThreadGlobal::toolThread = new ToolThread();
connect(ToolThreadGlobal::toolThread, &ToolThread::started,
this, &ThreadStarter::createApplication, Qt::DirectConnection);
// The thread's 'started' event is emitted after the thread is started but
// before its run() method is invoked. By arranging for the createApplication
// subroutine to be called before run(), we ensure that the required QCoreApplication
// object is instantiated *within the thread* before ToolThread's customised run()
// method calls the application's exec() command.
ToolThreadGlobal::toolThread->start();
}
}
//! Destructor
~ThreadStarter()
{
// Ensure that the thread and the QCoreApplication are cleanly shut down:
ToolThreadGlobal::coreApp -> quit();
delete ToolThreadGlobal::coreApp;
ToolThreadGlobal::coreApp = nullptr;
delete ToolThreadGlobal::toolThread;
ToolThreadGlobal::toolThread = nullptr;
}
//! Function to return a pointer to the actual tool thread:
ToolThread* getThread() const { return ToolThreadGlobal::toolThread; }
private:
//! Create the QCoreApplication that will provide the tool thread's event loop
/*! This subroutine is intended to be called from the tool thread itself as soon
as the thread starts up.
*/
void createApplication()
{
// Start the QCoreApplication event loop, so long as no other Qt event loop
// is already running
{
ToolThreadGlobal::argv);
}
}
};
// Define a global namespace. We need to do this because the parameters passed to QCoreApplication
// must have a lifetime exceeding that of the QCoreApplication object
namespace ToolThreadGlobal
{
static int argc = 1;
static char * argv[] = { "MyVirtualMainApplication.exe", NULL };
static QCoreApplication *coreApp = nullptr;
static ToolThread *toolThread = nullptr;
};
//! The ToolThread class differs from a standard QThread only in its run() method
class ToolThread : public QThread
{
//! Override QThread's run() method so that it calls the QCoreApplication's exec() method
//! rather than the QThread's own
void run() { ToolThreadGlobal::coreApp -> exec(); }
};
class ThreadStarter : public QObject
{
Q_OBJECT
public:
//! Constructor
ThreadStarter()
{
// Set up the tool thread:
if (!ToolThreadGlobal::toolThread)
{
ToolThreadGlobal::toolThread = new ToolThread();
connect(ToolThreadGlobal::toolThread, &ToolThread::started,
this, &ThreadStarter::createApplication, Qt::DirectConnection);
// The thread's 'started' event is emitted after the thread is started but
// before its run() method is invoked. By arranging for the createApplication
// subroutine to be called before run(), we ensure that the required QCoreApplication
// object is instantiated *within the thread* before ToolThread's customised run()
// method calls the application's exec() command.
ToolThreadGlobal::toolThread->start();
}
}
//! Destructor
~ThreadStarter()
{
// Ensure that the thread and the QCoreApplication are cleanly shut down:
ToolThreadGlobal::coreApp -> quit();
delete ToolThreadGlobal::coreApp;
ToolThreadGlobal::coreApp = nullptr;
delete ToolThreadGlobal::toolThread;
ToolThreadGlobal::toolThread = nullptr;
}
//! Function to return a pointer to the actual tool thread:
ToolThread* getThread() const { return ToolThreadGlobal::toolThread; }
private:
//! Create the QCoreApplication that will provide the tool thread's event loop
/*! This subroutine is intended to be called from the tool thread itself as soon
as the thread starts up.
*/
void createApplication()
{
// Start the QCoreApplication event loop, so long as no other Qt event loop
// is already running
if (QCoreApplication::instance() == NULL)
{
ToolThreadGlobal::coreApp = new QCoreApplication(ToolThreadGlobal::argc,
ToolThreadGlobal::argv);
}
}
};
To copy to clipboard, switch view to plain text mode
To use this, a subroutine called from the main Java applications’ thread just needs to create a ThreadStarter object
which will automatically create a ToolThread with a QCoreApplication running inside it:
itsThreadStarter = new ThreadStarter();
itsToolThread = itsThreadStarter -> getThread();
itsThreadStarter = new ThreadStarter();
itsToolThread = itsThreadStarter -> getThread();
To copy to clipboard, switch view to plain text mode
We can then instantiate a QObject class in the usual way, move it to the thread and call its methods asynchronously using QMetaObject::invokeMethod:
itsWorker = new Worker();
itsWorker -> moveToThread(itsToolThread);
QMetaObject::invokeMethod(itsWorker, “doSomethingInterestingâ€
);
itsWorker = new Worker();
itsWorker -> moveToThread(itsToolThread);
QMetaObject::invokeMethod(itsWorker, “doSomethingInterestingâ€);
To copy to clipboard, switch view to plain text mode
When we’re done, we just delete the ThreadStarter object and everything is cleaned up nicely. Apart from the annoying message saying
WARNING: QApplication was not created in the main() thread
on startup, it seems to meet all my requirements.
Except… (and here, at last, is my question).
Occasionally - and without any pattern that I’ve been able to discern so far - I get an error during the shutdown process. Usually it occurs at the line
delete ToolThreadGlobal::coreApp;
delete ToolThreadGlobal::coreApp;
To copy to clipboard, switch view to plain text mode
but sometimes at the line
ToolThreadGlobal::coreApp -> exec();
ToolThreadGlobal::coreApp -> exec();
To copy to clipboard, switch view to plain text mode
(which of course is executed in the thread’s run() method and doesn’t return until after ToolThreadGlobal::coreApp -> quit(); has been fully executed).
Often the error message is a simple access violation; sometimes it’s a rather more involved:
ASSERT failure in QObjectPrivate::deleteChildren(): "isDeletingChildren already set, did this function recurse?", file ..\qtbase\src\corelib\kernel\qobject.cpp, line 1927
I assume it’s because, once I issue the quit() command to the QCoreApplication, I should be waiting for a little while for it to close down the event loop properly before deleting it - just as one would usually call quit() and then wait() on an ordinary QThread before deleting it. However, QCoreApplication doesn’t seem to have the equivalent of a wait() command, and I can’t implement a QTimer to force a delay because it wouldn’t work once I’ve closed down the event loop with quit(). I’m therefore at a loss what to do. I have an inkling that, as QCoreApplication is a QObject, I could call its deleteLater() method but I can’t see where I should call it from.
Is there an expert out there who understands the ins and outs of QCoreApplication and QThread well enough to suggest a solution to this? Or is there a fundamental flaw in the way that I have designed this?
Bookmarks