利用Qt信号槽和std::function将任务放在主线程(GUI线程)运行

  1. 声明一个添加任务接口,信号和槽函数各两个(返回类型void和非void)。添加任务接口的参数为std::function类型。信号与槽函数的第一个参数是std::function类型的任务,第二个参数则为任务的返回值。
class TestQtMainThread : public QWidget {
  Q_OBJECT

 public:
  TestQtMainThread(QWidget* parent = Q_NULLPTR);

  template <typename T>
  T AppendTask(const std::function<T()>& task);

 signals:
  void SIGAppendTask(const std::function<QVariant()>& task, QVariant& ret);
  void SIGAppendTask(const std::function<void()>& task);

 private slots:
  void ONAppendTask(const std::function<QVariant()>& task, QVariant& ret);
  void ONAppendTask(const std::function<void()>& task);

 private:
  Ui::TestQtMainThreadClass ui;
};
  1. 使用Qt::BlockingQueuedConnection方式连接信号与槽函数
  connect(this,
          qOverload<const std::function<QVariant()> &, QVariant &>(
              &TestQtMainThread::SIGAppendTask),
          this,
          qOverload<const std::function<QVariant()> &, QVariant &>(
              &TestQtMainThread::ONAppendTask),
          Qt::BlockingQueuedConnection);

  connect(
      this,
      qOverload<const std::function<void()> &>(
          &TestQtMainThread::SIGAppendTask),
      this,
      qOverload<const std::function<void()> &>(&TestQtMainThread::ONAppendTask),
      Qt::BlockingQueuedConnection);
  1. 槽函数定义
void TestQtMainThread::ONAppendTask(const std::function<QVariant()> &task,
                                    QVariant &ret) {
  ret = task();
}

void TestQtMainThread::ONAppendTask(const std::function<void()> &task) {
  task();
}
  1. 接口定义
template <typename T>
inline T TestQtMainThread::AppendTask(const std::function<T()>& task) {
  // 信号槽无法对没特化的tempalte进行connect,使用QVariant转换
  // 转换包括:1.使用lambda包裹task转换其返回类型;2.转换参数类型
  QVariant ret;
  // 避免BlockingQueuedConnection死锁
  // this->thread() == QCoreApplication::instance()->thread()
  if (QThread::currentThread() == this->thread()) {
    ONAppendTask([&]() { return QVariant::fromValue<T>(task()); }, ret);
  } else {
    emit SIGAppendTask([&]() { return QVariant::fromValue<T>(task()); }, ret);
  }
  return ret.value<T>();
}

// void特化版
template <>
inline void TestQtMainThread::AppendTask(const std::function<void()>& task) {
  if (QThread::currentThread() == this->thread()) {
    ONAppendTask(task);
  } else {
    emit SIGAppendTask(task);
  }
}
  1. 使用
// WorkerThread是QThread子类,此段函数在非主线程运行
void WorkerThread::run() {
  auto result = main_thread_->AppendTask<QString>([]() {
    if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
      return "error!";
    }
    return "ok!";
  });
  qDebug() << result;

  // QWidget::move(...),此函数不允许在非主线程调用
  main_thread_->AppendTask<void>([&]() { main_thread_->move({0, 0}); });
}

覆盖QWidget::move(...),非子线程可直接调用TestQtMainThread::move(...)

void TestQtMainThread::move(const QPoint &pos) {
  this->AppendTask<void>([&] { this->QWidget::move(pos); });
}
posted @ 2022-05-09 17:19  ithepug  阅读(616)  评论(0)    收藏  举报