0

I would like to use a TextArea to log messages about what happend after each iteration. Currently all logs are apend to the end of the loop.

@FXML
private TextArea logBookTextArea;

for (Input x : inputList) {

    logBookTextArea.appendText("Czytam " +i+" produkt");
    id = dataDownloader.downloadID(x.getProducer_code());
    writer.print(x.getProduct_code() + ';');
    writer.print("0;");
    writer.print(producentChoiceBox.getSelectionModel().getSelectedItem().toString().toUpperCase() + ";");
    writer.print(x.getProducer_code() + ";");
    writer.print(x.getName() + ";");
    writer.print("Import - bez kategorii;");
    writer.print("4.6;");
    writer.print("20;");
    writeImages(id);
    logBookTextArea.appendText("OK");
    logBookTextArea.appendText("\n");
    i++;
}
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
Logan_on
  • 117
  • 4
  • 11
  • can you be a bit more specific about the difference on **after each iteration** and **end of the loop** – Martin Frank Oct 25 '19 at 08:26
  • 4
    The GUI cannot be redrawn until some time after the loop has completed; this is why all the text is appearing at once. If you want to "loop" on the _JavaFX Application Thread_ while still allowing other actions to take place you need to use the animation API. – Slaw Oct 25 '19 at 08:35
  • @MartinFrank i understand iteration as a single loop execution. – Logan_on Oct 25 '19 at 09:07
  • @Slaw u can speak some more how i should implement this api ? – Logan_on Oct 25 '19 at 09:09

2 Answers2

3

I assume you want to create something like a "async call" because you want to append Data each time it is done with retrieving the values of the list Object.

What you could do. Is starting a thread around the loop and Put the UI changes in a Platform.runLater(...)

ExecutorService ex = Executors.newSingleThreadExecutor(r ->{
        Thread t = Executors.defaultThreadFactory().newThread(r);
        t.setDaemon(true);
        return t;
    });

    ex.execute(()-> {
    int i = 0;
    for (Input x : inputList) {
        int index= i;

        //must be declareid here because of lambda rules
        //or put in a container that is effectively final
        Int id = dataDownloader.downloadID(x.getProducer_code());
        writer.print(x.getProduct_code() + ';');
        writer.print("0;");
        writer.print(producentChoiceBox.getSelectionModel().getSelectedItem().toString().toUpperCase() + ";");
        writer.print(x.getProducer_code() + ";");
        writer.print(x.getName() + ";");
        writer.print("Import - bez kategorii;");
        writer.print("4.6;");
        writer.print("20;");
        writeImages(id);
        Platform.runLater(()->{
           logBookTextArea.appendText("Czytam " +index+" );
           logBookTextArea.appendText("OK");
           logBookTextArea.appendText("\n");

        });
        I++
    }
});
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
Luxusproblem
  • 1,913
  • 11
  • 23
  • 3
    Unless you're scheduling multiple executions, I don't recommend using a `ExecutorService`. A `Thread` would be sufficient. For `ExecutorService`s you need to make sure that they shut down before/when the application exits either by making sure the threads are daemons or by explicitly shutting down the service. – fabian Oct 25 '19 at 14:43
1

I would suggest you take @Slaw's advice. Use a Timeline.

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 *
 * @author blj0011
 */
public class JavaFXApplication374 extends Application
{
    TextArea textArea = new TextArea();
    List<String> inputList = new ArrayList();
    AtomicInteger counter = new AtomicInteger();

    @Override
    public void start(Stage stage)
    {

        //Add fake data to the list
        for (int i = 0; i < 1000; i++) {
            inputList.add("word ");
        }

        Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(.5), (ActionEvent event) -> {
            textArea.appendText(inputList.get(counter.getAndIncrement()));
        }));
        timeline.setCycleCount(inputList.size());
        timeline.play();

        stage.setScene(new Scene(textArea));
        stage.show();

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }

}

This is a duplicate question at its core. See

SedJ601
  • 12,173
  • 3
  • 41
  • 59
  • 2
    I do wonder what the OP is actually trying to do. This works if the goal is simply to log to a `TextArea` over time, which is what my comment assumed. However, the OP also has a bunch of `writer.print` calls and, assuming they're I/O, using a background thread would make more sense (with posts back to the FX thread). – Slaw Oct 25 '19 at 20:19