1

So this are my first steps with JavaFX, I'm trying to create a simple desktop application in JavaFx which emulates a real time kind of bar chart, refreshing every second to add the portion of the bar that represents the status of every sensor (off, not working, working ok...). I have to implement some buttons/textfields/menuItems for sending information to both sensor and application.

So I'm using a method which is called from start() method just before adding the canvas to the scene, from where I invoke a TimerTask to draw the graph every second, and it works fine. With this, when I try to set this canvas in the center of a BorderPane, and add the other elements to another part of the pane, the working of them is affected by the delay of the TimerTask, even when this timerTask is supposedly executed by a different thread.

How could I separate the real time drawing from the rest of the application in different threads?? Should I use another class different from "TimerTask"??

The code is something like this:

@Override
public void start(Stage primaryStage) {
    this.primaryStage = primaryStage;
    BorderPane bp = new BorderPane();
    Group root = new Group();

    // create a canvas node
    Canvas canvas = new Canvas();

    // bind the dimensions when the user resizes the window.
    canvas.widthProperty().bind(primaryStage.widthProperty());
    canvas.heightProperty().bind(primaryStage.heightProperty());

    // obtain the GraphicsContext (drawing surface)
    final GraphicsContext gc = canvas.getGraphicsContext2D();

    //Invoke the real time graph drawing
    draw(gc);

    // add the single node onto the border pane
    bp.setCenter(canvas);
    MenuBar bar = new MenuBar();
    Menu sensorsMenu = new Menu("Sensors");
    bar.getMenus().add(sensors);
    for(int i=0; i<nDetect; i++){
        sensorsMenu.getItems().add(new CheckMenuItem("Sensor " + (i+1)));
    }
    //add the menu bar to top
    bp.setTop(bar);
    root.getChildren().add(bp);

    Scene scene = new Scene(root);
    primaryStage.setScene(scene);
    primaryStage.show();
}

public void draw(GraphicsContext gc){
    c = new SerialPortConection();

    c.establishConnection("COM1", 2400, 8, 1, 1);
    LinkedList<String> resp = null;

        .
        .
        .

    timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            javafx.application.Platform.runLater(new Runnable() {
            @Override
            public void run() {
                //Send request to sensor
                try {
                    resp = c.sendStatusRequest();
                } catch (WrongFormatException ex) {
Logger.getLogger(Sensor.class.getName()).log(Level.SEVERE, null, ex);
                }
                //Update sensor status
                for(int i=0; i<nSensors; i++){
                    sensors.get(i).update(resp.get(37+i));
                }

                //Draw sensors status
                for(int i=0; i<nSensors; i++){
                    sensors.get(i).draw(gc);
                }
                //Paint axis
                .
                .
                .
            }
        });
        }
    }, 0, 1000);
}
Juan
  • 1,949
  • 1
  • 15
  • 22
  • 1
    What are resp and sensors? – ItachiUchiha Mar 10 '15 at 10:08
  • "resp" is the response of the serial port request (as a list of hexadecimal string that represent the value of every byte). "sensors" is the list of every sensor, being "sensor" a list of rectangles of different colors which represents the different status of the sensors along time (there is a "sensor" class and a "rectangle" class, but since the "drawing" is working fine, I guess they can stay out of the topic) – Juan Mar 10 '15 at 10:47
  • 1
    Then `resp = c.sendStatusRequest(); `may need some time to execute, since it waits for the request. Isn't it? If yes, you may want to take it out of the `Platform.runLater()`, since you are blocking the UI thread while waiting for the response. By putting it into `Platform.runLater`, it is executing in the same UI thread and not on a different thread. – ItachiUchiha Mar 10 '15 at 11:01
  • Exactly, about 650-700ms to get the response, I didn't know this issue about the Platform.runLater(). Thank you for your reply – Juan Mar 10 '15 at 12:16
  • This is not an issue, but how [`Platform.runLater`](http://docs.oracle.com/javase/8/javafx/api/javafx/application/Platform.html#runLater-java.lang.Runnable-) works. – ItachiUchiha Mar 10 '15 at 12:23
  • My bad, I will use "fact" instead ;) – Juan Mar 10 '15 at 14:52

1 Answers1

0

You could look at this answer:

JavaFX periodic background task

Using a TimerTask is ok. You should use the timer thread (background thread) to call your request to the external service and the JFX thread to update the graphic:

    timer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            // call external service in the background thread
            resp = c.sendStatusRequest();
            javafx.application.Platform.runLater(new Runnable() {
            @Override
            public void run() {
                // update the graphics in the JFX thread
            }
        });
        }
    }, 0, 1000);
Community
  • 1
  • 1
gontard
  • 28,720
  • 11
  • 94
  • 117