【问题标题】:Updating javafx textArea elment using separated thread or task使用单独的线程或任务更新 javafx textArea 元素
【发布时间】:2020-06-03 12:47:50
【问题描述】:

我正在尝试立即更新 javafx textArea 元素内的文本以使用线程和任务显示执行信息,但似乎没有任何效果,尽管当我在控制台中打印某些内容时它可以工作,因此线程正在执行。一旦程序执行,程序就会打印所有消息,但我希望在程序执行的同时显示消息。

这里有我的 tsak 和线程声明

    @Override
public void initialize(URL url, ResourceBundle rb) {
    System.setProperty("webdriver.gecko.driver", "C:\\Users/lyesm/Downloads/geckodriver-v0.26.0-win64/geckodriver.exe");
    try {
        restoreValues();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    text = new Text(this.getLogs());



    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            Runnable updater = new Runnable() {
                @Override
                public void run() {
                    printMessages();
                    System.out.println(" working on ... \n");

                }
            };

            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                }
                //Platform.runLater(updater);

            }
        }

    });
    thread.setDaemon(true);
    thread.start();

    service = new Service<Void>() {
        @Override
        protected Task<Void> createTask() {
            return new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    Platform.runLater(() -> textArea.appendText(logs));
                    return null;
                }
            };
        }
    };

    service.start();

}

我正在通过这个方法调用服务

 public void launchTest() {
    this.setLogs("\n\n");
    service.restart();
    this.setLogs("   Test starting ...\n");
    service.restart();
    //this.setLogs("   Opening the navigator \n");
    this.setDriver(new FirefoxDriver());
    //this.setLogs("   Reaching http://127.0.0.1:8080/booksManager ... \n");
    driver.get("http://127.0.0.1:8080/booksManager");
    //this.setLogs("   Setting test data \n");
    driver.findElement(By.id("lyes")).click();
    driver.findElement(By.name("email")).sendKeys(pseudo.getText());
    driver.findElement(By.name("password")).sendKeys(password.getText());
    //this.setLogs("   Submitting ... \n");
    driver.findElement(By.name("submit")).click();
    if(driver.getCurrentUrl().equals("http://127.0.0.1:8080/booksManager/Views/index.jsp") == true) {
        //InputStream input= getClass().getResourceAsStream("https://w0.pngwave.com/png/528/278/check-mark-computer-icons-check-tick-s-free-icon-png-clip-art-thumbnail.png");
        //Image image = new Image(input);
        //ImageView imageView = new ImageView(image);
        Label label = new Label("   Test successed");
        testsInfos.getChildren().add(label);
    }else {
        Text textRes = new Text("\n  Test failed  ");
        textRes.setFill(javafx.scene.paint.Color.RED);
        testsInfos.getChildren().add(textRes);
    }
    driver.close();

}

这里是线程调用的 printMessage 方法

public void printMessages() {
    String ll = this.getLogs();
    this.text.setText(ll);
    testsInfos.getChildren().remove(text);
    testsInfos.getChildren().add(text);
    textArea.clear();
    textArea.setText(ll);
}

这两种方法似乎都不起作用。

有人知道如何解决吗?

已编辑:

   package application;


import java.util.concurrent.CountDownLatch;
import javafx.application.Application;
import javafx.application.Platform; 
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class Main extends Application {


private Service<Void> service;



@Override
public void start(Stage primaryStage) throws InterruptedException {
    StackPane root = new StackPane();
    TextArea ta = new TextArea();
    ta.setDisable(true);
    root.getChildren().add(ta);
    Scene scene = new Scene(root, 200, 200);

    // longrunning operation runs on different thread
    /*Thread thread = new Thread(new Runnable() {

        @Override
        public void run() {
            Runnable updater = new Runnable() {

                @Override
                public void run() {
                    incrementCount();
                }
            };

            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                }

                // UI update is run on the Application thread
                Platform.runLater(updater);
            }
        }

    });
    // don't let thread prevent JVM shutdown
    thread.setDaemon(true);
    thread.start();*/

    primaryStage.setScene(scene);
    primaryStage.show();
    service = new Service<Void>() {
        @Override
        protected Task<Void> createTask() {
            return new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    final CountDownLatch latch = new CountDownLatch(1);
                    Platform.runLater(new Runnable() {                          
                        @Override
                        public void run() {
                            try{
                                ta.appendText("\n Printed ");
                            }finally{
                                latch.countDown();
                            }
                        }
                    });
                    latch.await();
                    return null;
                }
            };
        }
    };

    service.start();
    showIT();
}

public static void main(String[] args) {
    launch(args);
}

public void showIT() throws InterruptedException {
    service.restart();
    for(int i = 0;i<1000000;i++) {
        System.out.println(i);
    }
    for(int i = 0;i<1000000;i++) {
        System.out.println(i);
    }
    service.restart();
    for(int i = 0;i<1000000;i++) {
        System.out.println(i);
    }
    for(int i = 0;i<1000000;i++) {
        System.out.println(i);
    }
    service.restart();
}

}

【问题讨论】:

  • 这能回答你的问题吗? JavaFX working with threads and GUI
  • 不,它不会,它会在程序执行后打印所有消息。
  • 你能创建一个minimal reproducible example吗?有许多不同的事情可能导致这不起作用......没有一个可重复的例子,我们只是在猜测。
  • @James_D 我更新了我的帖子,见下文编辑,此代码在完成后打印所有消息,就像在我的初始程序中一样。
  • 嗯,是的,它会的;您正在使用 Thread.sleep() 阻塞 JavaFX 应用程序线程,因此它无法呈现 UI。

标签: multithreading javafx


【解决方案1】:

JavaFX 中的两条线程规则是:

  1. 不得在 FX 应用程序线程上执行长时间运行的代码,并且
  2. 任何更新 UI 的代码都必须在 FX 应用程序线程上执行。

第一条规则的原因是 FX 应用程序线程负责渲染 UI(除其他外)。因此,如果您在该线程上执行长时间运行的任务,您将阻止 UI 呈现,直到您的任务完成。这就是为什么您只在一切完成后才能看到更新:您在 FX 应用程序线程上运行长时间运行的代码,防止它重新渲染文本区域,直到一切完成。

相反,您在后台线程上运行的代码(通过Task.call() 方法) 不会执行任何需要很长时间才能运行的操作:

@Override
protected Void call() throws Exception {
    final CountDownLatch latch = new CountDownLatch(1);
    Platform.runLater(new Runnable() {                          
        @Override
        public void run() {
            try{
                ta.appendText("\n Printed ");
            }finally{
                latch.countDown();
            }
        }
    });
    latch.await();
    return null;
}

您在这里唯一要做的就是在 FX 应用程序线程上安排更新;对Platform.runLater() 的调用立即退出。根本没有长时间运行的代码,因此运行它的后台线程没有任何意义。 (从技术上讲,对latch.await() 的调用是一个阻塞调用,但无论如何它都是多余的,因为您只需在等待后退出该方法。)使用此任务实现,调用service.restart();ta.appendText("\n Printed"); 之间没有区别。

因此,您的showIT() 方法应该在后台线程上调用,并且可以使用Platform.runLater() 将文本附加到文本区域。比如:

import java.util.concurrent.CountDownLatch;
import javafx.application.Application;
import javafx.application.Platform; 
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class Main extends Application {


    private Service<Void> service;



    @Override
    public void start(Stage primaryStage) throws InterruptedException {
        StackPane root = new StackPane();
        TextArea ta = new TextArea();
        ta.setDisable(true);
        root.getChildren().add(ta);
        Scene scene = new Scene(root, 200, 200);


        primaryStage.setScene(scene);
        primaryStage.show();

        // run showIT() on a background thread:
        Thread thread = new Thread(this::showIT);
        thread.setDaemon(true);
        thread.start();
    }

    public static void main(String[] args) {
        launch(args);
    }

    public void showIT() {
        try {
            Platform.runLater(() -> ta.appendText("\nPrinted"));
            Thread.sleep(1000);

            Platform.runLater(() -> ta.appendText("\nPrinted"));
            Thread.sleep(1000);

            Platform.runLater(() -> ta.appendText("\nPrinted"));
            Thread.sleep(1000);
        } catch (InterruptedException exc) {
            Thread.currentThread().interrupt();
        }

    }

}

对于您的原始代码,我必须猜测您使用的 API 的哪些部分是长期运行的,哪些不是。我将首先创建一个实用程序log() 方法,您可以从任何线程调用它:

private void log(String message) {
    Runnable update = () -> ta.appendText(message);
    // if we're already on the FX application thread, just run the update:
    if (Platform.isFxApplicationThread()) {
        update.run();
    }
    // otherwise schedule it on the FX Application Thread:
    else {
        Platform.runLater(update);
    }
}

现在您可以执行以下操作:

public void launchTest() {
    log("\n\n");
    log("   Test starting ...\n");
    log("   Opening the navigator \n");

    Task<Boolean> task = new Task<>() {
        @Override
        protected Boolean call() throws Exception {
            this.setDriver(new FirefoxDriver());
            log("   Reaching http://127.0.0.1:8080/booksManager ... \n");
            driver.findElement(By.name("email")).sendKeys(pseudo.getText());
            driver.findElement(By.name("password")).sendKeys(password.getText());

            driver.get("http://127.0.0.1:8080/booksManager");
            log("   Setting test data \n");
            driver.findElement(By.id("lyes")).click();

            log("   Submitting ... \n");
            driver.findElement(By.name("submit")).click();

            boolean result = driver.getCurrentUrl().equals("http://127.0.0.1:8080/booksManager/Views/index.jsp");
            driver.close();
            return result ;
        }
    };

    task.setOnSucceeded(e -> {
        if (task.getValue()) {

            //InputStream input= getClass().getResourceAsStream("https://w0.pngwave.com/png/528/278/check-mark-computer-icons-check-tick-s-free-icon-png-clip-art-thumbnail.png");
            //Image image = new Image(input);
            //ImageView imageView = new ImageView(image);
            Label label = new Label("   Test successed");
            testsInfos.getChildren().add(label);
        } else {
            Text textRes = new Text("\n  Test failed  ");
            textRes.setFill(javafx.scene.paint.Color.RED);
            testsInfos.getChildren().add(textRes);
        }
    });

    Thread thread = new Thread(task);
    thread.setDaemon(true);
    thread.start();

}

【讨论】:

    猜你喜欢
    • 2012-10-03
    • 1970-01-01
    • 2018-05-24
    • 2012-07-08
    • 2013-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-26
    相关资源
    最近更新 更多