QApplication In Non-Main Thread

2019-02-02 15:02发布

I need to exec() a QApplication in a thread that is not main (my GUIs must be plugins that can be dynamically loaded and unloaded at runtime, so I have no access to the main thread). Does anyone know of a (relatively) painless way to hack around Qt's restriction against starting QApplication outside of main?

I'm developing in Linux with Qt4 in C++ using gcc4.3.4.

5条回答
放我归山
2楼-- · 2019-02-02 15:10

Adding my 2 cents with a fancy lambda and C++ threads:

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

int main(int argc, char *argv[])
{
    std::thread t1
    (
        [&]
        {
            QApplication a(argc, argv);
            qInstallMessageHandler(MainWindow::qtMessageOutput);
            MainWindow w;
            w.show();
            return a.exec();
        }
    );
    t1.join();
}

Here Mainwindow can be your QMainWindow.

查看更多
Evening l夕情丶
3楼-- · 2019-02-02 15:12

If you are using QThread then you already have normal Qt event loop and can just run exec() inside QThread::run() function. While you can't work with GUI objects outside of the main thread you still can interact with them through queued signal/slot connections. Maybe you can try to store pointer to the main thread QThread object and call QObject::moveToThread() to move your GUI objects to the main thread instead of moving QApplication into another thread.

I think it's not really good idea to try to go against toolkit with different kind of hacks and kluges.

查看更多
地球回转人心会变
4楼-- · 2019-02-02 15:16

Patch Qt, I guess and remove the main thread check, and test if that works for you. According to http://bugreports.qt-project.org/browse/QTBUG-7393 that won't work on OS X/Cocoa though, as Cocoa assumes the first thread spawned to be the main/UI thread.

查看更多
老娘就宠你
5楼-- · 2019-02-02 15:18

You can start a QApplication in a PThread as below

//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);
}
查看更多
走好不送
6楼-- · 2019-02-02 15:34

Ok, I got something that works! It's not pretty, but it definitely does the job.

  1. Create a QMainWindow derivative with all of your actual GUI code in it and overload the event() function of this class to call this->show()

  2. Create a class (let's call it Runner) which will hold a pointer to your QMainWindow derivative, and give it a run function.

  3. In the Runner::Runner(), start up a thread which will call Runner::run()

  4. In Runner::run() (which is now running in it's own thread) construct a QApplication, and an instantiation of your QMainWindow derivative. Call the exec() function of the QApplication.

  5. Now, when you want to start up your GUI, just post any event to your QMainWindow derivative, and it will show itself!

This solution seems to work very well in Linux, although it really seems to be exploiting some loophole in Qt and may not work on other platforms. Definitely easier than patching Qt though.

查看更多
登录 后发表回答