user3810626
user3810626

Reputation: 7679

JavaFX update screen

I'm trying to show the process of a maze being generated on screen, but the screen never updates until the process is complete. I'm using Consumers to notify the JavaFX side. I've tried using Platform.runLater() to no avail.

Here are the files in use:

"hello-view.fxml"

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.StackPane?>
<StackPane onMouseClicked="#randomize" fx:id="sp" xmlns:fx="http://javafx.com/fxml" fx:controller="com.example.demo2.HelloController">
</StackPane>

Here's a truncated version of the maze generator, "BasicMaze.java":

package com.example.demo2;
import java.util.*;
import java.util.function.Consumer;

public class BasicMaze {
    public int width;
    public int height;

    private transient List<Consumer<BasicMaze>> consumers = new ArrayList<>();

    public void reset(int w, int h) {
        width = w;
        height = h;
        alertConsumers();
    }

    public void addConsumer(Consumer<BasicMaze> l) {
        if (consumers == null) consumers = new ArrayList<>();
        consumers.add(l);
    }

    public void alertConsumers() {
        consumers.forEach(c -> c.accept(this));
    }

    public void generate() {
        int slots = width * height;

        do {
            if (Math.random() * 10 < 1) {
                alertConsumers(); //Platform.runLater tried here
                slots--;
            }
        } while (slots > 1);
        System.out.println("DONE!");
    }
}

Here's the Controller, "HelloController.java":

package com.example.demo2;

import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;

public class HelloController {
    @FXML
    StackPane sp;

    final Canvas canvas = new Canvas(250,250);
    BasicMaze maze = new BasicMaze();

    @FXML
    public void initialize() {
        sp.getChildren().add(canvas);
        maze.addConsumer(basicMaze -> this.draw()); //platform.runLater tried here.
    }

    public void randomize() {
        maze.reset(5, 5);
        maze.generate();
    }

    public void draw() {
        GraphicsContext gc = canvas.getGraphicsContext2D() ;
        gc.setLineWidth(1.0);
        gc.setFill(Color.BLACK);
        for (int j = 0; j < maze.height; j++) {
            for (int i = 0; i < maze.width; i++) {
                gc.moveTo(Math.random()*250, Math.random()*250);
                gc.lineTo(Math.random()*250, Math.random()*250);
                gc.stroke();
            }
        }

    }

}

And here's the main function stub:

package com.example.demo2;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;

public class HelloApplication extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 320, 240);
        stage.setScene(scene);
        stage.show();
    }

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

If you run this and click on the screen, "DONE!" will appear on the console, there'll be a lag, then the screen will present a bunch of black lines. I'm trying to get the screen to update as each line is drawn.

Upvotes: 1

Views: 684

Answers (1)

user3810626
user3810626

Reputation: 7679

OK, this actually turns out to be super easy (barely an inconvenience!), which I sussed out from some of Sergey Grinev's posts. All that has to be changed (almost) is to run the background task on its own thread.

So, we wrap the maze.generate() in a thread:

public void randomize() {
    maze.reset(500, 500);
    new Thread(maze::generate).start();
}

Now the runLater in the initialize will work!

@FXML
public void initialize() {
    sp.getChildren().add(canvas);
    maze.addConsumer(basicMaze -> Platform.runLater(this::draw));
}

The underlying object doesn't need to change at all.

Upvotes: 1

Related Questions