Calling a shared library that creates/uses Qt GUI
-
Hello all...
I'm trying to call a shared library (specifically, a Mac dylib) from a main program that knows nothing of Qt. Both the main program and Qt dynamic library are in <code>Qt Creator</code>.
I have the entry point into the library working just fine, my main program calls it with no issues. The problem I'm having is: at what point do I create the <code>QApplication</code> to get the ball rolling in the library?
This is complicated by my wanting to use a <code>QThread</code> to spawn off a thread to run the Qt-based code in the shared library. I'm hoping I can then return to the main program and let the Qt GUI run. Ultimately, I'd like to put entry points in the library to let the main program stop the Qt thread.
I'm hoping this is possible, and that nobody's head has exploded at the thought of what I'm trying to do. ;)
If there's some document that gives hints on how to do this, that'd be great, otherwise, any pearls of wisdom would be appreciated.
And no question would be complete without some code snippets...
The library entry point looks like this:
@
static Runner *theObject;QString dialogString = "Using default label";
// create QApplication
int argc = 0;
const QApplication *a = new QApplication(argc, nullptr);// OK, set up the thread and let it loose...
theObject = new Runner(dialogString, *a);// Conect the Runner object to a new thread
QThread* thread = new QThread;// This code is taken from http://qt-project.org/wiki/QThreads_general_usage
theObject->moveToThread(thread);
theObject->connect(thread, SIGNAL(started()), theObject, SLOT(process()));theObject->connect(theObject, SIGNAL(finished()), thread, SLOT(quit()));
theObject->connect(theObject, SIGNAL(finished()), theObject, SLOT(deleteLater()));
theObject->connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}@The <code>Runner</code> object looks like this:
@class Runner : public QObject
{
Q_OBJECTpublic:
Runner(const QString label, const QApplication &a)
{
thisQApp = &a;
messageLabel = label;
}~Runner()
{
}public slots:
void process();signals:
void finished();
void error(QString err);private:
QString messageLabel;
const QApplication *thisQApp;};@
and the <code>process</code> method of the <code>Runner</code> object looks like this:
@void Runner::process()
{
PoppyUpper *thePup;thePup = new PoppyUpper();
thePup->setRandomColors();
thePup->setLabel(messageLabel);thePup->show();
thisQApp->exec();
delete thePup;
emit finished();
}@<code>PoppyUpper</code> is the function in the library does does all the GUI goodness. Everything works find if I don't put <code>QThread</code>s into the mix.
What happens currently (in the debugger) is that <code>Runner::process()</code> never gets called, and the <code>thread->start()</code> returns with no error message and the whole program exits.
Oh, and in case it makes any difference, the fact that it's a dynamic library is entirely optional. It could just as well be static-linked, in case that makes any difference.
Hopefully, I'm just overlooking something...
Thanks!
-Eric
-
Hi,
See http://lists.qt-project.org/pipermail/development/2013-December/014702.html and the messages before it. You can't use a QThread before your QApplication has been created.
It's very tricky on Mac. In other OS'es, you can simply construct your QApplication in a separate std::thread and start its event loop -- voila, Qt Widgets called from a shared library.
However, the Cocoa framework requires all GUI operations to occur on the thread that calls main(). If you can find a way construct your dylib's QApplication in that thread while moving your "main" code to a separate thread, you'll be good to go.
-
Already running into confusion... in that message thread you mention constructing the QApplication in the main thread.
First question: is "constructing" different from calling the QApplication's exec method in this context? Can
QThreads be used after constructing the Qapplication or only after running the QApplication's exec method?Second, in the message thread you cited it says:
bq. But it's not that simple,
because the application must be created in the "main" thread because there
might be global objects. So you may want to dlopen Qt from the new thread to
initialize the global object from that thread.Which begs the question... what is gained by explicitly calling <code>dlopen</code> to access the library?
And third, the tricky bit would definitely be trying to move the QApplication to the main native thread. <code>moveToThread</code> is a <code>QThread</code>-ism. The main thread is a native thread not a <code>QThread</code>. I see you can get the native thread from a <code>QThread</code> but I don't see a way to create a QThread from a native thread.
So I'm kind of at a loss right now. Part of the problem, I think, is that <code>QApplication.exec()</code> doesn't ever return until the application is done. So running that in the main thread, would block the main thread (true? false?). I almost need a <code>QApplication.execAsync()</code>.
Not sure... kind of thinking out loud here... any other ideas ... anyone?
-
Hi,
I don't have a Mac, so the only things I know are what I've read, not from experience. I'll try to answer what I can, but I recommend subscribing to the "Interest mailing list":http://lists.qt-project.org/mailman/listinfo/interest and asking there. Qt's engineers are active there.
[quote author="EricRFMA" date="1390274726"]First question: is "constructing" different from calling the QApplication's exec method in this context? Can
QThreads be used after constructing the Qapplication or only after running the QApplication's exec method?[/quote]They are different.Constructing QApplication (or QCoreApplication) initializes lots of stuff behind-the-scenes.
exec() simply starts an event loop to handle signals+slots and events. This is required to handle GUI events, but not required to use QThreads.
[quote]what is gained by explicitly calling <code>dlopen</code> to access the library?[/quote]I don't know this one.
[quote]And third, the tricky bit would definitely be trying to move the QApplication to the main native thread. <code>moveToThread</code> is a <code>QThread</code>-ism. The main thread is a native thread not a <code>QThread</code>. I see you can get the native thread from a <code>QThread</code> but I don't see a way to create a QThread from a native thread.[/quote]Yes, "move to that thread" is a Qt-ism. It means "I want the (queued) slots of this QObject to run in that thread". "Raw" C++ does not have the concept of signals+slots, therefore it doesn't have the concept of moving threads either. See "QObject thread affinity":http://qt-project.org/doc/qt-5/QObject.html#thread-affinity for more info on moving.
The only way to get a QApplication to live in the main thread is to have that thread run a function that constructs the QApplication. A QObject automatically lives in the thread that created it.
[quote]So I'm kind of at a loss right now. Part of the problem, I think, is that <code>QApplication.exec()</code> doesn't ever return until the application is done. So running that in the main thread, would block the main thread (true? false?).[/quote]True. Which is why I said you'll need to move your non-GUI code out of the way into a secondary thread.
Anyway, try asking at the mailing list to see if anyone has found a good solution for Mac OS X.
-
You're welcome :) Good luck!
-
Hi !
I know this is 2 years old... But i stumbled into a very related problem.
However my shared lib is not running GUI so i am talking about QCoreApplication.
But it seems the hacky solution i found would work for QApplication as well (to be confirmed).I create the QCoreApplication like this:
int main(int argc, char *argv[]) { QCoreApplication myQCA(argc, argv); return NSApplicationMain(argc, (const char **) argv); }
Later in the Objective C code i create a NSTimer (100 mSec) and this timer calls
QCoreApplication::processEvents():
This way signals/slots/QTimer works well.
This might also works for QApplication.
If someone has a better idea PLEASE share it !
Greetings,
Nils
P.S. i tried to put QCoreApplication in a posix thread and exec it but that did not make signals/slots work although there was no complaint. A QTimer complained that its Thread was not created by QThread...