简体   繁体   中英

Refreshing Java FX window in thread

I've got client-server application and the client has two threads, one for sending informations to server and the other, which should show changing window, but i get errors. I've read something about Platform.runlater, but I don't know how to use it. Here's the class with the window:

import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;

public class B implements Runnable{

private int counter = 0, lg;
private double x_1, x_2, y_1, y_2;
int [] bts = new int[2];
Button[] change_bts = new Button[2];
Button [] all_bts = new Button[221];

public void run() {

    Group grupa = new Group();
    int x = 0; 

    int ctr = 1;


    for (int i = 0; i < 17; ++i) {
        for (int j = 0; j < 13; ++j) {
            if ( i%2 == 1)  {  x = 30; }
            else { x = 0; }

            Button bt = new Button();
            bt.setLayoutX(x+30+j*60); bt.setLayoutY(30+i*60);

            bt.setOnAction(
                    event -> { 
                        if((bt.getText()!="0")) {
                            if ((counter == 1)) {
                                change_bts[counter] = bt;
                                bts[0] = Integer.parseInt(change_bts[0].getText());
                                bts[1] = Integer.parseInt(change_bts[1].getText());
                            }
                            else if (counter == 0) {
                                change_bts[counter] = bt;
                                counter++;
                            }
                        }
                        System.out.println(bt.getText());
                    }
            );
            all_bts[ctr-1] =  bt;
            ctr ++;
        }
    }
    grupa.getChildren().addAll(all_bts);
    Scene tsc = new Scene(grupa, 1100, 1200);
    Stage tst = new Stage(); //New window (Stage)
    tst.setScene(tsc);
    tst.show();
}
}

And here is the class, which creates new threads:

import javafx.application.Application;
import javafx.stage.Stage;
import java.io.IOException;

public class Clients extends Application {

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

public void start(final Stage first_stage) throws IOException, InterruptedException {
   Names n = new Names (); // this Class' constructor calls gogo() method below
}
public static void gogo(String a) {
    Runnable[] runners = new Runnable[2];
    Thread[] threads = new Thread[2];

        runners[0] = new B();
        runners[1] = new Another_Class();

        threads[0] = new Thread(runners[0]);
        threads[1] = new Thread(runners[1]);

        threads[0].start();
        threads[1].start();
}
}

please, help :<

As @Eggplant correctly pointed out that Thread class objects are not allowed to manipulate JavaFx UI Components and all UI components must be manipulated using JavaFx thread. What Platform.runLater does is it schedules your UI changes in the running JavaFx thread. Use it like this:

Platform.runLater(() -> {
//your UI manipulation code here...
});

You can actually use this bit of code from other "Thread" instances to delegate UI manipulation to actual JavaFx thread.

Consider encapsulating the information needed by the client in a separate class (typically referred to as model).
The client (the view) should respond to changes in the model. In the following example it does so by binding.
Now you can use a thread to update the model :

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Clients extends Application {

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

    @Override
    public void start(final Stage first_stage) throws IOException, InterruptedException {

        Model model = new Model();
        Button run = new Button("Run");
        run.setOnAction(e -> {
            gogo(model);
            run.setDisable(true);
        });

        Label counter = new Label();
        counter.textProperty().bind(model.getCounterProperty().asString());

        BorderPane root = new BorderPane(counter);
        root.setBottom(run);
        Scene scene = new Scene(root);
        first_stage.setScene(scene);
        first_stage.show();
    }

    private void gogo(Model model) {
        //thread manipulates model 
        new Thread(new Updater(model)).start();
    }
}

class Updater implements Runnable{

    private final Model model;
    private int counter = 0;
    private boolean stop = false;

    public Updater(Model model) {
        this.model = model;
    }

    @Override
    public void run() {
        while (! stop){
            model.setCounter(++counter);
            try {
                TimeUnit.MILLISECONDS.sleep(1000);
            } catch (InterruptedException ex) { ex.printStackTrace();}
        }
    }

    void setStop(boolean stop) {this.stop = stop;   }
}

//use a model to encapsulate information needed for the view 
class Model {

    private final SimpleIntegerProperty counterProperty = new SimpleIntegerProperty(0);

    SimpleIntegerProperty getCounterProperty() { return counterProperty; }

    //use `Platform.runLater` to allow update from non javafx thread 
    //if updated by more than one thread it needs to be synchronized 
    void setCounter(int value) {
        Platform.runLater(() -> counterProperty.set(value) );
    }
}

Here is a quick example to show you how to use Platform.runLater(()-> ...); I tried to keep it similar to your application but mine is dumbed down I added some comments in there as well

public class Main extends Application {

    @Override
    public void start(Stage stage) throws Exception{
        VBox vBox = new VBox();
        vBox.setAlignment(Pos.CENTER);

        for (int i = 0; i < 3; i++) {
            //Create new class with Runnable Implemented
            IncrementingButton button = new IncrementingButton();
            vBox.getChildren().add(button.getButton());
            //Start Thread
            new Thread(button).start();
        }

        stage  = new Stage();
        stage.setScene(new Scene(vBox, 100, 100));
        stage.setAlwaysOnTop(true);
        stage.show();
    }

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

    class IncrementingButton implements Runnable{

        int count = 0;
        private Button button = new Button();

        private Button getButton(){ return button; }

        @Override
        public void run(){
            while (count != 10){
                //Update Button Text
                Platform.runLater(()-> button.setText("Count:"+count));

                //Sleep for variation
                try {
                    Thread.sleep((long)((Math.random() * 3)+1)*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count++;
            }
            //Update Button Text
            Platform.runLater(()-> button.setText("DONE"));
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM