12

是的,我知道你不能从非 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();
}

我创建了一个包装类,其 API 与 QWidget 相同,它存储了我想直接创建的 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();
}

------------ 编辑 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();
}

不幸的是,事实并非如此。谁能解释为什么?

--------------- 编辑 3 --------

我不确定为什么会这样?它使用信号来触发 GUI 组件,但 GUI 对象(QDialog)不是还在非 GUI 线程中创建吗?

#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();
}
4

1 回答 1

6

Qt 坚持在 GUI 线程中创建小部件。它禁止将小部件移动到不同的线程,以防止它们存在于 GUI 线程之外。实际上,您上面的示例并没有BasicViewer将 移动到另一个线程;它只会移动BasicViewerWrapper到不同的线程。BasicViewerWrapper::Function如果您在and中打印出指向包含线程的指针,您可以看到这一点BasicViewer::Function

std::cout << std::hex << thread() << std::endl;

如果您确实希望从 GUI 线程外部触发小部件的创建,则更建议其他线程通知 GUI 线程创建您想要的小部件。您可以从连接到创建小部件的 GUI 线程中的插槽的非 GUI 线程发出信号,也可以调用 GUI 线程中的函数来使用QMetaObject::invokeMethod.

编辑

不幸的是,除了QMetaObject::invokeMethod尝试在QObject. 过去,我尝试通过将方法调用放在单独的类或函数中来解决可读性问题,但不可否认,这并不完美。

您的第三个示例不起作用,因为QObject::moveToThread不是同步的。在对象实际移动到目标线程之前,控制必须返回目标线程的事件循环。因此,您可能需要结合使用 sleep 语句和对QCoreApplication::processEventsafter call 的调用moveToThread。在这些调用之后,您可能应该调用basicViewerWrapper::create()basicViewerWrapper::exec()via QMetaObject::invokeMethod

于 2012-11-01T20:22:31.867 回答