Dav*_*ria 10 qt multithreading qt4
是的,我知道你不能从非GUI线程中使用GUI.但是,能够创建QWidget对象,将其发送到GUI线程,然后向其发送信号似乎是合理的.但是,当我尝试这样做时,我会收到无法移动小部件的错误.但是,这似乎有效:
#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();
}
Run Code Online (Sandbox Code Playgroud)
我创建了一个包装类,其包含与QWidget相同的API,它存储了我想直接创建的QWidget实例.我允许创建该包装器,将其移动到GUI线程,然后使用它.我的问题是,有没有办法这样做而不必编写这个包装器?这似乎很乏味,而且由于这个概念有效,我不明白为什么它不能直接完成.有什么想法吗?
-----------编辑---------------
第一个例子是坏的,因为它没有尝试用GUI元素做任何事情.此示例确实生成"无法为处于不同线程中的父级创建子级".
#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();
}
Run Code Online (Sandbox Code Playgroud)
-----------编辑2 ----------------
我认为这样可行,因为在移动Wrapper的线程之后会创建成员对象:
#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();
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,事实并非如此.有谁能解释为什么?
---------------编辑3 --------------------
我不确定为什么会这样?它使用一个信号来触发GUI组件,但是不是仍然在非GUI线程中创建的GUI对象(QDialog)?
#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();
}
Run Code Online (Sandbox Code Playgroud)
Qt坚持在GUI线程中创建小部件.它禁止将小部件移动到不同的线程,以防止它们存在于GUI线程之外.事实上,上面的例子并没有将其BasicViewer
转移到另一个线程; 它只会移动BasicViewerWrapper
到另一个线程.你可以看到这一点,如果您在打印出来的指针,包含线程BasicViewerWrapper::Function
和BasicViewer::Function
:
std::cout << std::hex << thread() << std::endl;
Run Code Online (Sandbox Code Playgroud)
如果您真的希望从GUI线程外部触发创建窗口小部件,则建议其他线程通知GUI线程创建所需的窗口小部件.您可以从连接到创建窗口小部件的GUI线程中的插槽的非GUI线程发出信号,也可以在GUI线程中调用函数来为您创建窗口小部件QMetaObject::invokeMethod
.
编辑
不幸的是,除了QMetaObject::invokeMethod
尝试在a之外执行调用之外,没有办法在不同的线程中调用方法QObject
.在过去,我试图通过将方法调用放在单独的类或函数中来解决可读性问题,但诚然,它并不完美.
您的第3个示例无效,因为QObject::moveToThread
它不同步.在将对象实际移动到目标线程之前,控件必须返回到目标线程的事件循环.因此,您可能需要将sleep语句和调用QCoreApplication::processEvents
后的调用组合在一起moveToThread
.在这些电话之后,你应该打电话basicViewerWrapper::create()
和basicViewerWrapper::exec()
通过QMetaObject::invokeMethod
.