使用 QtConcurrent::run 在单独的线程上连接信号/插槽

Posted

技术标签:

【中文标题】使用 QtConcurrent::run 在单独的线程上连接信号/插槽【英文标题】:Connecting signals/slots on separate thread using QtConcurrent::run 【发布时间】:2012-02-20 19:57:34 【问题描述】:

在我的应用程序中,我在对话框中有以下代码:

connect(drive, SIGNAL(FileProgressChanged(Progress)), SLOT(OnFileProgressChanged(Progress)));

QtConcurrent::run(this, &ProgressDialog::PerformOperation, Operation, *Path, OutPath, drive);

PerformOperation函数最终调用drive中的一个函数,该函数发出信号FileProgressChanged,而我的OnFileProgressChanged函数如下:

void ProgressDialog::OnFileProgressChanged(Progress p)

    if (ui->progressCurrent->maximum() != p.Maximium)
        ui->progressCurrent->setMaximum(p.Maximium);

    ui->progressCurrent->setValue(p.Current);

    if (ui->groupBoxCurrent->title().toStdString() != p.FilePath)
        ui->groupBoxCurrent->setTitle(QString::fromStdString(p.FilePath));

我正在阅读并看到 QFuture 和 QFutureWatcher 支持监控进度值(在这种情况下效果很好!),但它们不能与 QtConcurrent::run 结合使用。

我将如何将在单独线程上发出的移动信号连接到我的主线程上的插槽,以便我可以监控在发射器线程上调用的函数的进度?

*Edit -- * 实际上我发现我的代码有一个错误,但它似乎没有影响。我忘了在信号后添加this 作为参数

connect(drive, SIGNAL(FileProgressChanged(Progress)), this, SLOT(OnFileProgressChanged(Progress)));

【问题讨论】:

我不太明白,乍一看这似乎应该可以工作。线程从drive 发出FileProgressChanged——OnFileProgressChanged 是否被正确调用?从一个线程向另一个线程的槽发出信号应该可以正常工作(它被排队)。 在发出的函数上,代码是emit FileProgressChanged(p)。如果我介入,它会将我带到this,但我在 OnFileProgressChanged 处的断点永远不会被命中。 【参考方案1】:

尝试将connect()QueuedConnection 一起使用,例如:

connect(drive, SIGNAL(FileProgressChanged(Progress)), this, SLOT(OnFileProgressChanged(Progress)), Qt::QueuedConnection);

默认情况下连接应该已经排队(因为发射器和接收器在不同的线程中),但这只是让它更加明确。

编辑:问题是Progress 类型没有在Qt 的元对象系统中注册。添加qRegisterMetaType<Progress>("Progress"); 解决了这个问题。

【讨论】:

没有骰子。我做了更多的搜索,我相信答案在于this question。没有参数,信号/插槽连接工作正常。 这也是可能的。在connect() 调用之前,您需要有一个qRegisterMetaType<Progress>("Progress"); 行。忽略它应该会引发错误(在编译时或运行时)。 经过测试,这就是问题所在!非常简单的修复,谢天谢地。感谢您的帮助。 很高兴听到。我很惊讶它在运行时没有抛出错误,你在看输出吗(比如 Qt Creator 的“应用程序输出”窗格)?另外,介意将此标记为已接受的答案吗? :) 我本来想说我会将此标记为答案,所以...是的。我不想等2天。我的应用程序吐出了太多信息,以至于无法注意到输出中的任何内容,因此如果确实如此,那么它就会被忽视。【参考方案2】:

似乎问题不在于跨线程信号/插槽,而在于参数Progress。 This 问题的答案更详细,但通过在声明 Progress 的头文件中执行以下操作找到了解决方案:

struct Progress

    int Current;
    int Maximium;
    std::string FilePath;
    std::string FolderPath;
    int TotalMinimum;
    int TotalMaximum;
;

Q_DECLARE_METATYPE(Progress)

在我的表单类中:

qRegisterMetaType<Progress>();
    connect(Drive, SIGNAL(FileProgressChanged(const Progress&)), this, SLOT(OnFileProgressChanged(const Progress&)), Qt::QueuedConnection);

Progress 更改为const Progress&amp; 很可能是不需要的,但我在测试时离开了它。

【讨论】:

以上是关于使用 QtConcurrent::run 在单独的线程上连接信号/插槽的主要内容,如果未能解决你的问题,请参考以下文章

QtConcurrent::run 如何停止后台任务

在 QtConcurrent::run 中使用 QSqlDatabase 连接(伪连接池)

使用 QtConcurrent::run() 修改成员变量?

在 c++11 模式下使用带有仅移动参数的 QtConcurrent::run

是否可以将 QtConcurrent::run() 与类的函数成员一起使用

停止由 QtConcurrent::run 启动的线程?