-1

I'm writing a javafx program in the program i need to put a progress indicator after login system it should wait for specific time until it load the needed objects from the server. I'm new in javafx i want a way to validate the setVisible(boolean) property from the controller, in initialize it should be invisible and there's no problem to set it to false in initialize method but in controller after initializing i think i should validate the changes. is there a method that i could use to validate this property change?

     //////pseudocode
@FXML ProgressIndicator pi;
public void initialize(...){
 PI.setVisible(false); 
  }
@FXML public void buttonOnClick(){
Thread t1=new Thread(new Runnable(....)) // A thread to load data
 t1.start
pi.setVisible(true); //HERE IS MY PROBLEM
Thread t2;//a thread to increase progress
  t2.start();
t1.join();
}
Devx3Fr
  • 33
  • 7
  • 1
    Do I understand your problem right? You want to set the visibility of the progress-indicator to true after initializing while the data is being loaded? – poisn Aug 27 '19 at 13:23
  • Yes, that's correct – Devx3Fr Aug 27 '19 at 13:24
  • In the class where you're loading the data from the server you can call the progress-indicator with a getter like: **ClassName.getProgressIndicator().setVisible(true);** – poisn Aug 27 '19 at 13:29
  • That's not my problem , i can return the progress indicator but i can't validate the property change – Devx3Fr Aug 27 '19 at 13:30
  • For example in Java Frame you can do JFrame.validate() so the frame refreshes the changes. – Devx3Fr Aug 27 '19 at 13:31
  • Could this eventually help you? https://stackoverflow.com/questions/14423109/how-to-force-java-fx-scene-refresh – poisn Aug 27 '19 at 13:34
  • Please provide a [mcve] that demonstrates the problem. – kleopatra Aug 27 '19 at 13:41
  • it's not fx-2, or is it? If so, please update to a newer version (current is openjfx12) – kleopatra Aug 27 '19 at 13:42
  • @kleopatra I have just added a pseudo code – Devx3Fr Aug 27 '19 at 14:18
  • My guess is that you are just asking the wrong question to get an answer to the problem you are trying to solve. But I guess I understand what you want to do, so I'll try to answer that, but maybe I misunderstand and my answer is not relevant. – jewelsea Aug 27 '19 at 22:45

1 Answers1

2

Why the concept of validate() is usually invalid in JavaFX, plus solving threading issues

To explain some issues with your original question and why this answer may not seem to directly answer it:

  1. You usually don't have to validate() (i.e. force the layout of the subcomponents) components in JavaFX, as JavaFX will do that for you automatically each pulse (study the linked doc to more fully understand this).
  2. Sometimes you might want to generate a layout pass (which is, I guess, somewhat similar in concept to an explicit call to validate for a JFrame). But, that is usually just because you want to measure what the dimensions of something would be, once it has had css applied and has been completely laid out (which isn't what you want to do here).
  3. You should not try to change things in the scene graph off the JavaFX application thread as that will cause exceptions and race conditions (i.e. your call to setVisible on the progress indicator within a thread that you have created is wrong).
  4. It is better to use built-in JavaFX concurrency mechanisms for some concurrent tasks, such as submitting a login to a login service and interfacing with the UI based upon the task progress and results.

What to do instead

What I think you are trying to do is:

  1. To create a login prompt where the login logic occurs in another thread
  2. Have an indefinite progress indicator spin while the login is progressing in the other thread.
  3. The progress indicator is only shown while the login is in progress and is not shown once the login attempt completes (regardless of whether or not it succeeds or fails).

not logged in logging in logged in

Sample App

A login Service handles the asynchronous login process. A progress indicator in the login pane indicates that a login is ongoing. Once login is complete, the login pane is replaced with the application pane for the logged in user.

The following line ensures that the progress indicator is only displayed while the login service is executing:

progressIndicator.visibleProperty().bind(loginService.runningProperty());

Full code:

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

import java.io.IOException;

public class LoginServiceApp extends Application {
    private LoginService loginService = new LoginService();

    @Override
    public void start(Stage stage) throws IOException {
        Pane loginPane = createLoginPane();
        loginService.setOnSucceeded(event ->
                stage.getScene().setRoot(createAppPane(stage))
        );

        stage.setScene(new Scene(new StackPane(loginPane)));
        stage.show();
    }

    private Pane createLoginPane() {
        GridPane credentialsGrid = new GridPane();
        credentialsGrid.setHgap(10);
        credentialsGrid.setVgap(10);
        TextField usernameField = new TextField("frobozz");
        PasswordField passwordField = new PasswordField();
        credentialsGrid.addRow(0, new Label("Username"), usernameField);
        credentialsGrid.addRow(1, new Label("Password"), passwordField);

        Button loginButton = new Button("Login");
        loginButton.setOnAction(event -> {
            loginService.setUsername(usernameField.getText());
            loginService.setPassword(passwordField.getText());
            loginService.restart();
        });
        loginButton.disableProperty().bind(loginService.runningProperty());

        ProgressIndicator progressIndicator = new ProgressIndicator();
        progressIndicator.visibleProperty().bind(loginService.runningProperty());
        progressIndicator.setPrefSize(20, 20);

        HBox loginControl = new HBox(10, loginButton, progressIndicator);
        VBox loginPane = new VBox(10, credentialsGrid, loginControl);
        loginPane.setPadding(new Insets(10));

        return loginPane;
    }

    private Pane createAppPane(Stage stage) {
        Button logoutButton = new Button("Logout");
        logoutButton.setOnAction(event -> stage.getScene().setRoot(createLoginPane()));
        HBox appPane = new HBox(logoutButton);
        appPane.setPadding(new Insets(10));

        return appPane;
    }

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

    private static class LoginService extends Service<Void> {
        private StringProperty username = new SimpleStringProperty(this, "username");
        public final void setUsername(String value) { username.set(value); }
        public final String getUsername() { return username.get(); }
        public final StringProperty usernameProperty() { return username; }

        private StringProperty password = new SimpleStringProperty(this, "password");
        public final void setPassword(String value) { password.set(value); }
        public final String getPassword() { return password.get(); }
        public final StringProperty passwordProperty() { return password; }

        @Override
        protected Task<Void> createTask() {
            final String _username = getUsername();
            final String _password = getPassword();
            return new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    // Simulate a long call to a login service,
                    // using the username and password we saved when the task was created.
                    // If login fails, an exception can be raised to report it and the
                    // caller starting the service can monitor setOnException to handle it.
                    // Or the Task could return a result value instead of void and the caller
                    // could monitor the value property of the task in addition to the exception handler.
                    Thread.sleep(1_000);
                    return null;
                }
            };
        }
    }
}
jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • Thanks a lot that was so helpful .. I think I'm stuck in old java haha – Devx3Fr Aug 27 '19 at 23:06
  • I want to ask you a question but i don't want to ask it as a new post ... how to make the font size in a label or in a Text components calculated according to parent container is there a property in fxml or should it be dynamically done ? and how? – Devx3Fr Aug 28 '19 at 12:30
  • For your follow-up question regarding dynamic sizing of fonts, see: [javafx automatic resizing and button padding](https://stackoverflow.com/questions/23229149/javafx-automatic-resizing-and-button-padding) and [Bind Font Size in JavaFX?](https://stackoverflow.com/questions/23705654/bind-font-size-in-javafx) – jewelsea Aug 28 '19 at 16:55