Обновление окна Java FX в потоке - PullRequest
0 голосов
/ 22 января 2019

У меня есть клиент-серверное приложение, и у клиента есть два потока, один для отправки информации на сервер, а другой, который должен показывать изменяющееся окно, но я получаю ошибки.Я прочитал кое-что о Platform.runlater, но я не знаю, как его использовать.Вот класс с окном:

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();
}
}

А вот класс, который создает новые потоки:

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();
}
}

, пожалуйста, помогите: <</p>

Ответы [ 3 ]

0 голосов
/ 22 января 2019

Рассмотрите возможность инкапсуляции информации, необходимой клиенту, в отдельный класс (обычно называемый моделью).Клиент (представление) должен реагировать на изменения в модели.В следующем примере это делается связыванием.Теперь вы можете использовать поток для обновления модели:

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) );
    }
}
0 голосов
/ 22 января 2019

Вот краткий пример, чтобы показать вам, как использовать Platform.runLater(()-> ...); Я пытался сделать его похожим на ваше приложение, но мое не использовалось, я также добавил несколько комментариев

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"));
        }
    }
}
0 голосов
/ 22 января 2019

Как правильно заметил @Eggplant, объекты класса Thread не могут манипулировать компонентами пользовательского интерфейса JavaFx, а все компоненты пользовательского интерфейса должны обрабатываться с использованием потока JavaFx.Platform.runLater планирует изменения вашего пользовательского интерфейса в работающем потоке JavaFx.Используйте его следующим образом:

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

Вы можете использовать этот бит кода из других экземпляров Thread для делегирования манипулирования пользовательским интерфейсом реальному потоку JavaFx.

...