PDA

View Full Version : Using a qt dependent library in a Qt application



talha
12th December 2012, 17:34
Hi all,

I am creating an application which uses Qt for the GUI. Inside this application, I am trying to launch a 3rd party library (OpenRAVE) which also uses Qt for its GUI. I am initializing OpenRAVE from a button press in my application. However, this is causing errors, namely:

QPixmap: It is not safe to use pixmaps outside the GUI thread
QApplication::exec: Must be called from the main thread

I gather from this that I can't call OpenRAVE from inside my Qt application. It must be called from the main method of my program, right? The problem is, I'm not sure how to approach this. The functionality I am looking for is that OpenRAVE should only launch at the request of the user.

I'll be grateful if anyone could help or point me in the right direction.

Thanks

Talha

anda_skoa
12th December 2012, 18:57
It seems that some code that needs to run in the main thread is being executed in a worker thread.

How does the slot of your button look like, the code that initializes the library?

Cheers,
_

talha
12th December 2012, 19:04
Yes it is being run on a worker thread. My problem is I'm not sure how to make it run on the main thread whilst also allowing the user to dictate when it is run. Here is the code for initializing the library:



// Function for running the GUI of the library
void setViewer(EnvironmentBasePtr penv, const std::string& viewername)
{
ViewerBasePtr viewer = RaveCreateViewer(penv,viewername);
BOOST_ASSERT(!!viewer);
// attach it to the environment:
penv->Add(viewer);
// finally call the viewer's infinite loop (this is why a separate thread is needed)
bool showgui = true;
viewer->main(showgui);
}

//Helper function for initializing the library
void oRave()
{
//int num = 1;
std::string scenefilename = "C:/Program Files (x86)/OpenRAVE/share/openrave-0.8/data/lab1.env.xml";
std::string viewername = "qtcoin";

RaveInitialize(true); // start openrave core
EnvironmentBasePtr penv = RaveCreateEnvironment(); // create the main environment
RaveSetDebugLevel(5);
boost::thread thviewer(boost::bind(setViewer,penv,viewername));
penv->Load(scenefilename); // load the scene
thviewer.join(); // wait for the viewer thread to exit
penv->Destroy(); // destroy
}

// THIS IS THE SLOT
void
MainWindow::loadMeshFile(){

oRave();
}

amleto
12th December 2012, 20:37
You can't have two independent Qt GUIs because either they are on the same thread and will block each other, or they are on different threads and Qt will assert.

talha
12th December 2012, 20:42
So are you saying that it is impossible to have 2 Qt GUIs running at the same time? Because I can get the library to run using QProcess::Start(). It's just that I don't have enough control over the library's functionality using this method. Also, what do you mean by "Qt will assert"? I'm not familiar with this terminology.

d_stranz
12th December 2012, 22:49
"Qt will assert" means that your application (when running in debug mode) will halt with an error (the assert) because Qt won't allow two GUI threads to run simultaneously. In release mode, the program will probably simply crash.

I don't know anything about OpenRave. Is is a library? In that case, it won't have a GUI thread, because libraries depend on the client program to supply that. Your problem is probably that you are trying to use the library from within a thread, and the Qt GUI can run only on the GUI thread (which your main() and its QApplication::exec() are providing).

Find a way to connect to the OpenRave GUI from within your application's GUI thread, not outside it.



// finally call the viewer's infinite loop (this is why a separate thread is needed)
bool showgui = true;
viewer->main(showgui);


You are probably mistaken about this (the thread part), if OpenRave is a library and not an application (executable).

talha
13th December 2012, 10:32
// finally call the viewer's infinite loop (this is why a separate thread is needed)
bool showgui = true;
viewer->main(showgui);


You are probably mistaken about this (the thread part), if OpenRave is a library and not an application (executable).

OpenRave is a full fledged application with its own Qt GUI but it can also be used as a library inside an application. The code that I presented above is taken from one of the examples in OpenRave. The only difference is that I am using a Qt GUI for my own application as well.

Is it possible to add a Qt application as a widget inside my application?

anda_skoa
15th December 2012, 17:14
The way this looks is that viewer->main() runs the Qt event loop.

Since the rest of Qt doesn't care how the event loop got started, you could just use that call instead of the usual app.exec().

Given that this function also gets the commandline arguments, it is potentially also creating the QApplication object.

There are two options:
1) check if the library has an option not to create the application object. In this case you can create it yourself and do the application initialization as usual.
2) if the library's design is really broken and there is no way around it creating the QApplication object, it will have to do that and you put your initialization code in a slot that is run after the event loop has started up.

Cheers,
_

talha
17th December 2012, 21:00
2) if the library's design is really broken and there is no way around it creating the QApplication object, it will have to do that and you put your initialization code in a slot that is run after the event loop has started up.
_

Can you elaborate on this option? I'm now letting the library create the QApplication object and start its GUI in the main thread. I am then initializing my program in a secondary thread, otherwise it won't respond, without a QApplication object. Now I am just calling for my MainWindow to be shown. However, this is causing the program to crash. I don't understand how I should put it in a slot as you mentioned. And what comprises the event loop?


Thanks

anda_skoa
19th December 2012, 13:46
Lets assume your main function looks like this


int main( int argc, char ** argv)
{
RaveInitialize(true); // start openrave core
EnvironmentBasePtr penv = RaveCreateEnvironment(); // create the main environment
RaveSetDebugLevel(5);
penv->Load(scenefilename); // load the scene

ViewerBasePtr viewer = RaveCreateViewer(penv,viewername);
BOOST_ASSERT(!!viewer);
// attach it to the environment:
penv->Add(viewer);

bool showgui = true;
viewer->main(showgui);

penv->Destroy(); // destroy
}


So what you need is a slot that can be called in a delayed fashion, i.e. once the event loop is runnning



class AppInitializer : public QObject
{
Q_OBJECT

public:
explicit AppInitializer( QObject *parent = 0 ) : QObject( parent ) {}

public Q_SLOTS:
void initialize();
};


Inside initialize() you create your main window and show it.

In the main function you add this


// ....
penv->Add(viewer);

// create app initializer and schedule delayed initialization
AppInitializer appInit;
QMetaObject::invokeMethod( &appInit, "initialize", Qt::QueuedConnection );

bool showgui = true;
viewer->main(showgui);
// ....


Cheers,
_