【问题标题】:QApplication In Non-Main Thread非主线程中的QApplication
【发布时间】:2011-02-17 10:27:31
【问题描述】:

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

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

【问题讨论】:

    标签: c++ user-interface qt qt4


    【解决方案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】:

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

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

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

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

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

      5. 现在,当您想要启动 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 线程中运行的任何代码。

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

          【讨论】:

            猜你喜欢
            • 2016-11-29
            • 1970-01-01
            • 1970-01-01
            • 2022-11-04
            • 1970-01-01
            • 2013-05-06
            • 2012-04-03
            • 1970-01-01
            相关资源
            最近更新 更多