ProgressBar update wird nicht zur laufzeit angezeigt

Hi,

ich versuche in mein erstes kleines JavaFX Programm einen ProgressBar einzubauen (ist gelungen…) den ich in einem eigenen Thread “hochzählen” lassen will.

Aktuell läuft eine while Bedingung mit System.out.prinln… Ausgabe zum Testen, aber der ProgressBar läuft nicht mit sondern aktualisiert sich erst wenn die While Bedingung beendet wurde…

Ich habe schon X Versuche unternommen und mir “einen Wolf gegoogelt” aber ich komm nicht drauf…

Ich habe jetzt eine eigene Klasse (ProgressBarClass) gemacht die ich dann in einer anderen aufrufe:


import demo.ProfileController;
import static demo.ProfileController.testBar;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.control.ProgressBar;


public class ProgressBarClass extends Thread {

    public static void main(String[] args) {
        // ProgressBarClass t = new ProgressBarClass();
        // t.start();

        
        Task task = new Task<Void>() {
       //   Platform.runLater(new Runnable() {
       @Override
       public void run() {
           double progress = 250.0;
           double count = 249.0;

           while (progress != 100) {
               testBar.setProgress(((progress - count) / count) * -1);
               progress--;
               try {
                   Thread.sleep(10);
               } catch (InterruptedException ex) {
                   Logger.getLogger(ProfileController.class.getName()).log(Level.SEVERE, null, ex);
               }
               System.out.println(progress);

           }
       }
//}) ;

       @Override
       protected Void call() throws Exception {
           throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
       }
   };
        ProgressBar testBar = new ProgressBar();
        testBar.progressProperty().bind(task.progressProperty());
        new Thread(task).run();
    }
}

Im ProfilController habe ich einen Button (csvTabelleAnzeigen). Hier soll auch der ProgressBar (zum Testen) durchlaufen…

.
.
.

 public void csvTabelleAnzeigen(ActionEvent event) throws FileNotFoundException, IOException, Exception {
        
        String[] args = null;
        Stage stageCSVTabelleAnzeigen = new Stage();
        ShowCSVInTable tw = new ShowCSVInTable();                     
      
        tw.start(stageCSVTabelleAnzeigen);
        
      
     //Thread für eigenständiges Starten des ProgressBars!!!
    
    
    schreiben.ProgressBarClass.main(args);
        



    }

.
.
.

leider weiß ich nicht mehr weiter, ich probiere schon seit Stunden etwas zu finden das Funktioniert… :wut:

unabhängig von JavaFX führt dich die Suche doch sicher etwa zu
http://docs.oracle.com/javase/tutorial/uiswing/components/progress.html

dessen Aufbau könntest du dir doch zumindest grundsätzlich anschauen,
wobei du ja auch schon weit gekommen bist, zu Thread und Task:
die Schleife muss nebenläufig arbeiten, damit der GUI-Thread zeichnen kann, statt die Schleife zu bearbeiten

der nächste Schritt wäre dann gewesen, sich zu Thread etwas besser zu informieren,
oder Verwendung von javafx.concurrent.Task, was ich persönlich wie ganz JavaFX nicht kenne, da sage ich also nichts zu,

aber zu Thread ist nun ganz grundsätzlich zu wissen: mit start()-Aufruf starten! dann gibts intern tatsächlich einen Ausführungs-Thread, der run() abarbeitet,

wenn du nur run() aufrufst, ist das eine ganz normale Methode,
ein Object der Klasse Thread ist für sich auch keine Magie, einfach nur ein Objekt

also start(), dann geht es vielleicht schon


grundsätzlich etwas aufpassen, auch im Auskommentier-Wirrwarr:
wenn du new Thread(task) anlegst, dann braucht die Klasse ProgressBarClass nicht selber auch von Thread erben

Ich habe selbst zwar noch nicht mit dem JavaFX-Task gearbeitet, aber die API dazu schaut recht erklärend aus: http://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html

Meiner ersten Sichtung nach sollte alles in call() stattfinden und du solltest die Methoden updateProgress() verwenden. Die rechnet sogar selbst den Prozentwert aus :wink:

die run-Methode von Task zu überschreiben ist sicher nicht vorgesehen,
dann kann man auch gleich ein Runnable nehmen,
aber ein einfacher Thread, einfach nur nebenläufig, geht für den Anfang ja auch,

schön dass im Link auch thread.start() aufgeführt ist

Danke schonmal für die Antworten!

Wenn ich thread.start() verwende, kommt:

xception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
	at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:237)
	at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:397)
	at javafx.scene.Parent$1.onProposedChange(Parent.java:245)
	at com.sun.javafx.collections.VetoableObservableList.setAll(VetoableObservableList.java:90)
	at com.sun.javafx.collections.ObservableListWrapper.setAll(ObservableListWrapper.java:314)
	at com.sun.javafx.scene.control.skin.ProgressBarSkin.initialize(ProgressBarSkin.java:289)
	at com.sun.javafx.scene.control.skin.ProgressBarSkin.access$400(ProgressBarSkin.java:56)
	at com.sun.javafx.scene.control.skin.ProgressBarSkin$5.invalidated(ProgressBarSkin.java:210)
	at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)

‚IllegalStateException: Not on FX application thread‘
ist wieder etwas, was sich perfekt für Suchmaschinen eignet

ich schätze auf die Schnelle:

  • entweder generell an womöglich vorhandene Mechanismen von JavaFX halten,
    doch kein eigener Thread sondern z.B. den Task auf offiziell richtigen Weg verwenden,
    im Link ist ein allgemeines Tutorial genannt
    http://docs.oracle.com/javafx/2/swing/jfxpub-swing.htm
    welches sich allerdings an Kenntnisse von Swing richtet, was bei dir vielleicht auch nicht so ideal vorhanden ist :wink:

  • oder beim Thread bleiben, aber jede einzelne Aktion, die die GUI verändern,
    in ein kleines Kommando an den offizellen GUI-Thread verwandeln
    (was sich bei Swing grundsätzlich auch anbietet, nur ohne generelle Fehlermeldung…)
    Platform.runLater(new Runnable() usw. wie im Link,
    aber wichtig dann: nur die einzelnen kurzen Aktionen, nicht die Schleife oder gar das sleep() dort ausführen

rechne den Progresswert noch normal aus, diese lokale Variable final machen, damit von anonymer innerer Klasse zu verwenden, dann das runLater()

Danke… ich habe es jetzt folgendermaßen gelöst:

public void csvTabelleAnzeigen(ActionEvent event) throws FileNotFoundException, IOException, Exception {

 //Thread für eigenständiges Starten des Progressbars!!!
        final Task task;
        task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                int max = 50;
                for (int i = 1; i <= max; i++) {
                    
                    if (isCancelled()) {
                        break;
                    }
                    
                    updateProgress(i, max);
                    updateMessage(String.valueOf(i));

                    Thread.sleep(100);
                    //System.out.println("in Schleife");
                    System.out.println(String.valueOf(i));
                   
                }
                return null;
            }
        };
        
        testBar.setProgress(0);
        testBar.progressProperty().bind(task.progressProperty());
       
        
        System.out.println("vor Thread start");
        
        new Thread(task).start();
        
        System.out.println("nach  Thread start");




    }

So läuft der Balken erst mal “mit fixen” werten…

Evtl. könnte ja jemand wenn er Lust hat das Beispiel so abändern wie es evtl. noch besser wäre…

Kann ja sein das die Lösung zwar funktioniert aber ganz und gar nicht gut ist? oder vielleicht auch gar nicht so genutzt werden sollt?

wie bist du denn auf diese Lösung gekommen?
die ist doch fast 1:1 aus den Beispielen von http://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html übernommen/ gleich

ich musste erstmal heftig überlegen wie das überhaupt funktionieren kann,
was machen unbekannte Methoden updateProgress(i, max); und updateMessage(String.valueOf(i));,
wie um alles in der Welt ist das mit einer bestimmten Progressbar verknüpft?

aber testBar.progressProperty().bind(task.progressProperty());
ist eben ein weiterer hochspezifischer Befehl genau für diese Aufgabe,
den kann man doch nur kopieren, kaum selber drauf kommen?
unter http://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html gar nicht genannt…

also das ist eine so maßgeschneiderte Lösung,
da ist die Frage „vielleicht auch gar nicht so genutzt werden sollt?“ tiefstapelnd :wink:

Ich hab ja nicht behauptet das ich da ganz von selbst drauf gekommen bin… wozu gibt es Google? :slight_smile:

Leider sind meine Java Kenntnisse sehr beschränkt und meine JavaFX Kenntnisse noch sehr viel geringen. Da ich aber die Aufgabe habe ein kleines aber für mich schon sehr herausforderndes Programm zu schreiben muss ich schauen wie ich an die notwendigen Bausteine komme um mir das „zusammen zu stöpseln“.

evtl. könntest du mir ja ein kurzes allgemeineres Beispiel aufschreiben, wie ich 2 Threads laufen lassen kann.

  1. Thread ist der der den ProgressBar aktuallisiert
  2. Thread ist ein SQL Update auf einer Oracle DB

Das SQL Update hatte ich schon, das mit dem ProgressBar aber eben nicht.
:frowning:

„das mit dem ProgressBar aber eben nicht.“
doch, denn das hast du hier vorliegen :wink:
du meinst vielleicht, dass du die Kombination noch nicht hast

und ich sage nicht dass es schlecht ist zu kopieren, poste ja selber nur Links,
sondern ich wundere mich dass du „ganz und gar nicht gut ist“ fragst
wenn du doch offensichtlich ein genau passendes Beispiel gefunden hast


wenn die ProgressBar den Stand der DB-Arbeit darstellen soll, dann reicht vielleicht ein Thread,
der ist so nebenläufig wie es nur geht, nebenläufig-besser wird es auch durch einen zweiten Thread nicht unbedingt,

alle X von insgesamt N fertigen Kommandos (oder schlicht bei jedem Schleifendurchlauf)
könnte der SQL-Thread die ProgressBar updaten,

dabei dann gegebenenfalls auf Platform.runLater() achten,
aber Task mit call() geht wohl auch, falls der DB-Code so nah an der GUI stehen soll und nicht in einen unabhängigen Thread,

ob im Task gesleep()t wird oder DB-Arbeit, das kommt fast aufs Gleiche hinaus

aber testBar.progressProperty().bind(task.progressProperty());
ist eben ein weiterer hochspezifischer Befehl genau für diese Aufgabe,
den kann man doch nur kopieren, kaum selber drauf kommen?

Das ganze nennt sich Bindings (gibt ein ganzes package in JavaFX mit dem Kram) und damit muss man sich schon separat auseinandersetzen damit, das lohnt sich aber. Es gibt auch auf der Tutorial-Seite von JavaFX tolle Erklärungen dazu. Die Felder jeglicher JavaFX-Klassen sind nur noch Properties, die die eigentlichen Werte „verwalten“ und mögliche Veränderungen beobachten. Es gibt Properties für primitive Werte wie int, long, double, boolean usw. und natürlich eine ObjectProperty.

ObjectProperty<Set<String, Krempel>> krempelSet = new SimpleObjectProperty(new Set<String, Krempel>());

Jetzt kann man das Verhalten seiner Komponenten an solche Properties „binden“. Es ist im Prinzip ein gewrappter Listener, man schreibt halt etwas weniger Code:

BooleanExpression be = textField.textProperty().isNotEqualTo("");
irgendeinElement.visibleProperty().bind(be);

Ich wollte ja eigentlich was zu den Threads in JavaFX sagen. Der von SlaterB gebrachte Einwand

ist eben ein weiterer hochspezifischer Befehl genau für diese Aufgabe,

ist vollkommen richtig, den gibt es nur in der ProgressBar. Denn normalerweise gilt, das man mit Task und Service (die in JavaFX zuständigen Klassen für Nebenläufigkeit) nicht direkt an der UI rumschrauben kann.
Das sollte man dann halt mit den besagten Properties lösen, also Property deklarieren (z.B. StringProperty), textProperty eines Labels an diese StringProperty binden und aus dem Thread nur die StringProperty „bearbeiten“. Task und Service unterscheiden sich, soweit ich das verstanden habe, nur in ihrer Wiederverwendbarkeit. Direkte Manipulationen an der UI, die in einem Thread laufen müssen oder sollen, besser mit Platform.runLater() bewerkstelligen.

So, ich habe jetzt folgendes probiert:

 final static Task<Void> task = new Task<Void>() {
            public Void call() throws Exception {
                for (double i=0; i<=1; i+=0.01) {
                    updateProgress(i, 1);
                    Thread.sleep(100);
                    System.out.println("In Thread: "  + i);
                }
                return null;
            }
        };

Und in meiner Methode die beim Drücken eines Buttons vom ProfilController gestartet wird:

demo.ProfileController.testBar = new ProgressBar();   
        demo.ProfileController.testBar.progressProperty().bind(task.progressProperty()); 
        new Thread(task).start();

mein Problem ist aber nach wie vor, der ProgressBar wird nicht upgedated… :grr:

was heißt ‘nach wie vor’, deine letzte Meldung vom 21.8. ging doch, oder?

updateProgress(i, 1);
mit Kommazahlen von 0 bis 1, funktioniert das?
war früher/ ist in Beispielen nicht eher was von 1-100 zu finden?
genau nachzuschlagen/ auszuprobieren

gehe etwa wieder auf den Stand von Posting #7 zurück, 1-50 geht dort wie zu erwaten?
geht auch von 0-5 in 0.5er Schritten, genau 10 Stufen zu erkennen bei ausreichender Pause?

evtl. einfach mit *100 hochrechnen

Stimmt, sorry… habe da unbewusst eine Kleinigkeit geändert gehabt und war dann der Meinung es hat nicht funktioniert!