【问题标题】:How can I set text of label when a function is running?函数运行时如何设置标签文本?
【发布时间】:2014-03-28 14:25:21
【问题描述】:

我想在代码中的函数运行时在主窗口上设置标签的文本。但是标签在函数结束之前不会改变。

for (int i = 0; i < files.size(); ++i) {

    ui->label->setText(files[i]);
    myfoo();

}

【问题讨论】:

    标签: qt qt4 qmainwindow mainwindow


    【解决方案1】:

    标签没有更新的原因是更新 GUI 的主线程忙于你的 for 循环,没有时间处理事件,除其他外,导致 GUI 小部件被重绘.

    虽然您可以使用 QApplication::processEvents,但这并不理想,尤其是在处理您的函数期间收到许多事件时。

    处理此问题的正确方法是创建一个单独的线程 (QThread) 和一个从 QObject 派生的对象,该对象将在向主 (GUI) 线程发送消息以更新标签时起作用。

    建议您阅读this article,了解如何正确使用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);
    }
    

    【讨论】:

    • 对不起,我没有这样成功。我的主窗口有一个插槽“updateLabelText()”。我的代码中有一个块。该块是: for() { QThread* thread = new QThread; // Worker* worker = new Worker(); this->moveToThread(thread);连接(线程,信号(开始()),这个,SLOT(updateLabelText()));线程->开始(); } .... 插槽设置标签的文本。我在运行时遇到错误,它说“moveToThread:无法将小部件移动到新线程”
    • 这个链接说“你不能将小部件移动到另一个线程”stackoverflow.com/questions/2207527/changing-a-label-in-qt
    • 这是正确的,这就是为什么我的回答建议创建一个从 QObject 派生的工作类并将其移动到不同的线程。
    • 也许我无法理解你...我的主类派生了 QObject。我创建了一个新线程,该线程的任务写入标签。但是我的编译器说你不能……
    • “这个线程的任务写入标签”是什么意思?我假设英语不是你的第一语言;抱歉,但这没有意义,所以请您解释一下,最好是通过编辑问题并显示您现在使用的代码以及编译器错误。
    【解决方案2】:

    发生这种情况是因为您的循环阻塞了事件循环 - 您永远不会返回到事件循环。在您的代码中调用大多数任何插槽时,都会从事件循环中调用它。事件循环只能在您从插槽返回或从 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 机制安全地“写入”到小部件,因为它是跨线程的。

    in my photomosaic Qt example, implemented in a mere 300 lines 是演示如何编写不阻塞 gui 的完全多线程文件访问的一个相当好的起点。

    【讨论】:

      【解决方案3】:

      您可以在一个单独的线程中执行您的 for 循环,该线程仅向 gui 线程发送信号,然后在适当的插槽中调用 label->setText()。 (使用 Qt::QueuedConnection)

      或者试试这个:

      for (int i = 0; i < files.size(); ++i) {
          ui->label->setText(files[i]);
          QApplication::processEvents();
          myfoo();  
      }
      

      【讨论】:

      • processEvents 重新进入事件循环,并可能导致比从多个线程不同步访问数据稍差的效果。
      • 这就是我首先提到线程解决方案的原因。但根据所做的工作,processEvents 可能是更好(=更简单)的解决方案
      • processEvents 本质上是一种技巧,应该在非常谨慎的情况下使用。这是非常危险的,因为突然所有你的代码必须在设计时考虑到可重入性,你甚至不能使用像互斥锁这样简单的东西来防止它,因为你只会得到僵局。 processEvents 只能从设计时考虑到可重入性的库代码中调用。它不属于通用实现代码。
      • 为什么processEvents 需要在设计所有代码时考虑到可重入性?它都在同一个线程中运行,因此不会有任何对数据的并发访问(前提是根本只有一个线程)。当然processEvents 可能会导致问题,尤其是在性能方面,为什么要小心使用它。但是在某些情况下它是更好的选择......(因为它使您的代码保持简单)
      • @MarioBlueSkies 重入不需要与线程有任何关系。即使有一个线程,如果从一个方法调用processEvents,它可能最终会在返回之前再次调用该方法。因此,该方法需要可以这样调用:重入。因此,任何可能调用使用processEvents 的方法/函数的代码都需要重新进入,除非它 100% 确定事件循环不会再次调用它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-27
      • 1970-01-01
      • 2018-09-19
      • 2014-07-24
      相关资源
      最近更新 更多