2

for days now i am trying to update a Javafx userinterface while sending 2 or more E-Mails automatically. The UI should update a Label with "connecting", "sending" and "sent". I've read about Runnables, Tasks, TimeLine but i dont really understand which method i should use and how it works.

Everywhere i read about Platform.runLater(), I used it but the animation in my JavaFX Gui freezes and the Label only changes, when all E-Mails have been sent.

I will update this post with my code soon, but can someone already tell me what kind of Thread i should use and how the ui can be updated easily?

//edit

thanks for your help, Branislav but now im getting the "Not on FX application thread"-Error. The following code is not my "extends Application"-class, please take a look.

public class ControllerOCM implements Initializable{



    @FXML
    private ComboBox<String> comboEmpf;

    @FXML
    private Label lblStatus;

    @FXML
    private Label lblStatus1;


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        hint.setText("some hint");
    }

    public void sende(ActionEvent event) throws Exception {

        switch (comboEmpf.getValue()) {
        case "A": System.out.println("A");break;
        case "B":
        File fTEST = new File("C:\\B\\");
        File[] fpathTEST = fTEST.listFiles();
        String[] fnameTEST = fTEST.list();

    for (int i=0; i<fpathTEST.length; i++) {
        SendenTask sendTEST = new SendenTask(mail@address.com,
                "bodycontent",
                "subject", 
                fpathTEST[i].toString(),    
                fnameTEST[i],               
                i,                      //count
                fpathTEST.length);      //max

        new Thread(sendTEST).start();
    }
            break;
        }
    }

     public class SendenTask extends Task<Void> {
            private String adr;
            private String body;
            private String subj;
            private String fp;
            private String fn;
            private int count;
            private int max;

         public SendenTask(String adr, String body, String subj, String fp, String fn, int count, int max) {
            this.adr = adr;
            this.body = body;
            this.subj = subj;
            this.fp = fp;
            this.fn = fn;
            this.count = count;
            this.max = max;

         }

         @Override 
         protected Void call() throws Exception {

         lblStatus1.setText("connecting");
         //doing connectionAction

         lblStatus1.setText("sending");
         updateMessage("sending");
         //doing sendingthingys


         //sending complete
         lblStatus1.setText("sent");
         System.out.println("done");
         updateMessage("done");

             return null;
         }
     }
}

//edit2

Now the programm is sending Emails one after another but the textproperty is not working correctly. It is not refreshing the text, it only emptys the label. If i remove the unbind-line (in succeed) it only works one time i run the send task. After that it is empty again for all other send tasks.

public class ControllerOCM implements Initializable{



        @FXML
        private ComboBox<String> comboEmpf;

        @FXML
        private Label lblStatus;

        @FXML
        private Label lblStatus1;


        @Override
        public void initialize(URL location, ResourceBundle resources) {
            hint.setText("some hint");
        }

        public void sende(ActionEvent event) throws Exception {

            switch (comboEmpf.getValue()) {
            case "A": System.out.println("A");break;
            case "B":
            File fTEST = new File("C:\\B\\");
            File[] fpathTEST = fTEST.listFiles();
            String[] fnameTEST = fTEST.list();

        for (int i=0; i<fpathTEST.length; i++) {
            SendenTask sendTEST = new SendenTask(mail@address.com,
                    "bodycontent",
                    "subject", 
                    fpathTEST[i].toString(),    
                    fnameTEST[i],               
                    i,                      //count
                    fpathTEST.length);      //max

              lblStatus1.textProperty().bind(sendTEST.messageProperty());
              thread = new Thread(sendTEST);
              thread.start();   
              }
              break;
            }
        }

         public class SendenTask extends Task<Void> {
                private String adr;
                private String body;
                private String subj;
                private String fp;
                private String fn;
                private int count;
                private int max;

             public SendenTask(String adr, String body, String subj, String fp, String fn, int count, int max) {
                this.adr = adr;
                this.body = body;
                this.subj = subj;
                this.fp = fp;
                this.fn = fn;
                this.count = count;
                this.max = max;

             }

             @Override 
             protected Void call() throws Exception {

             while(!sending) {
             sending = true

             updateMessage("connecting");
             //doing connectionAction


             updateMessage("sending");
             //doing sendingthingys


             //sending complete
             updateMessage("done");
                 }
                 return null;
             }
     @Override
     protected void succeeded() {
         super.succeeded();
         lblStatus1.textProperty().unbind();
         sending = false;
         }
       }

    }

//edit3 The big question is, where do i put the lblStatus1.textProperty().bind(sendTEST.messageProperty()); and where the lblStatus1.textProperty().unbind(); ?

Simsala
  • 77
  • 1
  • 2
  • 8

1 Answers1

3

You have to use Task class.

Here is a small demo. Let's simulate time consuming task via for loop:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;


public class TaskDemo extends Application {


    @Override
    public void start(Stage stage) throws Exception {
        final Label label = new Label();

        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                boolean fxApplicationThread = Platform.isFxApplicationThread();
                System.out.println("Is call on FXApplicationThread: " + fxApplicationThread);
                // Run your time consuming task here!
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(1000);
                    updateMessage("Time elapsed: " + i);
                }
                return null;
            }

            @Override
            protected void succeeded() {
                boolean fxApplicationThread = Platform.isFxApplicationThread();
                System.out.println("Is call on FXApplicationThread: " + fxApplicationThread);
                super.succeeded();
                label.textProperty().unbind();
                label.setText("Task done");
            }
        };
        // Bind messageProperty of task for textProperty of Label
        label.textProperty().bind(task.messageProperty());
        stage.setScene(new Scene(label, 300, 200));
        stage.show();
        new Thread(task).start();

    }
}

Things are simple. Your time consuming task will freeze UI until it finishes. By using Task class, your time consuming task runs on background thread leaving JavaFX thread responsive. Occasionally, you can "publish" results of your task from call method. DO NOT update your JavaFX UI on background thread (in call method). By calling Platform.isFxApplicationThread() you can check whether your code runs on FXApplicationThread. If your code is not running on that thread, do not update your JavaFX UI from there!

Branislav Lazic
  • 14,388
  • 8
  • 60
  • 85
  • @Simsala And what did I said where "time consuming" task is running? – Branislav Lazic Aug 04 '15 at 13:22
  • thank you very much for your help till now. I just managed to get things running, did a Property binding to my Label and my animation is running while sending E-Mails BUT the task currently sends E-Mails simultaneously but i want it one after another. I tried thread.join() but if I use that, my UI freezes again. Is there any solution? – Simsala Aug 04 '15 at 13:52