我目前正在使用 SceneBuilder 开发 JavaFX 程序,该程序也使用 Windows 命令行。为了向用户表明程序正在执行某些操作,我希望它将控制台输出放入文本区域。到目前为止,它只会在一切完成后更新,而不是实时更新,这是我的目标。
这是我到目前为止的代码,它输入“tree”作为测试。 “textareaOutput”是显示输出的文本区域:
try {
Process p = Runtime.getRuntime().exec("cmd /C tree");
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
textareaOutput.appendText(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
我知道 Swing 中有 MessageConsole,但我不知道 JafaFX 中是否存在类似的东西
最佳答案
基于自定义日志记录框架的解决方案
生成一个线程来执行您的命令,然后使用以下问题的答案:
这将以线程安全的方式将来自生成线程启动的进程的消息记录回 UI。
您可以使用ExecutorService如果您愿意,可以协助线程管理。如果这样做,请记住彻底关闭它(通常在 JavaFX 应用程序的 stop()
方法中)。
基于 JavaFX 并发实用程序的解决方案
您还可以使用JavaFX concurrency utilities ,例如 Task
、Service
和 Platform.runLater
(如果您愿意)。请参阅 Task
java documentation 的“返回部分结果的任务”或“修改场景图的任务”部分。有关此方法的更多信息。
使用 JavaFX 并发实用程序的示例。
import javafx.application.*;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class ConsoleLogger extends Application {
@Override
public void start(Stage stage) throws Exception {
TextArea outputArea = new TextArea();
outputArea.setStyle("-fx-font-family: monospace");
outputArea.setEditable(false);
TextField commandField = new TextField();
commandField.setOnAction(event ->
executeTask(
commandField.getText(),
outputArea
)
);
VBox layout = new VBox(
10,
new Label("Enter a command and press return to execute it."),
commandField,
outputArea
);
VBox.setVgrow(outputArea, Priority.ALWAYS);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
private void executeTask(String commandString, TextArea outputArea) {
CommandExecutor commandExecutor = new CommandExecutor(
commandString,
outputArea
);
new Thread(commandExecutor).start();
}
public class CommandExecutor extends Task<Void> {
private final String commandString;
private final TextArea outputArea;
private final ProcessBuilder processBuilder;
public CommandExecutor(
String commandString,
TextArea outputArea
) {
this.commandString = commandString;
this.outputArea = outputArea;
processBuilder = new ProcessBuilder(
commandString.trim().split("\\s+")
)
.redirectErrorStream(true);
exceptionProperty().addListener((observable, oldException, newException) -> {
if (newException != null) {
newException.printStackTrace();
}
});
}
@Override
protected Void call() throws IOException {
Process process = processBuilder.start();
try (
BufferedReader reader =
new BufferedReader(
new InputStreamReader(
process.getInputStream()
)
)
) {
String line;
while ((line = reader.readLine()) != null) {
final String nextLine = line;
Platform.runLater(
() -> outputArea.appendText(nextLine + "\n")
);
}
}
return null;
}
}
public static void main(String[] args) {
launch(args);
}
}
请注意,这是一个幼稚的解决方案,它会淹没 JavaFX 可运行队列,不包括强大的异常处理,没有线程池,不处理采用交互式输入 I/O 的进程,将并发执行进程的输出合并到单个文本区域,不限制文本区域大小等。特别是,如果生成的进程执行诸如拖尾大型日志文件之类的操作(该日志文件不断快速写入),则需要小心,因为简单的解决方案可能会通过多次调用 Platform.runLater
淹没 JavaFX 可运行队列,这不太好。
为了获得更有效的解决方案,之前链接的自定义日志记录系统可能会更好。然而,对于某些应用程序,本示例中基于任务的系统记录到 TextArea 或对其进行一些小的修改可能没问题。
附加说明
无论如何,请小心不要尝试直接从另一个线程修改文本区域或其任何属性(使用 Platform.runLater
来防止这种情况),否则程序可能会失败由于并发问题。
使用 Java Process API 时可能会发生很多技巧和意外(不良)的事情,因此,如果您不是很精通它,那么请四处搜索一下,看看这些是什么以及如何解决它们您需要一个非常强大的解决方案。
关于java - 如何实时更新 TextArea 的输入?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59491390/