Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
776 views
in Technique[技术] by (71.8m points)

java - JavaFX rectangle doesn't update

I'm trying to do a maze resolver with a cellular automaton but I have a display problem for each new generation of a grid of an automaton we try to display the cells in the form of rectangle.The initialization works well and the grid is displayed,the last generation of the simulation is displayed too but not the intermediate steps.

pic of first generation

last generation

//Initialise la liste des rectangles
    public void initRectList() {

            for(int height = 0; height < this.mazeA.grid.getHeight(); height++) {
                for(int width = 0; width < this.mazeA.grid.getWidth(); width++) {

                this.rectList[height][width] = new Rectangle(REC_HEIGHT,REC_WIDTH, getColorCell(this.mazeA.grid, height, width));
            }
        }
    }

    //Dessine le labyrinthe
    public void drawGrid() {

        for (int height = 0; height < this.mazeA.grid.getHeight(); height++) {
            for(int width = 0; width < this.mazeA.grid.getWidth(); width++) {

                tilePane.getChildren().add(this.rectList[height][width]);

            }
        }
    }
public class MazeFX  {

    private HBox root = new HBox();
    private Scene scene = new Scene(root,1100,800);
    private TilePane tilePane = new TilePane();
    private Grid grid = new Grid(30,30);

    private Rectangle[][] rectList = new Rectangle[30][30];

    private VBox buttons = new VBox();
    private Button reset = new Button("Reset");
    private Button pas = new Button("Play");
    private Button load = new Button("Load");

    private MazeAutomaton mazeA;

    private final double REC_HEIGHT = 20.;
    private final double REC_WIDTH = 20.;


    public MazeFX(Stage stage) throws InterruptedException {

        scene.getStylesheets().add(getClass().getResource("/src/application.css").toExternalForm());
        initButton();
        initLayout();

        initGridByTopologie();

        mazeA = new MazeAutomaton(this.grid);

        initRectList();
        drawGrid();



        pressedButtons(stage);
        setWindow(stage);

        displayWindow(stage);

    }

to start the next generation you press a button.

//Action de l'utilisateur sur l'un des bouttons
    public void pressedButtons(Stage stage) {
        pressReset();
        pressPAS();
        pressLoad(stage);
    }

   //Boutton Play/Stop préssé
    public void pressPAS() {

        this.pas.setOnMouseClicked(e -> {
            for (int i = 0; i < 30; i++) {

                mazeA.nextStep();

                try {
                    Thread.sleep(100);
                } catch (InterruptedException interruptedException) {
                    interruptedException.printStackTrace();
                }
                updateRectColor();

            }
                }
        );
    }

the problem seems to be that we are stuck in the method setOnMouseClicked() and that the update of the rectangles is not done, with a textual display I see the evolution of the automaton which indicates us that the simulation works and that the problem seems to come from JavaFX

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The JavaFX Application Thread runs as a loop. Effectively (the actual implementation details are far more complex) that loop does the following (in pseudocode):

while (applicationIsRunning()) {
    if (thereAreEventsToHandle()) {
        handleEvents();
    }
    if (thereAreAnimationsToUpdate()) {
        updateAnimations();
    }
    if (itsTimeToRenderTheScene()) {
        renderScene();
    }
}

By default, JavaFX renders the scene at most 60 times per second.

The code in your event handler is executed on the FX Application Thread (it's invoked by the first block in the pseudocode loop above). Since it sleeps on that thread, the FX Application Thread never gets to the third part of the loop (rendering the Scene) until the entire event handler completes. Consequently, you never see the intermediate updates, because the scene is never rendered.

Assuming mazeA.nextStep() doesn't block (or take a long time to run), it's best to refactor this as an Animation, e.g. a Timeline:

public void pressPAS() {

    this.pas.setOnMouseClicked(e -> {
        KeyFrame updateMaze = new KeyFrame(Duration.ZERO, evt -> mazeA.nextStep());
        KeyFrame updateRect = new KeyFrame(Duration.millis(100), evt -> updateRectColor());
        Timeline timeline = new Timeline(updateMaze, updateRect);
        timeline.setCycleCount(30);
        timeline.play();
    });
}

The timeline.play() method simply starts the animation and returns immediately, allowing the FX Application Thread to proceed. When the FX Application Thread checks for running animations, it will check if it's time to execute either of the handlers in the key frames, and if so will execute them. Then it will render the scene as usual.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...