PDA

View Full Version : API Development - QEventLoop: Cannot be used without QApplication



erichumenay
17th February 2015, 16:41
We developed an API in Qt, and it worked beautifully when called from another Qt application. However, whenever I tried use the API by calling it from a C# application I ran into some troubles. I noticed the API functions that used helper threads were not working when I called them from the C# application. Whenever I started debugging this on the API side I realized the following error was occurring “QEventLoop: Cannot be used without QApplication”. This error occurs for each helper thread that the API spawns with the end result being the threads don’t appear to be doing anything.

I did some research on this and I realize the problem being that QEventLoop needs to be called from an instance of QApplication which in my limited QT experience resides in main.cpp.

So with all that being said, I am left with the impression that the API in its current state is only going to be compatible with QT applications, and if I needed it to be compatible with anything else we are going to have reimplement the threading on the API side.

Is my conclusion correct? From what I understand that QApplication can only be called in the main loop of an application and not in an initialization routine in an API function. Does anyone have any suggestions and/or workarounds for this issue?

jefftee
17th February 2015, 19:18
Have you considered creating a QCoreApplication? QCoreApplication is used for non-GUI applications.

While I have not done it, I don't see where it is documented that it must be instantiated in your main() routine. Should be easy to test out, just create a QCoreApplication in your API initialization and exec() to start the event loop.

erichumenay
17th February 2015, 19:32
Yes, I just tried this minutes ago. Something is hanging in the API. On the bright side the QEventLoop error isnt being thrown, but it very well could be that the API is crapping out prior to getting to the point the error is thrown.

jefftee
17th February 2015, 19:43
What is your thread usage in your API? The thread you are running when you QCoreApplication::exec() will block of course. If it were me, I'd create a separate thread for listening to TCP/IP connections, when you receive a new request, use signals/slots to invoke your various API methods, etc.

Edit: Scratch my recommendation for separate thread. I forget that Qt's networking is asynchronous, so assuming you're not using native TCP/IP functions from your OS, you should be OK with a single thread design.

erichumenay
17th February 2015, 20:13
Actually, I made some headway by calling QCoreApplication::ProcessEvents rather than QCoreApplication::exec which totally make since because exec is blocking. It still isnt working 100% but I am hoping that if create a timer event and call ProcessEvents from it periodically that it may work.

jefftee
17th February 2015, 20:39
The fact that QCoreApplication::exec() is blocking is not a bad thing, it's what drives the Qt event driven architecture. The Qt networking functions should function OK since they are asynchronous and use signals/slots to interact with your application.

Is there a reason the exec() causes you an issue? The use of QTimer also requires a functioning event loop, no?

Could you redesign your API so that the QCoreApplication event loop is started and you scrap the need for QEventLoop altogether?

erichumenay
17th February 2015, 20:52
It isn't a networking API, but It is ok to think of it that way. Certain API functions (for example collect_data) will fire off a thread and then upon completion emit a signal that results in a callback function that is exposed through the API.

The exec causes an issue because if I call it after setting up slots in my API's connection function the function will never return


> Could you redesign your API so that the QCoreApplication event loop is started and you scrap the need for QEventLoop altogether?
It is possible that we could do this. The problem being that we have already underwent a round of testing, and I hate to introduce a dramatic change to the source. The API's compatibility with non-QT languages was never a requirement and therefore was never tested, and as a result of customer demand I am doing this little exercise to see how difficult it would be to get this implemented.

Oh btw, I just before you posted I realized that calling ProcessEvents from within QTimer wont work..... "smashes forehead off desk"

jefftee
17th February 2015, 22:00
All depends on your architecture. For example, is your API running its own process or you a dynamic/static library that is invoked by the caller's process?

If your API runs its own process, then QCoreApplication::exec() should not pose a problem as you would spend the bulk of your time listening for new API requests. If you are a library and part of the caller's process, you'll definitely need to create a separate thread when your API is initiated by the caller, so that you can create a new thread that does QCoreApplication::exec() and return to the caller, etc.

Since you already use callbacks, either design should work with Qt's asynchronous nature. The challenge is to get your event loop created and running while returning control to the caller after your API has initialized.

Good luck.

d_stranz
17th February 2015, 22:38
I started to reply to this early on (would have been the first reply, in fact), then bailed when I could not think of a way in DllMain() (or whatever the equivalent is here) to get the event loop started without hanging on the exec() call. After reading all this, I still can't think of a way.

Your approach of periodically calling processEvents() is probably the only way this is going to work. The timer should be on the C# side of things; when it fires, you call into the DLL to where the processEvents() call is actually made.

jefftee
17th February 2015, 23:00
I started to reply to this early on (would have been the first reply, in fact), then bailed when I could not think of a way in DllMain() (or whatever the equivalent is here) to get the event loop started without hanging on the exec() call. After reading all this, I still can't think of a way.


Couldn't he create a thread during his API initialization routine and have the thread create the QCoreApplication instance and exec() to get the event loop running? He could then return from the API initialization routine with the thread running in the background?

This would require that API callers adhere to an init()/cleanup() methodology so that the thread could be created or shutdown properly (as well as other initialization or cleanup that may need to be performed).

Is there a reason this approach would not work in your opinion?

Thanks

wysota
18th February 2015, 00:26
I started to reply to this early on (would have been the first reply, in fact), then bailed when I could not think of a way in DllMain() (or whatever the equivalent is here) to get the event loop started without hanging on the exec() call. After reading all this, I still can't think of a way.

The proper approach is to implement an event loop integration -- either process events of your other toolkit as part of Qt's event loop or process Qt events as part of the other framework's event loop. Qt already integrates quite well with glib's event loop, there is no reason one wouldn't integrate it with other event loops too.

d_stranz
18th February 2015, 05:07
Qt already integrates quite well with glib's event loop

Google is not being my friend tonight. Do you have a link to somewhere that discusses how this (or integration with any external event loop) is done (and not just someone blogging about having done it in less than 600 lines of code without sharing the details)?

wysota
18th February 2015, 09:38
First there is docs on QAbstractEventDispatcher which is a direct way to do the integration. Second there is or used to be the Qt-Mfc migration framework which I think integrated the two event loops. This there is Qt source code which has a number of event dispatchers implemented. Fourth a basic integration can be done by simply setting a timer in one of the frameworks and processing events of the other framework in the timeout function. I remember a couple of years ago I was using some CORBA framework with Qt and there was integration available for it that I think used the last approach. If I remember correctly it was a Qt Solution so there is a good chance its code is available somewhere on Qt's gitorious. But I think the glib one which Qt uses by default on Unix platforms if glib is available will give you the most information how to implement such integration.

erichumenay
20th February 2015, 00:07
I finally have gotten this working.

I had 2 separate problems both related to QCoreApplication not being present in the dll
1. The dll threads weren't being spawned
2. Signals from the threads back to the main thread were not occurring

Both 1 & 2 were occurring due to lack of an event loop in the main thread.

To fix one #1 followed the instruction here:
​http://stackoverflow.com/questions/2150488/using-a-qt-based-dll-in-a-non-qt-application

To fix #2 I had to change my signal/slot connections
Original code
connect(commMGR, SIGNAL(collectFin()), this, SLOT(ReportCollectFin()));

New and Working code
connect(commMGR, SIGNAL(collectFin()), this, SLOT(ReportCollectFin()), Qt::DirectConnection);
commMGR->moveToThread(QAppPriv::pThread );

where QAppPriv::pThread is the thread I created to solve problem #1.

The elegant thing about my solution is that the newly added code is isolated from the original code so there is no way that the new code could introduce a bug in the original code which would necessitate retesting the entire dll.

Thanks for helping me out

- All