非主线程中的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的主要内容,如果未能解决你的问题,请参考以下文章
使用 Dispatcher.Invoke 从非主线程更改 WPF 控件
从 Frida 的非主线程调用 API 来修改应用程序的 GUI
CoreAnimation:[EAGLContext renderbufferStorage:fromDrawable:] 是从非主线程调用的