【问题标题】:BusyIndicator does not show upBusyIndi​​cator 不显示
【发布时间】:2014-12-01 05:32:44
【问题描述】:

我想在一个漫长的过程中显示BusyIndicator。问题是当我让它运行时它不显示,然后在过程完成时显示。根据文档

忙碌指示符应用于指示正​​在加载内容时的活动,或者 UI 被阻塞以等待资源可用。

我创建了一个基于原始代码的最小代码

Window {
    id: win
    width: 300
    height: 300

    property bool run : false

    Rectangle {
        anchors.fill: parent
        BusyIndicator {
            anchors.centerIn: parent
            running: run
        }

        MouseArea {
            anchors.fill: parent
            onClicked: {
                run = true
                for(var a=0;a<1000000;a++) { console.log(a) }
                run = false
            }
        }
    }
}

因此,当单击Rectangle 时,我想在计算完成之前显示BusyIndicator

例如,我在这里使用了 for 循环。在实际场景中,我通过ContextProperty 调用一个函数(将大约1000 行插入数据库)。但在这种情况下,BusyIndicator 也不会显示。

我做对了吗?或者最好的方法是什么?

【问题讨论】:

    标签: qt qml qtquick2 qtquickcontrols


    【解决方案1】:

    您无法查看您的BusyIndicator,因为onClicked 处理程序中的长时间操作会阻塞应用程序GUI 并且指示器不会更新。您应该在不同的线程中运行此类操作以避免冻结 GUI。简单例子:

    QML

    Window {
        id: win
        width: 300
        height: 300
    
        property bool run : false
    
        Rectangle {
            anchors.fill: parent
            BusyIndicator {
                id: busy
                anchors.centerIn: parent
                running: win.run
            }
    
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    win.run = true
                    thread.sendMessage({run : true});
                }
            }
    
            WorkerScript {
                id: thread
                source: "handler.js"
                onMessage: {
                    win.run = messageObject.run;
                }
            }
        }
    }
    

    handle.js

    WorkerScript.onMessage = function(message) {
        if(message.run === true) {
            for(var a=0;a<1000000;a++) { console.log(a) }
        }
        WorkerScript.sendMessage({run : false});
    }
    

    【讨论】:

    • 同意。在实际场景中,我有一个名为 onClick 的 C++ 函数。那里可以使用 WorkerScript 吗?
    • 如果你的处理程序是 C++,那么我猜你可以用 QThread 轻松做到这一点。 Ans 在计算完成时发送一些通知。
    • 好的,我想避免这种情况:) 任何可能的方法来做类似于 QML 本身的 processEvents 的事情?或者是否可以在单独的线程上运行 BusyIndi​​cator 本身?
    • 您可以使用QtConcurrent::run(),而不是使用QThreads。它在线程池中为长时间运行的进程生成一个线程,并返回一个QFuture<T>。然后您可以使用QFutureWatcher<T>::finished 信号来查看任务何时完成,如果存在则获取任何结果。 “已完成”信号在创建 QFutureWatcher 的同一线程中发出,因此您无需处理任何同步。
    • @Jairo,除此之外还有什么?请参考我之前评论的其他部分。
    【解决方案2】:

    有一种方法可以使用QQuickWindowafterSynchronizing 信号:

    import QtQuick 2.4
    import QtQuick.Controls 1.3
    
    ApplicationWindow {
        width: 400
        height: 400
        visible: true
    
        Component.onCompleted: print(Qt.formatDateTime(new Date(), "mm:ss:zzz"), "QML loaded")
    
        onAfterSynchronizing: {
            print(Qt.formatDateTime(new Date(), "mm:ss:zzz"), "Window content rendered")
            if (!loader.item) {
                loader.active = true
            }
        }
    
        Item {
            anchors.fill: parent
    
            BusyIndicator {
                running: !loader.item
                anchors.centerIn: parent
            }
    
            Loader {
                id: loader
                active: false
                anchors.fill: parent
                sourceComponent: Text {
                    wrapMode: Text.Wrap
    
                    Component.onCompleted: {
                        for (var i = 0; i < 500; ++i) {
                            text += "Hello, ";
                        }
                    }
                }
            }
        }
    }
    

    这个想法是使用Loader 来控制何时发生昂贵的操作。您还可以通过Qt.createQmlObject()Qt.createComponent() 使用动态加载的组件,将组件动态加载到单独的文件中。

    如果你运行这个例子,你会看到你得到以下输出:

    qml:58:12:356 QML 已加载
    qml: 58:12:608 呈现的窗口内容

    我们使用QQuickWindowafterSynchronizing 信号来了解窗口内容何时显示,并且仅在第一次对其进行操作(通过if (!loader.item))。

    当信号最初发出时,我们可以确定BusyIndicator 已经开始了它的动画,所以用户实际上会看到一个旋转的图标。

    一旦Loader 完成加载文本,其item 属性将变为非空,BusyIndicator 将消失。

    【讨论】:

      【解决方案3】:

      今天遇到同样的问题!我将假设您正在通过名为 busy 的 C++ 属性控制您的 BusyIndicator。您在计算之前将busy 设置为true,之后设置为false。这样做为我解决了这个问题。这不是一个非常优雅的解决方案,但它确实有效:

      QML

      BusyIndicator {
          running: CPPModule.busy
      }
      

      CPP

      void CPPModule::setBusy(const bool &busy)
      {
          m_busy = busy;
          emit busyChanged();
      }
      void CPPModule::InsertIntoDB()
      {
          setBusy(true);
          QThread::msleep(50);
          QCoreApplication::processEvents();
          /*
          very Long Operation
          */
          setBusy(false);
      }
      

      【讨论】:

      • Busyindicator 是否旋转?因为如果您使用的是 QML 渲染器的非线程版本(例如在 Windows 上),我看不到 BusyIndicator 在“非常长的操作”部分中没有进一步的 processEvents() 调用如何旋转。对于长时间、耗时的任务,只需使用线程。就是这样。
      • 是的,它会旋转!我提供的代码非常适合我,但我真的不知道为什么,尤其是 msleep(50) 是一个阻塞函数 AFAIK。
      • 它正在阻塞。在哪个平台上?如果后端是线程化的,那么它是非常有意义的,因为您正在阻塞 that 线程,而不是 UI 线程。在 Qt 5.5 中,Windows 也将具有线程渲染……但目前还没有。在任何情况下processEvents() 这都是非常糟糕的做法。
      猜你喜欢
      • 1970-01-01
      • 2011-06-25
      • 2012-11-11
      • 2012-07-10
      • 2013-04-19
      • 2011-04-02
      • 2013-05-19
      • 2012-09-05
      • 1970-01-01
      相关资源
      最近更新 更多