0

I am trying to create my own code editor with the help of javafx. And now I want to implement a code runner in my project, but I got stucked and don't know how to implement this.

But before asking this question I have tried this

    private void runCommand(String cmd) {
        ProcessBuilder builder = new ProcessBuilder("powershell.exe", "/c", cmd);
        builder.redirectErrorStream(true);
        console.setText("");
        try {
            Process p = builder.start();
            BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line;
            while (true) {
                line = r.readLine();
                if (line == null) { break; }
                console.appendText(line+'\n');
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

But the problem is console.appendText(line+'\n'); will not executed until the process is running. For e.g - I have tried to run a python file which creates a simple pygame window and print "hello world" in every iteration. But the output of this python file is not showing up in the console until we close the pygame window.

So is there any other way for creating a code runner.

1 Answers1

0

Run your command using a separate thread using a JavaFX Task (search for JavaFX concurrency).

Append the result using a platform runLater so you don’t update the JavaFX scene graph off the JavaFX app thread.

Your problem is that you are hanging the UI thread, so no screen updates or events can occur.

Example

import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

import java.io.*;

public class ProcessApp extends Application {

    private static final VBox logs = new VBox(10);

    @Override
    public void start(Stage stage) throws IOException {
        TextField cmd = new TextField();
        cmd.setPromptText("Enter a command");

        Button run = new Button("Run");
        run.setDefaultButton(true);
        run.setDisable(true);
        run.setOnAction(e -> runCommand(cmd.getText()));
        run.disableProperty().bind(cmd.textProperty().isEmpty());

        HBox commandInput = new HBox(10, cmd, run);
        HBox.setHgrow(cmd, Priority.ALWAYS);

        ScrollPane scrollableLogs = new ScrollPane(logs);
        scrollableLogs.setFitToWidth(true);
        scrollableLogs.setPrefSize(800, 400);

        VBox layout = new VBox(10, commandInput, scrollableLogs);
        layout.setPadding(new Insets(10));

        stage.setTitle("Process runner");
        stage.setScene(new Scene(layout));
        stage.show();
    }

    private void runCommand(String cmd) {
        String name = "Executing: " + System.currentTimeMillis() + ": " + cmd;

        TextArea logView = new TextArea();
        logView.setPrefHeight(100);

        logs.getChildren().addAll(new Label(name), logView);

        Task<Void> processTask = new Task<>() {
            @Override
            protected Void call() throws IOException {
                Process process = new ProcessBuilder()
                        .command("powershell.exe", "/c", cmd)
                        .redirectErrorStream(true)
                        .start();

                try (
                        BufferedReader r = new BufferedReader(
                                new InputStreamReader(
                                        process.getInputStream()
                                )
                        )
                ) {
                    String line;

                    while ((line = r.readLine()) != null) {
                        final String nextLine = line;
                        Platform.runLater(() -> logView.appendText(nextLine + "\n"));
                    }
                }

                return null;
            }
        };

        processTask.setOnSucceeded(event ->
            logs.getChildren().add(
                    new Label("Process execution completed successfully")
            )
        );

        processTask.exceptionProperty().addListener((observable, oldValue, newValue) -> {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            processTask.getException().printStackTrace(pw);

            logs.getChildren().add(
                    new Label("Process execution exception: " + sw)
            );
        });

        Thread processThread = new Thread(processTask, name);
        processThread.setDaemon(true); // quitting the main app will kill the thread.
        processThread.start();
    }

    public static void main(String[] args) {
        launch();
    }
}
jewelsea
  • 150,031
  • 14
  • 366
  • 406