在非GUI线程中创建QWidget [英] Creating a QWidget in a non-GUI thread

查看:111
本文介绍了在非GUI线程中创建QWidget的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是的,我知道您不能使用非GUI线程中的GUI东西.但是,能够创建QWidget对象,将其发送到GUI线程然后向其发送信号似乎是合理的.但是,当我尝试这样做时,会收到无法移动小部件的错误.但是,这似乎可行:

Yes, I know that you cannot use GUI things from non-GUI threads. However, it seems reasonable to be able to create a QWidget object, send it to the GUI thread, and then send signals to it. However, when I try to do so, I get errors that widgets cannot be moved. However, this seems to works:

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

我用与QWidget相同的API创建了一个包装器类,该类存储了我想直接创建的QWidget的实例.我允许创建该包装,将其移动到GUI线程,然后使用它.我的问题是,有没有办法无需编写此包装器就可以做到这一点?似乎很乏味,并且由于该概念有效,所以我不明白为什么不能直接完成它.有什么想法吗?

I have created a wrapper class with the same API as the QWidget that stores an instance of the QWidget I wanted to create directly. I AM allowed to create that wrapper, move it to the GUI thread, and then use it. My question is, is there a way to do this without having to write this wrapper? It seems quite tedious, and since the concept works, I don't understand why it cannot be done directly. Any thoughts?

-----------编辑---------------

----------- EDIT ---------------

第一个示例是一个糟糕的示例,因为它没有尝试对GUI元素执行任何操作.此示例确实生成了无法为处于不同线程中的父级创建子级."

The first example was a bad one, because it did not attempt to do anything with GUI elements. This example indeed generates "Cannot create children for a parent that is in a different thread."

#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 ----------------

----------- EDIT 2 ----------------

我认为这会行得通,因为在移动包装器线程之后会创建成员对象:

I thought this would work, since the member object gets created after the thread of the Wrapper has been moved:

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

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

Unfortunately, it does not. Can anyone explain why?

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

--------------- EDIT 3 --------------------

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

I'm unsure why this works? It uses a signal to trigger the GUI component, but isn't the GUI object (the QDialog) still created in the non-GUI thread?

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

推荐答案

Qt坚持在GUI线程中创建窗口小部件.它禁用将窗口小部件移动到不同的线程,以防止它们存在于GUI线程之外.上面的示例,实际上是将BasicViewer移动到了另一个线程.它只会将BasicViewerWrapper移动到另一个线程.如果在BasicViewerWrapper::FunctionBasicViewer::Function中打印出指向包含线程的指针,则可以看到此信息:

Qt insists that widgets be created within the GUI thread. It disables moving widgets to different threads to prevent them from existing outside of the GUI thread. Your example above does not, in fact, move the BasicViewer to a different thread; it only moves BasicViewerWrapper to a different thread. You can see this if you print out the pointer to the containing thread within BasicViewerWrapper::Function and BasicViewer::Function:

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

如果您确实希望从GUI线程之外触发窗口小部件的创建,则建议其他线程通知GUI线程创建所需的窗口小部件.您可以从连接到创建小部件的GUI线程中的插槽的非GUI线程发出信号,也可以使用

If you really wish to trigger the creation of widgets from outside the GUI thread, it is more advisable for other threads to notify the GUI thread to create the widgets that you desire. You can either emit a signal from the non-GUI thread that connects to a slot in the GUI thread that creates the widgets, or you can invoke a function within the GUI thread to create the widgets for you using QMetaObject::invokeMethod.

编辑

不幸的是,如果您试图在QObject之外执行调用,则无法在除QMetaObject::invokeMethod之外的其他线程中调用方法.过去,我曾尝试通过将方法调用放在单独的类或函数中来解决可读性,但不可否认,它并不完美.

Unfortunately, there is no way to invoke a method in a different thread other than QMetaObject::invokeMethod if you are attempting to perform the invocation outside of a QObject. In the past, I've tried to tackle readability by placing the method invocation in a separate class or function, but admittedly, it's not perfect.

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

Your 3rd example is not working because QObject::moveToThread is not synchronous. Control must return to the destination thread's event loop before the object is actually moved to the destination thread. As such, you probably need a combination of a sleep statement and a call to QCoreApplication::processEvents after calling moveToThread. After these calls, you should probably call basicViewerWrapper::create() and basicViewerWrapper::exec() via QMetaObject::invokeMethod.

这篇关于在非GUI线程中创建QWidget的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆