我想在代码中的函数运行时在主窗口上设置标签的文本。但是标签在函数结束之前不会改变。
for (int i = 0; i < files.size(); ++i) {
ui->label->setText(files[i]);
myfoo();
}
我想在代码中的函数运行时在主窗口上设置标签的文本。但是标签在函数结束之前不会改变。
for (int i = 0; i < files.size(); ++i) {
ui->label->setText(files[i]);
myfoo();
}
没有看到标签更新的原因是更新 GUI 的主线程正忙于您的 for 循环并且没有时间处理事件,除其他外,这会导致 GUI 小部件被重绘。
虽然您可以使用 QApplication::processEvents,但这并不理想,尤其是在处理您的函数期间收到许多事件时。
处理此问题的正确方法是创建一个单独的线程 (QThread) 和一个从 QObject 派生的对象,该对象将在向主 (GUI) 线程发送消息以更新标签时工作。
与其重复代码,我建议你阅读这篇关于如何正确使用 QThread 的文章。这并不难。
然后,您将现在位于第二个线程的对象中的函数更改为如下所示:-
for (int i = 0; i < files.size(); ++i)
{
// calling UpdateLabel signal, which connects to an object on the main thread
UpdateLabel(files[i]);
myfoo();
}
假设来自 Worker 类的信号已连接到主线程上的对象中的插槽,例如 QMainWindow,您将收到插槽中的文本并更新标签:-
void QMainWindow::UpdateLabel(const QString text)
{
ui->label->setText(text);
}
发生这种情况是因为您的循环阻塞了事件循环——您永远不会返回到事件循环。在您的代码中调用大多数任何插槽时,都会从事件循环中调用它。只有当您从插槽或方法返回时,事件循环才能继续QObject::event
。
使用 Qt 5 和 C++11,从单独的线程更新文件列表非常容易。
QtConcurrent::run([this] {
// This code runs in a separate thread
for (int i = 0; i < files.size(); ++i) {
// This is safe for cross-thread use
QMetaObject::invokeMethod(ui->label, "setText", files[i]);
myfoo();
}
});
的实现myfoo()
必须是线程安全的,并且不能直接访问任何小部件。它仍然可以通过使用该invokeMethod
机制安全地“写入”到小部件,因为它是跨线程的。
在我的 photomosaic Qt 示例中给出了演示如何编写不阻塞 gui 的完全多线程文件访问的一个相当好的起点,该示例仅用 300 行实现。
您可以在一个单独的线程中执行您的 for 循环,该线程仅向 gui 线程发送信号,然后在适当的插槽中调用 label->setText()。(使用 Qt::QueuedConnection)
或者试试这个:
for (int i = 0; i < files.size(); ++i) {
ui->label->setText(files[i]);
QApplication::processEvents();
myfoo();
}