PDA

View Full Version : Create QMainWindow from different thread



Viatorus
20th February 2015, 19:41
Hey community,

I try to create something like a window pool. You can use these windows everywhere in your program to display graphics and plot diagrams etc. The widgets working well but the main problem at the moment is the frustrated tries, to create the pool. One non QObject-Object should represent a QMainWindow to cut the bindings to Qt.

I cannot create a widget -> I tried invokeMethode, connect and QTimer but nothing works. Sometimes the methods don´t get called or I am not in the gui thread... Any idea?

header:


#pragma once
#include <QMainWindow>

class MyWindow : QObject
{
Q_OBJECT
public:
MyWindow();
signals:
void start();
private:
QWindow *mWin;
};

class QWindowPool : public QObject
{
Q_OBJECT
public:
QWindowPool();
public slots:
void createWindow();
};

class QWindow : public QMainWindow
{
Q_OBJECT
};


#include <utils/MatWindow.h>

#include <QApplication>
#include <QTimer>

#include <thread>

QWindowPool pool;

QWindowPool::QWindowPool()
{
int c = 0;
new QApplication(c, NULL);
}

void QWindowPool::createWindow()
{
printf("window created\n");
new QWindow();
}

MyWindow::MyWindow()
{
connect(this, SIGNAL(start()), &pool, SLOT(createWindow()));
QTimer::singleShot(0, &pool, SLOT(createWindow()));
QMetaObject::invokeMethod(&pool, "createWindow", Qt::BlockingQueuedConnection);
emit start();
}


int main()
{
std::thread t1([](){
MyWindow mw;
qApp->processEvents(QEventLoop::AllEvents); // <- not working
});
// qApp->processEvents(QEventLoop::AllEvents); // work
t1.join();

return 0;
}

So, it does not work (createWindow does not get called) unless I uncomment the last processEvents in the main thread but that should not be the solution.

What I want is in the final application: The user should get the possibility to write anywhere in his code and in any thread:

MyWindow mw(dataToDisplay)
and the window should be created and showed to him. Currently it only works if he would call:

qApp->processEvents(QEventLoop::AllEvents);
in the main thread. But I want do do this for him.... HOW?

Thank you.

BTW: I "cross posted" this question. First I asked at stackoverflow but didn´t get a right solution. http://stackoverflow.com/questions/28602040/create-qmainwindow-from-different-thread

wysota
20th February 2015, 20:09
If you are not in the gui thread then in what thread are you? Widgets can only be accessed from the gui thread. If you want your methods to be callable from non-gui thread then you need to cross thread boundaries inside your calls (e.g. using signals and slots).

Viatorus
20th February 2015, 20:30
Hello wysota,

Thank you for your answer.

My english must be really bad... sorry about that. But why do all people say the same: I know it!

I KNOW: To call between non-gui threads and gui thread I have to use singal and slot.
On line 24 to 27 what do you think I supposed to do? I use signal/slot principle.

But this is not working without a call in the main thread... line 37

jefftee
20th February 2015, 21:57
But this is not working without a call in the main thread... line 37
Where are you creating an event loop and starting it by calling exec()? My understanding is that qApp->processEventLoop isn't the same as as running the event loop with exec().

I don't believe you can have a true event loop without creating one of QCoreApplication or QApplication. Others know much better than me I am sure, so hopefully they can chime in and point you in the right direction.

Good luck.

Viatorus
20th February 2015, 22:15
Hey JThomps,

You are right, I hoped to get it working without exec...
The main problem for me at "qApp->exec()" is, it blocks the current thread. So when I would call it just before main get called, I would block the main thread. That is not an solution.
My current versions looks like so:

hpp:


#pragma once
#include <QMainWindow>
#include <QTimer>

class MyWindow : QObject
{
Q_OBJECT
public:
MyWindow();
};

class QWindowPool : public QObject
{
Q_OBJECT
public:
QWindowPool();
public slots:
void createWindow();
};

class QWindow : public QMainWindow
{
Q_OBJECT
};


cpp:


#include <utils/MatWindow.h>

#include <QApplication>
#include <QTimer>
#include <QtConcurrent/qtconcurrentrun.h>

#include <iostream>
#include <future>

static QWindowPool *pool = new QWindowPool();

QWindowPool::QWindowPool() {

// check if app is running
if (!QApplication::instance()) {
bool appOnline = false;
QtConcurrent::run([&appOnline](){
int c = 0;
new QApplication(c, NULL);
appOnline = true;
qApp->exec();
});
while (!appOnline) {}
}
moveToThread(QApplication::instance()->thread());
}

void QWindowPool::createWindow() {
printf("window created\n");
new QWindow();
}

MyWindow::MyWindow() {
QTimer::singleShot(0, pool, SLOT(createWindow()));
}

int main()
{
MyWindow mw;
std::thread t1([](){
MyWindow mw;

std::thread t2([](){
MyWindow mw;
});

t2.join();
});
t1.join();

std::cin.ignore();
return 0;
}


Now the code do what is should. I can create widgets in different threads. But there are two scenarios, where I would stack at:

Someone (anyone who would like to use this library) creates before me QApplication and never calls qApp->exec
Someone want to create an own UI with Widget but does not work in my qt::concurrent gui thread. He would probably not get along with this.

wysota
20th February 2015, 22:48
I KNOW: To call between non-gui threads and gui thread I have to use singal and slot.
On line 24 to 27 what do you think I supposed to do? I use signal/slot principle.
Line #26 won't work, as far as I remember.


But this is not working without a call in the main thread... line 37

It won't because for cross-thread signal-slot connections to work the receiving thread (in your case the GUI thread) has to be running an event loop.

In general for Qt widgets to work you will need the main event loop running, so trying to work around having the main event loop is not really an option.

jefftee
21st February 2015, 00:05
In general for Qt widgets to work you will need the main event loop running, so trying to work around having the main event loop is not really an option.
Wysota, this is at least the 2nd time a similar thing has come up in the last week. Both cases the person is developing a library or API to be used by non-Qt applications and/or Qt applications and how to create/run an event loop seems to be the central problem.

The reason they don't create an event loop when their API/library is called is because they then can't return to the caller if they create and exec an event loop on the same thread used to call into the API/library.

Would it be possible to create a thread on the first library call that creates a QCoreApplication and exec()'s to enter an event loop in the newly created thread? The API/library method can return to the caller after creating and starting the thread and the thread would remain running with the QCoreApplication::exec() event loop.

Queued signals/slots could then be used to communicate between the event loop thread and any future API/library calls made.

Could this approach be used or am I missing something that would make this approach either undesirable or impossible?

Thanks

ChrisW67
21st February 2015, 00:35
Take a look at ActiveQt which, for ActiveX controls on Windows, manages creating a QApplication (if one does not already exist) hooked into the non-Qt event loop.

https://qt.gitorious.org/qt/qtactiveqt/source/a736ef5bab4ed044af3b24edeff0d9178a139148:src/activeqt/control/qaxserverbase.cpp

jefftee
21st February 2015, 02:28
Thanks ChrisW. I'll go through that code tonight in more detail. A quick read looks like it creates a QApplication if qApp is null and then does a qApp->processEvents(), but a quick read didn't show that it does an exec().

It also has the luxury or burden (depending how you look at it) that it can be in-process or out-of-process, so my guess it handles those cases differently.

Thanks for the pointer though.