2014-12-01 29 views
5

Ich möchte eine BusyIndicator anzeigen, während ein langer Prozess läuft. Das Problem ist, dass es nicht angezeigt wird, wenn ich es ausführen lasse, und es danach anzeigt, wenn der Prozess abgeschlossen ist. Nach der Dokumentation sollteBusyIndicator wird nicht angezeigt

Die Besetztanzeige verwendet werden, Aktivität, um anzuzeigen, während die Inhalte oder die Benutzeroberfläche wird geladen wartet blockiert für eine Ressource verfügbar wird.

Ich habe einen minimalen Code erstellt, die

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 
      } 
     } 
    } 
} 

So auf dem ursprünglichen Code basiert, wenn die Rectangle geklickt wird ich die BusyIndicator für die Zeit, bis die Berechnungen angezeigt werden soll abgeschlossen wird.

Zum Beispiel Zweck Ich habe hier die for-Schleife verwendet. Im aktuellen Szenario rufe ich eine Funktion (die etwa 1000 Zeilen in die Datenbank einfügt) über die ContextProperty. Aber auch in diesem Fall wird BusyIndicator nicht angezeigt.

Mache ich es richtig? Oder was wäre der beste Weg, es zu tun?

Antwort

3

Es gibt eine Möglichkeit, dies mit QQuickWindow ‚s afterSynchronizing Signal zu tun:

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, "; 
        } 
       } 
      } 
     } 
    } 
} 

Die Idee ist, eine Loader zu verwenden, um Kontrolle zu haben, wenn die teure Operation geschieht. Sie können auch eine dynamisch geladene Komponente über Qt.createQmlObject() oder Qt.createComponent() verwenden, um eine Komponente dynamisch in einer separaten Datei zu laden.

Wenn Sie das Beispiel ausführen, werden Sie sehen, dass Sie die folgende Ausgabe:

qml: 58: 12: 356 QML geladen
qml: 58: 12: 608 Fensterinhalt gemacht

Wir verwenden QQuickWindow ‚s afterSynchronizing Signal zu wissen, wann der Inhalt des Fensters angezeigt wurde, und wirken nur auf den ersten (via if (!loader.item)).

Wenn das Signal anfänglich ausgegeben wird, können wir sicher sein, dass die BusyIndicator ihre Animation gestartet hat, so dass der Benutzer tatsächlich ein sich drehendes Symbol sehen wird.

Sobald der Loader den Text geladen hat, wird seine item -Eigenschaft nicht null und die BusyIndicator wird verschwinden.

5

Sie können Ihre BusyIndicator nicht anzeigen, nur weil lange Operation in onClicked Handler blockiert Anwendung GUI und Indikator nicht aktualisiert. Sie sollten diese Operation in einem anderen Thread ausführen, um das Einfrieren der GUI zu vermeiden. Einfaches Beispiel:

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}); 
} 
+1

zu einig. Im aktuellen Szenario habe ich eine C++ Funktion, die onClick aufgerufen wird. Kann dort ein WorkerScript verwendet werden? – astre

+0

Wenn dein Handler in C++ ist, kannst du das leicht mit QThread machen, denke ich. Ans sendet eine Benachrichtigung, wenn eine Berechnung abgeschlossen ist. – folibis

+0

Ok, das wollte ich vermeiden :) Irgendeine Möglichkeit, sowas wie processEvents von QML selbst zu machen? Oder ist es möglich, den BusyIndicator selbst auf einem separaten Thread auszuführen? – astre

1

In das gleiche Problem heute laufen! Ich gehe davon aus, dass Sie Ihre BusyIndicator von einer C++ - Eigenschaft namens busy steuern. Und Sie setzen busy auf true kurz vor Ihren Berechnungen und auf false direkt nach. Dadurch wurde es für mich gelöst. Es ist nicht eine sehr elegante Lösung, aber es funktioniert:

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); 
} 
+0

Spinnt der Busy-Indikator? Wenn Sie eine Version des QML-Renderers ohne Threaded (z. B. unter Windows) verwenden, kann ich nicht sehen, wie der "BusyIndicator" im Abschnitt "sehr lange Operation" ohne weitere Aufrufe von processEvents() rotieren kann. Für lange, zeitraubende Aufgaben verwenden Sie einfach Threads. Das ist es. – BaCaRoZzo

+0

ja es dreht sich! Der Code, den ich zur Verfügung gestellt habe, funktionierte perfekt für mich, aber ich weiß nicht wirklich warum, besonders, dass msleep (50) eine Sperrfunktion AFAIK ist. – luffy

+0

Es blockiert. Auf welcher Plattform? Wenn das Backend threaded ist, macht es Sinn, wenn Sie * diesen * Thread und nicht den UI-Thread blockieren. In Qt 5.5 wird auch Windows Rendering threading ... aber nicht aktuell. In jedem Fall 'processEvents()' ist es wirklich eine schlechte Übung. – BaCaRoZzo