Boost Thread Qt Application: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
 
(4 intermediate revisions by 3 users not shown)
Line 1: Line 1:
[[Category:HowTo]]
[[Category:HowTo]]
== Introduction ==


= How-to launch Qt Application in a non-main thread =
In this article I'll show how to manage running QApplication in a non-main thread. My approach is to create an external shared library, that can offer a GUI interface when requested.


In this article I'll show how to manage running QApplication in a non-main thread. My need is to create an external shared library,<br />that can offer a GUI interface when requested.
Anyway, I don't have control of the external process that loads my library, and I have to face the following two requirements:


Anyway, I don't have control on the external process that loads my library, and I have to face the following two requirements:
# loading of my library is performed in a thread
# calling of the public method that shows GUI interfaces happens in a different thread


a) loading of my library is performed in a thread;<br />b) calling of the public method that shows GUI interfaces happens in a different thread.
My solution requires the usage of <tt>boost::thread</tt> class (even if you can use any other thread-management technology, i.e. POSIX thread API, '''but not QThread class'''). Boost Thread lets me preserve "code once, run everywhere" requirement.


My solution requires the usage of boost::thread class (even if you can use any other thread-management technology, i.e. POSIX thread API,<br />'''but not QThread class'''). Boost Thread lets me preserve &quot;code once, run everywhere&amp;quot; requirement.
Some basic ideas were taken from VLC source code.


Some basic ideas were taken from VLC source code.
Here's just a sample of my solution, that has been extracted from my top-secret code: :)
 
'''application.hpp'''
 
<code>
#ifndef APPLICATION_HPP
#define APPLICATION_HPP


Here's just a sample of my solution, that has been extracted from my top-secret code :)
#include <boost/thread/thread.hpp>


application.hpp file:<br /><code><br />#ifndef APPLICATION_HPP<br />#define APPLICATION_HPP
#include <QObject>
#include <QApplication>
#include <QEvent>
#include <QWaitCondition>
#include <QMutex>


#include &lt;boost/thread/thread.hpp&amp;gt;
class Application;
class DialogProvider : public QObject
{
Q_OBJECT


#include &lt;QObject&amp;gt;<br />#include &lt;QApplication&amp;gt;<br />#include &lt;QEvent&amp;gt;<br />#include &lt;QWaitCondition&amp;gt;<br />#include &lt;QMutex&amp;gt;
Application *_qtApp;


class Application;<br />class DialogProvider : public QObject<br />{<br /> Q_OBJECT
QWaitCondition _waitCondition;
QMutex _mutex;
volatile bool _eventProcessed;


Application *_qtApp;
void signalEventProcessed() {
  QMutexLocker locker(&_mutex);
  _eventProcessed = true;
  _waitCondition.wakeAll();
  }


QWaitCondition _waitCondition;<br /> QMutex _mutex;<br /> volatile bool _eventProcessed;
protected:
  Application *qtApp() const { return _qtApp; }
  void customEvent(QEvent *event);


void signalEventProcessed()<br /> {<br /> QMutexLocker locker(&amp;_mutex);<br /> _eventProcessed = true;<br /> _waitCondition.wakeAll();<br /> }
public:
  explicit DialogProvider(Application *qtApp, QObject *parent = 0) :
  QObject(parent), _qtApp(qtApp), _eventProcessed(false) {}


protected:<br /> Application *qtApp() const { return _qtApp; }
  void waitForEventProcessed() {
  QMutexLocker locker(&_mutex);
  if (!_eventProcessed) _waitCondition.wait(&_mutex);
  _eventProcessed = false;
  }
};


void customEvent(QEvent *event);
class ShowMyWidgetEvent : public QEvent {
int _paramA;
float _paramB;


public:<br /> explicit DialogProvider(Application *qtApp, QObject '''parent = 0) :<br /> QObject(parent),<br /> _qtApp(qtApp),<br /> _eventProcessed(false) { }
public:
<br /> void waitForEventProcessed()<br /> {<br /> QMutexLocker locker(&amp;_mutex);<br /> if (!_eventProcessed)<br /> _waitCondition.wait(&amp;_mutex);<br /> _eventProcessed = false;<br /> }<br />};
  explicit ShowWaitDialogEvent(
<br />class ShowMyWidgetEvent : public QEvent<br />{<br /> int _paramA;<br /> float _paramB;
  QEvent::Type eventType, int paramA, float paramB) :
<br />public:<br /> explicit ShowWaitDialogEvent(QEvent::Type eventType,<br /> int paramA, float paramB) :<br /> QEvent(eventType),<br /> _paramA(paramA),<br /> _paramB(paramB) { }
  QEvent(eventType), _paramA(paramA), _paramB(paramB) {}
<br /> int paramA() const { return _paramA; }<br /> float paramB() const { return _paramB; }<br />};
<br />class Application : public QApplication<br />{<br /> DialogProvider'''_dpInstance;


static int staticArgc;<br /> static char staticDummy[];<br /> static char *staticArgv[2];
  int paramA() const { return _paramA; }
  float paramB() const { return _paramB; }
};


QEvent::Type _showMyWidgetEventType;
class Application : public QApplication
{
DialogProvider* _dpInstance;


protected:<br /> virtual DialogProvider *createDialogProvider(Application *app)<br /> { return new DialogProvider(app,this); }
static int staticArgc;
static char staticDummy[];
static char *staticArgv[2];


public:<br /> explicit Application();
QEvent::Type _showMyWidgetEventType;


DialogProvider *dpInstance();
protected:
  virtual DialogProvider *createDialogProvider(Application *app) {
  return new DialogProvider(app,this);
  }


QEvent::Type showMyWidgetEventType() const { return _showMyWidgetEventType; }
public:
  explicit Application();
  DialogProvider *dpInstance();
  QEvent::Type showMyWidgetEventType() const { return _showMyWidgetEventType; }
  QEvent* createShowMyWidgetEvent(int paramA, float paramB) const {
  return new ShowMyWidgetEvent(showMyWidgetEventType(),paramA,paramB);
  }
};


QEvent '''createShowMyWidgetEvent(int paramA, float paramB) const<br /> { return new ShowMyWidgetEvent(showMyWidgetEventType(),paramA,paramB); }<br />};
class ThreadedApplication
<br />class ThreadedApplication<br />{<br /> Application'''_app;<br /> volatile bool _started;<br /> boost::mutex _dialogMutex;<br /> boost::mutex _waitForApplicationRunningMutex;<br /> boost::condition_variable _applicationRunningCondition;
{
Application* _app;
volatile bool _started;
boost::mutex _dialogMutex;
boost::mutex _waitForApplicationRunningMutex;
boost::condition_variable _applicationRunningCondition;


protected:<br /> Application *qtApp() const { return _app; }
protected:
  Application *qtApp() const { return _app; }
  virtual Application *createApplication() { return new Application(); }
  boost::mutex &dialogMutex() { return _dialogMutex; }


virtual Application *createApplication() { return new Application(); }
public:
  explicit ThreadedApplication() : _app(nullptr), _started(false) {}
  virtual ~ThreadedApplication() { stop(); }


boost::mutex &amp;dialogMutex() { return _dialogMutex; }
void operator()();
{
  if (!_app) {
  boost::unique_lock< boost::mutex > guard(_waitForApplicationRunningMutex);
  _app = createApplication();
  _app->dpInstance();
  _started = true;
  _applicationRunningCondition.notify_all();
  }


public:<br /> explicit ThreadedApplication() :<br /> _app(nullptr),<br /> _started(false) { }
  _app->exec();
}


virtual ~ThreadedApplication() { stop(); }
void waitForApplicationRun() {
  boost::unique_lock< boost::mutex > guard(_waitForApplicationRunningMutex);
  if (!_started) _applicationRunningCondition.wait(guard);
}


void operator()();<br /> {<br /> if (!_app)<br /> {<br /> boost::unique_lock&amp;lt; boost::mutex &gt; guard(_waitForApplicationRunningMutex);
void stop() {
  if (_app) _app->quit();
}


_app = createApplication();<br /> _app-&gt;dpInstance();<br /> _started = true;<br /> _applicationRunningCondition.notify_all();<br /> }
void showMyWidget(int paramA, float paramB) {
  if (_app) {
  _app->postEvent(_app->dpInstance(),_app->createShowMyWidgetEvent(paramA,paramB));
  _app->dpInstance()->waitForEventProcessed();
  }
}
};


_app-&gt;exec&amp;amp;#40;&amp;#41;;<br /> }
#endif
</code>


void waitForApplicationRun()<br /> {<br /> boost::unique_lock&amp;lt; boost::mutex &gt; guard(_waitForApplicationRunningMutex);<br /> if (!_started)<br /> _applicationRunningCondition.wait(guard);<br /> }
'''application.cpp'''


void stop()<br /> {<br /> if (_app)<br /> _app-&gt;quit();<br /> }
<code>
#include <boost/thread/locks.hpp>
#include "application.hpp"


void showMyWidget(int paramA, float paramB)<br /> {<br /> if (_app)<br /> {<br /> _app-&gt;postEvent(_app-&gt;dpInstance(),_app-&gt;createShowMyWidgetEvent(paramA,paramB));<br /> _app-&gt;dpInstance()<s>&gt;waitForEventProcessed();<br /> }<br /> }<br />};
int Application::staticArgc = 1;
<br />#endif<br /></code>
char Application::staticDummy[] = "QtLibApplication";
<br />application.cpp file:
char *Application::staticArgv[2] = { staticDummy, nullptr };
<br /><code><br />#include &lt;boost/thread/locks.hpp&amp;gt;
<br />#include &quot;application.hpp&amp;quot;
<br />int Application::staticArgc = 1;<br />char Application::staticDummy[] = &quot;QtLibApplication&amp;quot;;<br />char *Application::staticArgv[2] = { staticDummy, nullptr };
<br />void DialogProvider::customEvent(QEvent *event)<br />{<br /> if (!_qtApp)<br /> return;
<br /> if (event</s>&gt;type() == _qtApp-&gt;showMyWidgetEventType())<br /> {<br /> ShowMyWidgetEvent '''ev = static_cast&amp;lt; ShowMyWidgetEvent''' &gt;(event);<br /> MyWidget *myWidget = new MyWidget(event-&gt;paramA(),event-&gt;paramB());<br /> myWidget-&gt;setAttribute(Qt::WA_DeleteOnClose);<br /> myWidget-&gt;show();<br /> }


QObject::customEvent(event);<br /> signalEventProcessed();<br />}
void DialogProvider::customEvent(QEvent *event)
{
if (!_qtApp) return;
if (event->type() == _qtApp->showMyWidgetEventType()) {
  ShowMyWidgetEvent *ev = static_cast< ShowMyWidgetEvent* >(event);
  MyWidget *myWidget = new MyWidget(event->paramA(),event->paramB());
  myWidget->setAttribute(Qt::WA_DeleteOnClose);
  myWidget->show();
}


Application::Application() :<br /> QApplication(staticArgc,staticArgv),<br /> _dpInstance(nullptr),<br /> _showMyWidgetEventType(static_cast&amp;lt; QEvent::Type &gt;(QEvent::registerEventType()))<br />{<br /> setQuitOnLastWindowClosed(false);<br />}
QObject::customEvent(event);
signalEventProcessed();
}


DialogProvider *Application::dpInstance()<br />{<br /> if (!_dpInstance)<br /> _dpInstance = createDialogProvider(this);
Application::Application() :
QApplication(staticArgc,staticArgv),
_dpInstance(nullptr),
_showMyWidgetEventType(static_cast< QEvent::Type >(QEvent::registerEventType()))
{
setQuitOnLastWindowClosed(false);
}


return _dpInstance;<br />}<br /></code>
DialogProvider *Application::dpInstance()
{
if (!_dpInstance) _dpInstance = createDialogProvider(this);
return _dpInstance;
}
</code>


My goal is to create my widget in the same thread of QApplication, as Qt requires, wait until the widget is shown, and let my widget run in the QApplication event loop context.
My goal is to create my widget in the same thread of QApplication, as Qt requires, wait until the widget is shown, and let my widget run in the QApplication event loop context.


When one of my shared library public methods is called for the first time,<br />I create an instance of the runnable ThreadedApplication class, and I fire it using boost::thread:
When one of my shared library public methods is called for the first time,
 
I create an instance of the runnable ThreadedApplication class, and I fire it using boost::thread:
<code><br />ThreadedApplication *app = new ThreadedApplication();<br />boost::thread appThread(boost::ref(*app));<br />app-&gt;waitForApplicationRun();<br /></code>


So, let's call &quot;Thread A&amp;quot; the thread that executes the public method of my library, that performs the three lines above.<br />A new thread is created (the &quot;boost&amp;quot; one), called &quot;Thread B&amp;quot;, that creates an instance of QApplication, my DialogProvider, and executes<br />event loop of QApplication, everything in the same context.<br />&quot;Thread A&amp;quot; is blocked until I'm sure that my application is started, then returns.
<code>
ThreadedApplication *app = new ThreadedApplication();
boost::thread appThread(boost::ref(*app));
app->waitForApplicationRun();
</code>


Some time later, a new thread, called &quot;Thread C&amp;quot;, wants to call a different public method that shows my widget.<br />I just need to call
So, let's call "Thread A" the thread that executes the public method of my library, that performs the three lines above.
A new thread is created (the "boost" one), called "Thread B", that creates an instance of QApplication, my DialogProvider, and executes
event loop of QApplication, everything in the same context.
"Thread A" is blocked until I'm sure that my application is started, then returns.


<code><br />app-&gt;showMyWidget(5,3.0);<br /></code>
Some time later, a new thread, called "Thread C", wants to call a different public method that shows my widget.
I just need to call


Of course, the two parameters are specified as example. &quot;Thread C&amp;quot; posts a custom event (that carries on all information I need to create my widget)<br />to QApplication event loop, that forwards it to my DialogProvider customEvent method.<br />In that method (I'm in QApplication context, i.e. &quot;Thread B&amp;quot;), I can create my widget and show it[[Image:|Image:]] In the end, I just signal (using a QWaitCondition)<br />that my widget is shown, so &quot;Thread C&amp;quot; can return.
<code>
app->showMyWidget(5,3.0);
</code>


The final result is that all my Qt classes have been created in the QApplication thread context, just as a normal Qt application. Qt world is unaware of what<br />has happened, and it never complains about anything.
Of course, the two parameters are specified as example. "Thread C" posts a custom event (that carries on all information I need to create my widget) to QApplication event loop, that forwards it to my DialogProvider customEvent method. In that method (I'm in QApplication context, i.e. "Thread B"), I can create my widget and show it in the end, I just signal (using a {{DocLink|QWaitCondition}}) that my widget is shown, so "Thread C" can return.


Just a warning about widgets creation and destruction in customEvent method. '''NEVER''' delete them there[[Image:|Image:]]! QDialog based widgets can let you do wrong, cause usually you destroy the dialog just after executing them. Please use WA_DeleteOnClose attribute, as in my example, or defer their deletion using deleteLater method.
The final result is that all my Qt classes have been created in the QApplication thread context, just as a normal Qt application. Qt world is unaware of what has happened, and it never complains about anything.


Comments on how to improve such solution are welcome[[Image:|Image:]]!
Just a warning about widgets creation and destruction in customEvent method. '''NEVER''' delete them there! QDialog based widgets can let you do wrong, cause usually you destroy the dialog just after executing them. Please use <tt>WA_DeleteOnClose</tt> attribute, as in my example, or defer their deletion using the deleteLater method.

Latest revision as of 20:59, 4 March 2015

Introduction

In this article I'll show how to manage running QApplication in a non-main thread. My approach is to create an external shared library, that can offer a GUI interface when requested.

Anyway, I don't have control of the external process that loads my library, and I have to face the following two requirements:

  1. loading of my library is performed in a thread
  2. calling of the public method that shows GUI interfaces happens in a different thread

My solution requires the usage of boost::thread class (even if you can use any other thread-management technology, i.e. POSIX thread API, but not QThread class). Boost Thread lets me preserve "code once, run everywhere" requirement.

Some basic ideas were taken from VLC source code.

Here's just a sample of my solution, that has been extracted from my top-secret code: :)

application.hpp

#ifndef APPLICATION_HPP
#define APPLICATION_HPP

#include <boost/thread/thread.hpp>

#include <QObject>
#include <QApplication>
#include <QEvent>
#include <QWaitCondition>
#include <QMutex>

class Application;
class DialogProvider : public QObject
{
 Q_OBJECT

 Application *_qtApp;

 QWaitCondition _waitCondition;
 QMutex _mutex;
 volatile bool _eventProcessed;

 void signalEventProcessed() {
  QMutexLocker locker(&_mutex);
  _eventProcessed = true;
  _waitCondition.wakeAll();
  }

 protected:
  Application *qtApp() const { return _qtApp; }
  void customEvent(QEvent *event);

 public:
  explicit DialogProvider(Application *qtApp, QObject *parent = 0) :
   QObject(parent), _qtApp(qtApp), _eventProcessed(false) {}

  void waitForEventProcessed() {
   QMutexLocker locker(&_mutex);
   if (!_eventProcessed) _waitCondition.wait(&_mutex);
   _eventProcessed = false;
  }
};

class ShowMyWidgetEvent : public QEvent {
 int _paramA;
 float _paramB;

 public:
  explicit ShowWaitDialogEvent(
   QEvent::Type eventType, int paramA, float paramB) :
   QEvent(eventType), _paramA(paramA), _paramB(paramB) {}

  int paramA() const { return _paramA; }
  float paramB() const { return _paramB; }
};

class Application : public QApplication
{
 DialogProvider* _dpInstance;

 static int staticArgc;
 static char staticDummy[];
 static char *staticArgv[2];

 QEvent::Type _showMyWidgetEventType;

 protected:
  virtual DialogProvider *createDialogProvider(Application *app) {
   return new DialogProvider(app,this);
  }

 public:
  explicit Application();
  DialogProvider *dpInstance();
  QEvent::Type showMyWidgetEventType() const { return _showMyWidgetEventType; }
  QEvent* createShowMyWidgetEvent(int paramA, float paramB) const {
   return new ShowMyWidgetEvent(showMyWidgetEventType(),paramA,paramB);
  }
};

class ThreadedApplication
{
 Application* _app;
 volatile bool _started;
 boost::mutex _dialogMutex;
 boost::mutex _waitForApplicationRunningMutex;
 boost::condition_variable _applicationRunningCondition;

 protected:
  Application *qtApp() const { return _app; }
  virtual Application *createApplication() { return new Application(); }
  boost::mutex &dialogMutex() { return _dialogMutex; }

 public:
  explicit ThreadedApplication() : _app(nullptr), _started(false) {}
  virtual ~ThreadedApplication() { stop(); }

 void operator()();
 {
  if (!_app) {
   boost::unique_lock< boost::mutex > guard(_waitForApplicationRunningMutex);
   _app = createApplication();
   _app->dpInstance();
   _started = true;
   _applicationRunningCondition.notify_all();
  }

  _app->exec();
 }

 void waitForApplicationRun() {
  boost::unique_lock< boost::mutex > guard(_waitForApplicationRunningMutex);
  if (!_started) _applicationRunningCondition.wait(guard);
 }

 void stop() {
  if (_app) _app->quit();
 }

 void showMyWidget(int paramA, float paramB) {
  if (_app) {
   _app->postEvent(_app->dpInstance(),_app->createShowMyWidgetEvent(paramA,paramB));
   _app->dpInstance()->waitForEventProcessed();
  }
 }
};

#endif

application.cpp

#include <boost/thread/locks.hpp>
#include "application.hpp"

int Application::staticArgc = 1;
char Application::staticDummy[] = "QtLibApplication";
char *Application::staticArgv[2] = { staticDummy, nullptr };

void DialogProvider::customEvent(QEvent *event)
{
 if (!_qtApp) return;
 if (event->type() == _qtApp->showMyWidgetEventType()) {
  ShowMyWidgetEvent *ev = static_cast< ShowMyWidgetEvent* >(event);
  MyWidget *myWidget = new MyWidget(event->paramA(),event->paramB());
  myWidget->setAttribute(Qt::WA_DeleteOnClose);
  myWidget->show();
 }

 QObject::customEvent(event);
 signalEventProcessed();
}

Application::Application() :
 QApplication(staticArgc,staticArgv),
 _dpInstance(nullptr),
 _showMyWidgetEventType(static_cast< QEvent::Type >(QEvent::registerEventType()))
{
 setQuitOnLastWindowClosed(false);
}

DialogProvider *Application::dpInstance()
{
 if (!_dpInstance) _dpInstance = createDialogProvider(this);
 return _dpInstance;
}

My goal is to create my widget in the same thread of QApplication, as Qt requires, wait until the widget is shown, and let my widget run in the QApplication event loop context.

When one of my shared library public methods is called for the first time, I create an instance of the runnable ThreadedApplication class, and I fire it using boost::thread:

ThreadedApplication *app = new ThreadedApplication();
boost::thread appThread(boost::ref(*app));
app->waitForApplicationRun();

So, let's call "Thread A" the thread that executes the public method of my library, that performs the three lines above. A new thread is created (the "boost" one), called "Thread B", that creates an instance of QApplication, my DialogProvider, and executes event loop of QApplication, everything in the same context. "Thread A" is blocked until I'm sure that my application is started, then returns.

Some time later, a new thread, called "Thread C", wants to call a different public method that shows my widget. I just need to call

app->showMyWidget(5,3.0);

Of course, the two parameters are specified as example. "Thread C" posts a custom event (that carries on all information I need to create my widget) to QApplication event loop, that forwards it to my DialogProvider customEvent method. In that method (I'm in QApplication context, i.e. "Thread B"), I can create my widget and show it in the end, I just signal (using a QWaitCondition) that my widget is shown, so "Thread C" can return.

The final result is that all my Qt classes have been created in the QApplication thread context, just as a normal Qt application. Qt world is unaware of what has happened, and it never complains about anything.

Just a warning about widgets creation and destruction in customEvent method. NEVER delete them there! QDialog based widgets can let you do wrong, cause usually you destroy the dialog just after executing them. Please use WA_DeleteOnClose attribute, as in my example, or defer their deletion using the deleteLater method.