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 的小部件?
【问题讨论】:
请发布一些重现问题的最小代码示例 你可以看看QtQProgressDialog
类。如果您需要小部件不阻塞主 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 的情况下显示小部件的主要内容,如果未能解决你的问题,请参考以下文章