1

I am creating a GUI for a Board Game using javaFX via RMI. I have a lot of problems because after having found a way for update my GUI I cannot really update GUI if I don't place a toggle breakpoint in a certain line.

If I start my clients not in debug mode the graphics don't seem to do the update, they just appear and nothing happens anymore.

Also if I do not place the toggle in a specified line of my code, and I place it in another line (deeper in the method calling sequence)nothing happens. So the client thread doesen't stop; as if it don't does the method call that it does with the breakpoint. I start my clients all in the same machine and only one (using toggle break points) gets updated.

Now I will show you my code.

javafx starts here in a clas called RMIGUIView.java

@Override
public synchronized void run() {
    waiting = true;
    pause();
    guiDisplayer = new GUIDisplayer();
    (new Thread(() -> guiDisplayer.startGUI())).start();
    waiting = false;
    do {
        state.acceptView(this);
    } while(!endGame);
}

then I do the start of the GUI (during the start method I save the stage on the controller as it is suggested here Passing Parameters JavaFX FXML

public class GUIDisplayer extends Application {

private StartTurnState currentState;
private Stage stage;

public void start(Stage stage) throws Exception {
    GUIController controller = new GUIController();
    FXMLLoader fxmlLoader = new FXMLLoader();
    Parent root = fxmlLoader.load(getClass().getResource("sceneMap.fxml"));
    stage.setTitle("Council of Four");
    Scene newScene = new Scene(root);
    stage.setScene(newScene);
    stage.show();
    controller.setStage(stage);
}

public static void startGUI() {
    Application.launch();
} }

after start of the gui I return in my RMIGUIView.java and I do this

@Override
public void visit(StartTurnState currentState) {
    GUIController guiController = new GUIController();
    guiController.updateGUI(currentState);
}

and here It is my controller class where I have to put the toggle breakpoint if I want somethig happen (please read at the class comments)

public class GUIController {

@FXML
ArrayList<ImageView> citiesImagesList;
@FXML
Group group;
@FXML
TableView<Player> players;
@FXML
TableColumn<Player, String> playerName;
@FXML
TableColumn<Player, String> victoryPoints;
@FXML
TableColumn<Player, String> coins;
@FXML
TableColumn<Player, String> assistants;
@FXML
TableColumn<Player, String> nobilityPoints;
@FXML
ImageView A;
@FXML
ImageView B;
@FXML
ImageView C;
@FXML
ImageView D;
@FXML
ImageView E;
@FXML
ImageView F;
@FXML
ImageView G;
@FXML
ImageView H;
@FXML
ImageView I;
@FXML
ImageView J;
@FXML
ImageView K;
@FXML
ImageView L;
@FXML
ImageView M;
@FXML
ImageView N;
@FXML
ImageView O;
@FXML
ImageView king;

private static StartTurnState currentState;
private static GUIController self = null;
private static Stage stage;

public GUIController() {
    self = this;
}

private void placeRewardToken(Group group, List<ImageView> citiesImagesList) {
    Map<String, City> citiesMap = GUIController.currentState.getGameMap().getCitiesMap();
    for (ImageView imageView : citiesImagesList) {
        City city = citiesMap.get(imageView.getId().toString());
        if (city instanceof NormalCity) {
            RewardToken rewardToken = ((NormalCity) city).getRewardToken();
            Text text = new Text();
            text.setText(rewardToken.toString());
            text.setX(imageView.getLayoutX() + 5.00);
            text.setY(imageView.getLayoutY());
            text.toFront();
            text.setFont(Font.font("Verdana", 12));
            text.setFill(Color.MAGENTA);
            group.getChildren().add(text);
        }

    }

}

private void setPlayerSet() {
    Platform.setImplicitExit(false);
    Scene scene = stage.getScene();
    ObservableList<Player> playersList = FXCollections.observableArrayList();
    playersList.addAll(GUIController.currentState.getPlayerSet().getPlayers());
    TableView<Player> playerTable = (TableView<Player>) scene.lookup("#players");
    TableColumn<Player, String> playersName = ((TableColumn<Player, String>) playerTable.getColumns().get(0));
    playersName.setCellValueFactory(new PropertyValueFactory<Player, String>("name"));
    TableColumn<Player, String> victoryPoints = ((TableColumn<Player, String>) playerTable.getColumns().get(1));
    victoryPoints.setCellValueFactory(new PropertyValueFactory<Player, String>("victoryPoints"));
    TableColumn<Player, String> coins = ((TableColumn<Player, String>) playerTable.getColumns().get(2));
    coins.setCellValueFactory(new PropertyValueFactory<Player, String>("coins"));
    TableColumn<Player, String> assistants = ((TableColumn<Player, String>) playerTable.getColumns().get(3));
    assistants.setCellValueFactory(new PropertyValueFactory<Player, String>("assistants"));
    TableColumn<Player, String> nobilityPoints = ((TableColumn<Player, String>) playerTable.getColumns().get(4));
    nobilityPoints.setCellValueFactory(new PropertyValueFactory<Player, String>("nobilityTrackPoints"));
    playerTable.setItems(playersList);
}

private void placeKing(ImageView king, List<ImageView> citiesImagesList) {
    for (ImageView imageView : citiesImagesList) {
        if (imageView.getId().toString().equals(GUIController.currentState.getKing().getPosition().getName())) {
            king.setX(imageView.getLayoutX());
            king.setY(imageView.getLayoutY());
        }
    }
}

@FXML
public void initialize() {
    self = this;
}

public void setStage(Stage stage) {
    this.stage = stage;
}

public void updateGUI(StartTurnState state) {
    GUIController.currentState = state;
    updateMap(); //here I should set my toggle breakpoint
}

private void updateMap() {
    Scene scene = stage.getScene();
    List<ImageView> citiesImagesList = new ArrayList<>();
    citiesImagesList.add((ImageView) scene.lookup("#A"));
    citiesImagesList.add((ImageView) scene.lookup("#B"));
    citiesImagesList.add((ImageView) scene.lookup("#C"));
    citiesImagesList.add((ImageView) scene.lookup("#D"));
    citiesImagesList.add((ImageView) scene.lookup("#E"));
    citiesImagesList.add((ImageView) scene.lookup("#F"));
    citiesImagesList.add((ImageView) scene.lookup("#G"));
    citiesImagesList.add((ImageView) scene.lookup("#H"));
    citiesImagesList.add((ImageView) scene.lookup("#I"));
    citiesImagesList.add((ImageView) scene.lookup("#J"));
    citiesImagesList.add((ImageView) scene.lookup("#K"));
    citiesImagesList.add((ImageView) scene.lookup("#L"));
    citiesImagesList.add((ImageView) scene.lookup("#M"));
    citiesImagesList.add((ImageView) scene.lookup("#N"));
    citiesImagesList.add((ImageView) scene.lookup("#O"));
    ImageView king = (ImageView) scene.lookup("#king");
    Group group = (Group) scene.lookup("#group");
    setPlayerSet(); //if I try setting here my toggle the thread doesent stop as the method updateGui() is never called
    placeKing(king, citiesImagesList);
    placeRewardToken(group, citiesImagesList);
} }

I hope you understand my question. I will Edit my question if you do not understand something. I have not found something like this here on Stackoverflow.

Thanks in advance for your help.

EDIT: I have tryed to rewrite the code as suggested by James_D this is my new call to the Update to GUI.

    @Override
public void visit(StartTurnState currentState) {
    guiDisplayer.setState(currentState);
    Platform.runLater(()-> guiDisplayer.run());
}

I have also modified the GUIDisplayer Class

public class GUIDisplayer extends Application implements Runnable{

private StartTurnState currentState;
private Stage stage;
static GUIController guiController;

public void start(Stage stage) throws Exception {
    GUIController controller = new GUIController();
    FXMLLoader fxmlLoader = new FXMLLoader();
    Parent root = fxmlLoader.load(getClass().getResource("sceneMap.fxml"));
    stage.setTitle("Council of Four");
    Scene newScene = new Scene(root);
    stage.setScene(newScene);
    stage.show();
    controller.setStage(stage);
    setController(controller);
}

public static void startGUI() {
    Application.launch();
}

public void setState(State state) {
    currentState = (StartTurnState) state;
}

public static void setController(GUIController controller) {
    guiController = controller;
}

@Override
public void run() {
    guiController.doUpdateGUI(currentState);

} }

The runLater method call seems to be ignored, maybe I am in this case descibed by the javadoc

If this method is called after the JavaFX runtime has been shutdown, the call will be ignored: the Runnable will not be executed and no exception will be thrown.

if I remove the runLater and add toggle brackpoints I can still modify the GUI.

Community
  • 1
  • 1
axoaxel
  • 21
  • 3
  • Your board game needs to update fast? – GOXR3PLUS Jun 23 '16 at 11:29
  • It needs to update at the start of each player turn, at least every 5 minutes – axoaxel Jun 23 '16 at 12:23
  • Generally in your code follow this direction:Avoid changing javaFX SceneGraph outside the javaFX Thread(if you are not sure in which thread your are use Platform.isFxApplicationThread()) else you have to use Platform.runLater() but not overuse it cause the app will lag.To run code in other Threads check http://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm .I asked how fast it updates in case you wanted to use javaFX AnimationTimer for fast updating http://download.java.net/jdk8/jfxdocs/javafx/animation/AnimationTimer.html – GOXR3PLUS Jun 24 '16 at 15:13

1 Answers1

2

while trying to update gui from other threads (other than javafx application thread) you should use Paltform.runLater( () -> doUpdateGUI() ); than you gui will properly be updated. check javafx concurrency

guleryuz
  • 2,714
  • 1
  • 15
  • 19
  • I have modified my visit calling the runLater like this. @Override public void visit(StartTurnState currentState) { GUIController guiController = new GUIController(); Platform.runLater(()->guiController.doUpdateGUI(currentState)); } and I have also created che method doUpdateGUI in the controller which calls updateGUI(currentState) but nothing happens, when it is supposed to refresh my GUI? – axoaxel Jun 23 '16 at 10:55
  • `Platform.runLater(...)` will run essentially as soon as it can on the FX Application Thread. Don't be concerned about the name of the method, it is merely indicating that the code inside that block will not necessarily complete before the code immediately after the `runLater(...)` call. I think @guleryuz is correct: this definitely sounds like a threading issue and it seems that you are somehow blocking the FX Application Thread somewhere. – James_D Jun 23 '16 at 11:41
  • I have tryed Platform.setImplicitExit(false) before the call to runLater() in order to force the runLater(), now it enters in the updateGUI() but the reference to stage is null and I get a nullPointerException. Why I loose this reference? – axoaxel Jun 23 '16 at 12:06
  • I can't really follow your code. There seem to be fields that are arbitrarily made static and calls to static methods from non-static contexts all over the place. But you are creating lots of instances of `GUIController` almost arbitrarily, which makes no sense: the controller instance is created for you by the `FXMLLoader` when you call `load()`. You should probably reorganize your code so that it retrieves that instance from the loader and only uses that instance; otherwise it is impossible to understand what is going on. – James_D Jun 23 '16 at 12:14
  • Oh, also, `Platform.setImplicitExit(...)` doesn't have anything to do with `Platform.runLater(...)`. It just means the FX platform doesn't automatically exit when the last window is closed; you may need it for other reasons, but it doesn't "force the `runLater(...)`" (whatever that means). – James_D Jun 23 '16 at 12:21
  • I call a new GUIController() because I have noticed that when start(Stage stage) method in GUIDispalyer closes I loose the reference to controller even if I try to set it everywhere else... – axoaxel Jun 23 '16 at 12:46