非主线程中的QApplication

Posted

技术标签:

【中文标题】非主线程中的QApplication【英文标题】:QApplication In Non-Main Thread 【发布时间】:2010-05-09 19:16:25 【问题描述】:

我需要在非主线程中 exec() 一个 QApplication(我的 GUI 必须是可以在运行时动态加载和卸载的插件,所以我无法访问主线程)。有谁知道(相对)轻松的方法来破解 Qt 对在 main 之外启动 QApplication 的限制?

我正在使用 gcc4.3.4 在 C++ 中使用 Qt4 在 Linux 中进行开发。

【问题讨论】:

【参考方案1】:

您可以在 PThread 中启动 QApplication,如下所示

//main.cpp

#include <iostream>
#include "appthread.h"
int main(int argc, char *argv[]) 
  InputArgs args = argc, argv;
  StartAppThread(args);
  sleep(10);
  return 0;

//appthread.h

struct InputArgs
  int argc;
  char **argv;
;
void StartAppThread(InputArgs &);

//appthread.cpp

#include <QApplication>
#include <QMainWindow>
#include <QPushButton>
#include "appthread.h"
#include <pthread.h>

void *StartQAppThread(void *threadArg) 
  InputArgs *args = (struct InputArgs*) threadArg;
  QApplication app(args->argc, args->argv);
  QMainWindow w;
  w.show();
  w.setCentralWidget(new QPushButton("NewButton"));
  app.exec();
  pthread_exit(NULL);


void StartAppThread(InputArgs &args) 
  pthread_t thread1;  
  int rc = pthread_create(&thread1, NULL, StartQAppThread, (void*)&args);

【讨论】:

【参考方案2】:

如果您使用的是 QThread,那么您已经有正常的 Qt 事件循环,并且可以在 QThread::run() 函数中运行 exec()。虽然您不能在主线程之外使用 GUI 对象,但您仍然可以通过排队的信号/插槽连接与它们进行交互。也许您可以尝试存储指向主线程 QThread 对象的指针并调用 QObject::moveToThread() 将您的 GUI 对象移动到主线程,而不是将 QApplication 移动到另一个线程。

我认为尝试使用不同类型的 hacks 和 kluges 来对抗工具包并不是一个好主意。

【讨论】:

我认为你可能是对的,VestniK - 虽然我的 GUI 确实在运行,但它似乎并没有真正为 update() 调用提供服务,除非我通过拖动另一个窗口来强制操作系统重绘 GUI在它之上。 我发现这个答案对于解决另一个问题很有用,但是在将 QMainWindow 对象从另一个线程移动到主线程时,我收到了QObject::moveToThread: Widgets cannot be moved to a new thread【参考方案3】:

好的,我得到了一些有用的东西!它不漂亮,但它确实可以完成这项工作。

    创建一个包含所有实际 GUI 代码的 QMainWindow 派生类,并重载此类的 event() 函数以调用 this->show()

    创建一个类(我们称之为 Runner),它将持有指向 QMainWindow 派生类的指针,并为其提供一个运行函数。

    在 Runner::Runner() 中,启动一个线程,该线程将调用 Runner::run()

    在 Runner::run()(现在在它自己的线程中运行)中构造一个 QApplication,以及一个 QMainWindow 衍生的实例化。调用 QApplication 的 exec() 函数。

    现在,当您想要启动您的 GUI 时,只需将任何事件发布到您的 QMainWindow 衍生产品,它就会显示出来!

这个解决方案似乎在 Linux 中运行良好,尽管它似乎确实利用了 Qt 中的一些漏洞,并且可能无法在其他平台上运行。不过肯定比修补 Qt 容易。

【讨论】:

嘿Boatzart,我正在努力实现同样的目标。当您说“ Runner::Runner(),启动一个将调用 Runner::run() 的线程”时,您是什么意思。启动另一个将启动 Runner 线程的 QThread?但是不构造 QApplication 怎么能启动另一个线程呢?【参考方案4】:

修补Qt,我猜并删除主线程检查,并测试它是否适合你。 根据 http://bugreports.qt-project.org/browse/QTBUG-7393 不过,这在 OS X/Cocoa 上不起作用,因为 Cocoa 假定生成的第一个线程是主线程/UI 线程。

【讨论】:

【参考方案5】:

用花哨的 lambda 和 C++ 线程加上我的 2 美分:

#include "mainwindow.h"
#include <QApplication>
#include <thread>

int main(int argc, char *argv[])

    std::thread t1
    (
        [&]
        
            QApplication a(argc, argv);
            MainWindow w;
            w.show();
            return a.exec();
        
    );
    t1.join();

这里的Mainwindow 可以是你的QMainWindow

【讨论】:

【参考方案6】:

一个很好的解决方案在:git@github.com:midjji/convenient_multithreaded_qt_gui.git

那么它只是例如

run_in_gui_thread(new RunEventImpl([]()
        QMainWindow* window=new QMainWindow();
        window->show();
    ));

或您希望在 gui 线程中运行的任何代码。

可随时从任何线程调用,同时在后台为您进行设置。

【讨论】:

以上是关于非主线程中的QApplication的主要内容,如果未能解决你的问题,请参考以下文章

多线程的非主线程的销毁机制

从非主线程加载的 VAO 崩溃

使用 Dispatcher.Invoke 从非主线程更改 WPF 控件

从 Frida 的非主线程调用 API 来修改应用程序的 GUI

CoreAnimation:[EAGLContext renderbufferStorage:fromDrawable:] 是从非主线程调用的

我无法使用非主 MOC 在后台线程上创建 NSManagedObject 的新实例