Yes, I know that you cannot use GUI things from non-GUI threads. However, it seems reasonable to be able to create a QWidget object, send it to the GUI thread, and then send signals to it. However, when I try to do so, I get errors that widgets cannot be moved. However, this seems to works:
#include <iostream>
#include <QApplication>
#include <QtConcurrentRun>
#include <QDialog>
class BasicViewer : public QDialog
{
Q_OBJECT
public:
void Function(const float a)
{
std::cout << a << std::endl;
}
};
struct BasicViewerWrapper : public QObject
{
Q_OBJECT
public:
BasicViewer WrappedBasicViewer;
void Function(const float a)
{
WrappedBasicViewer.Function(a);
}
};
#include "main.moc" // For CMake's automoc
void Function2()
{
BasicViewerWrapper basicViewerWrapper;
basicViewerWrapper.moveToThread(QCoreApplication::instance()->thread());
basicViewerWrapper.Function(2.0f);
}
void Function1()
{
Function2();
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QtConcurrent::run(Function1);
std::cout << "End" << std::endl;
return app.exec();
}
I have created a wrapper class with the same API as the QWidget that stores an instance of the QWidget I wanted to create directly. I AM allowed to create that wrapper, move it to the GUI thread, and then use it. My question is, is there a way to do this without having to write this wrapper? It seems quite tedious, and since the concept works, I don't understand why it cannot be done directly. Any thoughts?
----------- EDIT ---------------
The first example was a bad one, because it did not attempt to do anything with GUI elements. This example indeed generates "Cannot create children for a parent that is in a different thread."
#include <iostream>
#include <QApplication>
#include <QtConcurrentRun>
#include <QMessageBox>
class BasicViewer : public QMessageBox
{
Q_OBJECT
public:
};
struct BasicViewerWrapper : public QObject
{
Q_OBJECT
public:
BasicViewer WrappedBasicViewer;
void exec()
{
WrappedBasicViewer.exec();
}
};
#include "main.moc" // For CMake's automoc
void Function2()
{
BasicViewerWrapper basicViewerWrapper;
basicViewerWrapper.moveToThread(QCoreApplication::instance()->thread());
basicViewerWrapper.exec();
}
void Function1()
{
Function2();
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QtConcurrent::run(Function1);
return app.exec();
}
----------- EDIT 2 ----------------
I thought this would work, since the member object gets created after the thread of the Wrapper has been moved:
#include <iostream>
#include <QApplication>
#include <QtConcurrentRun>
#include <QMessageBox>
class BasicViewer : public QMessageBox
{
Q_OBJECT
public:
};
struct BasicViewerWrapper : public QObject
{
Q_OBJECT
public:
BasicViewer* WrappedBasicViewer;
void exec()
{
WrappedBasicViewer->exec();
}
void create()
{
WrappedBasicViewer = new BasicViewer;
}
};
#include "main.moc" // For CMake's automoc
void Function2()
{
BasicViewerWrapper basicViewerWrapper;
basicViewerWrapper.moveToThread(QCoreApplication::instance()->thread());
basicViewerWrapper.create();
basicViewerWrapper.exec();
}
void Function1()
{
Function2();
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QtConcurrent::run(Function1);
return app.exec();
}
Unfortunately, it does not. Can anyone explain why?
--------------- EDIT 3 --------------------
I'm unsure why this works? It uses a signal to trigger the GUI component, but isn't the GUI object (the QDialog) still created in the non-GUI thread?
#include <iostream>
#include <QApplication>
#include <QtConcurrentRun>
#include <QMessageBox>
class DialogHandler : public QObject
{
Q_OBJECT
signals:
void MySignal(int* returnValue);
public:
DialogHandler()
{
connect( this, SIGNAL( MySignal(int*) ), this, SLOT(MySlot(int*)), Qt::BlockingQueuedConnection );
}
void EmitSignal(int* returnValue)
{
emit MySignal(returnValue);
}
public slots:
void MySlot(int* returnValue)
{
std::cout << "input: " << *returnValue << std::endl;
QMessageBox* dialog = new QMessageBox;
dialog->addButton(QMessageBox::Yes);
dialog->addButton(QMessageBox::No);
dialog->setText("Test Text");
dialog->exec();
int result = dialog->result();
if(result == QMessageBox::Yes)
{
*returnValue = 1;
}
else
{
*returnValue = 0;
}
delete dialog;
}
};
#include "main.moc" // For CMake's automoc
void MyFunction()
{
DialogHandler* dialogHandler = new DialogHandler;
dialogHandler->moveToThread(QCoreApplication::instance()->thread());
int returnValue = -1;
dialogHandler->EmitSignal(&returnValue);
std::cout << "returnValue: " << returnValue << std::endl;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QtConcurrent::run(MyFunction);
std::cout << "End" << std::endl;
return app.exec();
}