0

I am creating a GUI that is running a program that runs tests and writes their output as text to the console. I created a table the allows the user to select the tests they want to run, when the user clicks "run" then it goes through the table and runs the tests selected and is supposed to write the output to a textArea. When I run the program the textArea wouldn't update until it has run all the tests but I need it to update as the test outputs the text.

From what I've read I need to create multiple Threads because running the program and writing to the textArea are both processes. I don't really have a solid grasp of how the Threading works but I've tried using a StringBuffer so the output of the test can be stored and used by the second Thread I created.

public void runTest(ArrayList<String> arr) throws InterruptedException{
        StringBuffer sb = new StringBuffer();

        Thread t = new Thread(() -> {
            try {
                ProcessBuilder builder = new ProcessBuilder(arr);
                builder.redirectErrorStream(true);
                Process p = builder.start();
                BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
                String line;
                while((line = r.readLine()) != null){
                    sb.append(line).append("\n");
                }  
                System.out.println(line);
            } catch (IOException ex) {
                Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
            }
        });

        Thread t2 = new Thread(()->{
            String line = sb.toString();
            System.out.println(line);
            txtOutputArea.appendText(line + "\n");
        });

        t.start();
        t2.start();

        t.join();
        t2.join();

    }

I'm printing the text to the console and it works but for some reason there is no output to the textArea.

HonestAbe
  • 3
  • 2
  • Possible duplicate of [How to wait for thread to finish with .NET?](https://stackoverflow.com/questions/1584062/how-to-wait-for-thread-to-finish-with-net) – Pedro Rodrigues Mar 28 '19 at 17:36

2 Answers2

1

Do it in one thread like so move txtOutputArea.appendText(line + "\n"); inside your while loop in Thread One and wrap it in a Platform.runlater so it doesn't throw a not on main thread exception like so.

private ExecutorService executorService = Executors.newSingleThreadExecutor();

public void runTest(ArrayList<String> arr) throws InterruptedException{
    Thread t = new Thread(() -> {
        try {
            ProcessBuilder builder = new ProcessBuilder(arr);
            builder.redirectErrorStream(true);
            Process p = builder.start();
            BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line;
            while((line = r.readLine()) != null){
                //sb.append(line).append("\n");//Remove this if thats all you were using it for
                Platform.runLater(()->txtOutputArea.appendText(line + "\n"));
            }
            System.out.println(line);//Move this inside the loop if you want it to print th output to the console
        } catch (IOException ex) {
            Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
        }
    });
    executorService.submit(t);
}

To add to this like @Slaw said to fix that issue if you use a newSingleThreadExecutor this will cause everything that you submit to this service to be acted upon like a queue so if you call runTest and then run whatever your second test is and submit it to the executorService(Like the first one). It will have to wait because there is only on worker thread in the executorService.

Matt
  • 3,052
  • 1
  • 17
  • 30
  • When I only need to call the test once then it works fine. The problem is when I call it multiple times, it doesn't output the lines in the in the correct order. For example, if I just select test 1, it would loop through the table see test 1 is selected then call this function, run the test, and output the result to the text area. If I select tests 1 and 2 it should run test 1 first, and display those results, then run test two and display those results. What is happening is it is displaying the results for the test in a mixed order. – HonestAbe Mar 28 '19 at 23:43
  • @HonestAbe Use a single-threaded `ExecutorService` and simply queue the tasks. As there is only one thread the tasks will be executed sequentially. – Slaw Mar 29 '19 at 05:44
  • @Slaw and HonestAbe I have made some edits take a look let me know what you think – Matt Mar 29 '19 at 13:12
-1

As an aside note: Please be aware that JavaFX TextArea will perform absolutely horrible if the text in your TextArea is too long. So if your output could become several thoundands of lines long, this will not work (due to the fact that TextAre is not virtualized).

rli
  • 1,745
  • 1
  • 14
  • 25
  • There is RichTextFX and there is e(fx)clipse. AFAIK both have texts in a virtualized container which will at least prevent that the scene graph collapses. – rli Apr 03 '19 at 05:09