从QT GUI应用程序中插槽的线程上下文(按按钮时),我试图启动工作线程以使用CPU密集型计算的结果更新GUI的其他部分-这些结果将更新表格或类似Google的地图小部件-因此,这需要在安全地更新这些小部件的主QT应用程序线程中进行。
我遇到的问题是,除非将连接类型更改为updateGUIWidget
,否则Qt::DirectConnection
插槽永远不会被调用-在这种情况下,它会在辅助线程中被调用(在这种情况下,不安全地更新GUI)。我检查了每个连接调用的结果,它们都很好,看来某个地方的事件循环存在一些问题。我不确定是否需要将线程和辅助对象分配为主窗口的成员,或者是否可以从插槽中的堆栈变量中分配它。
void
mainwindow::on_importSimulatedFlight_clicked()
{
// experimental worker thread model adapted from YouTube tutorial
// by Giuseppe di Angelo https://www.youtube.com/watch?v=BgqT6SIeRn4
// auto thread = new QThread;
// note worker created in gui thread here - we will move its thread
// affinity to 'thread' below before starting it.
auto thread = new QThread;
auto worker = new Worker;
connect(thread, &QThread::started, worker, &Worker::doWork);
// connect(worker, &Worker::progressUpdate, this, &mainwindow::updateGUIWidget, Qt::DirectConnection);
connect(worker, &Worker::progressUpdate, this, &mainwindow::updateGUIWidget, Qt::QueuedConnection);
connect(worker, &Worker::workDone, thread, &QThread::quit);
connect(thread, &QThread::finished, worker, &Worker::deleteLater);
// move worker to separate thread
worker->moveToThread(thread);
thread->start();
}
mainwindow在mainwindow.h中声明了一个插槽,如下所示:
class mainwindow : public QMainWindow
{
Q_OBJECT
public:
explicit mainwindow(QWidget *parent = Q_NULLPTR);
~mainwindow();
...
public slots:
void on_importSimulatedFlight_clicked();
void updateGUIWidget(const param& rParam);
...
}
并在mainwindow.cpp中实现,如下所示:
void
mainwindow::updateGUIWidget(const param& rParam)
{
... update widget components with rParam partial result here
}
我的工人如下:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork() {
const QString result;
for (int i=0; i<5; i++) {
const MMRTimedRecord foo;
// QThread::sleep(1);
emit progressUpdate(foo);
}
emit workDone(result);
}
signals:
void progressUpdate(const MMRTimedRecord&);
void workDone(const QString &result);
};
答案 0 :(得分:2)
它不起作用的原因是因为您的代码中存在严重缺陷:您试图发出对要在其他线程上的插槽中处理的局部变量的引用。那是灾难的秘诀。
使用Qt::QueuedConnection
时,必须按值发出,如下所示:
void progressUpdate(MMRTimedRecord val);
这意味着您的MMRTimedRecord必须是可复制的,并且相应地,您的广告位还必须按值接受。为何信号progressUpdate(const MMRTimedRecord&)
与时隙updateGUIWidget(const param& rParam);
之间不匹配?
答案 1 :(得分:0)
您可以检查此answer以获得可能的解决方案。您可以
MainThreadEvent::post([&]()
{
// gui update stuff
}
);
在插槽中执行主线程中的gui更新,但是可以肯定,这是一种粗略的方法。尽管如此,我一直都在做这样的事情。小心悬挂指针和引用(使用QPointer
)...,因为发出的事件独立于发出对象。或者,使用计时器方法。
答案 2 :(得分:0)
这真的很容易-而且您不应该手动管理任何线程:
void Ui::slot() {
QtConcurrent::run([this]{
auto result = compute();
QMetaObject::invokeMethod(this, [this, r = std::move(result)]{
m_widget.setSomething(r);
});
});
}
您计算的数据类型应该是可移动的。