QT - 在不阻止他的 GUI 的情况下显示小部件

Posted

技术标签:

【中文标题】QT - 在不阻止他的 GUI 的情况下显示小部件【英文标题】:QT - Show Widget without blocking his GUI 【发布时间】:2014-04-10 13:44:14 【问题描述】:

我想在另一个函数 (pthread) 计算任务时显示一个用于显示动画加载 gif 的小部件。

我尝试使用QThread 类对我的小部件进行子类化,并实现了方法run(),我在其中调用了方法show()。但是,我的小部件 GUI 冻结了。

如何启动单独处理 GUI 的小部件?

【问题讨论】:

请发布一些重现问题的最小代码示例 你可以看看Qt QProgressDialog类。如果您需要小部件不阻塞主 GUI,您可以在非模式模式下运行它吗? 我认为每个人都对这个问题的糟糕措辞感到有些困惑。这是我的看法:提问者尝试在工作线程中做某事,同时在 gui 线程中显示一些进度。他通过实验正确地确定了直接调用 gui 对象的幼稚方法将不起作用。这个问题基本上归结为:我怎样才能从另一个线程“做 gui 的事情”。 【参考方案1】:

你不能让小部件在除主线程之外的任何东西上运行。

另外,除非你想改变 Qt 处理线程的方式,否则你不应该从 QThread 继承。

相反,创建一个从 QObject 继承的工作对象并将其移动到新线程。你可以阅读how to really use QThread here。

然后您的工作对象可以移动到另一个线程,进行计算并通过信号和插槽与主线程上的 Gui 小部件进行通信。

例如,下面是一个工人阶级的简要概述:-

class Worker : public QObject

    Q_OBJECT        

    signals:
        void finished();

        void displayWidget();

    private slots:
        void run();


QThread pThread = new QThread;
Worker pObject = new Worker;

// move the pObject to the thread
pObject->moveToThread(pThread);  

然后您可以使用信号和槽来控制线程。

// assuming you've added a run slot function to the Consumer class
connect(pThread, SIGNAL(started()), pObject, SLOT(run()));
connect(pObject, SIGNAL(finished()), pThread, SLOT(quit()));
connect(pObject, SIGNAL(finished()), pObject, SLOT(deleteLater()));

// Note the thread cleans itself up here, but if the app is quitting,
// waiting on the thread to finish may be required instead
connect(pThread, SIGNAL(finished()), pThread, SLOT(deleteLater()));

并启动线程:-

pThread->start();

使用这种方式,它还可以将多个对象移动到一个新线程,而不是为每个对象实例创建一个新线程。

所以现在,例如,如果您想在工作对象处理期间的某个时间点显示一个小部件,您将发出它的 displayWidget() 信号,之前已将它连接到小部件的 show() 插槽。

QWidget* pWidget = new QWidget(parent); // assumes parent is initialized
// using Qt 5 connect call
connect(pWorker, &Worker::displayWidget, pWidget, &Widget::show);

【讨论】:

你没有注意到 user2543127 用 QThread 子类化了他的小部件,这是他尝试的邪恶的根源吗?无法从 UI 线程(主线程)外部访问/创建 UI。 是的,这就是为什么我建议他创建一个继承自 QObject 的工作对象。 但是你没有对这个答案解释太多 OP 没有显示任何可使用的示例代码。我没有从一篇优秀的文章中反省信息,而是链接到它。所有信息都在那里。 @Merlin069 这些都没有解决提问者的问题:如何从非 GUI 线程调用 widget->show()。提到“信号和槽”似乎是朝这个方向发展的,但随后突然停止。关于如何正确使用QThread 的建议虽然有效,但在这里有些偏离主题。教学法要求不要一次调用太多新事物。【参考方案2】:

您不能从 GUI 线程以外的线程直接使用QWidget(或任何派生类)。您可以直接做的就是使用工作线程拥有的QImage 并直接从该线程对其进行绘制。这里,直接的意思是你只是在调用对象的方法。

您需要一种方法来执行show(),而不是直接在调用线程中,而是在 GUI 线程的上下文中间接执行。这很简单,因为QWidget::show() 是一个插槽。因此,从您的计算线程中,只需执行以下操作:

QMetaObject::invokeMethod(widget, "show");

就是这样。 invokeMethod 的实现将确定widget 存在于不同的线程中,并会自动选择QueuedConnection 调用传递方法。在内部,它会将QMetaCallEvent 发布到widget。小部件的QObject::event 方法将作用于事件并调用show 方法。这将发生在 GUI 线程中。

你可以用同样的方法设置QProgressBar,例如:

int value = ...;
QMetaObject::invokeMethod(progressBar, "setValue", Q_ARG(int, value));

【讨论】:

感谢您的帮助,我尝试实例化我的 QObject:LoadingWindow* load_window = new LoadingWindow() 并调用函数 QMetaObject::invokeMethod(load_window,"slot_openWindow");我的“slot_openWindow”调用方法显示了一个 qlabel,但 GUI 冻结 @user2543127 您不能在非 GUI 线程中实例化该窗口。您必须有一个已经存在的实例。 我的问题没有解决! :( 您可以提供一个简单的完整示例,向我展示一个小部件如何在任务功能长时间工作时保持他的 gui 响应?谢谢 我写了a photographic mosaic example,它在工作线程中执行所有计算和图像 I/O。 GUI 在任何时候都是响应式的。我还有很多其他的例子,请随意使用关键字 QThread 搜索我的答案。

以上是关于QT - 在不阻止他的 GUI 的情况下显示小部件的主要内容,如果未能解决你的问题,请参考以下文章

QT gui 项目不会缩小到其父小部件的 ft

如何为 Qt GUI 小部件使用图像

在 GUI 应用程序中定位小部件 (PySide)

如何在不中断 tkinter 主循环的情况下运行一个函数,同时将该函数的信息发送到我的主循环中的小部件?

在已经运行的 c++ 控制台应用程序上实现 Qt Gui

从 Qt 中的文本文件填充表格小部件