JavaFX and ProgressIndicator with fxml - java

I wonder from morning how to solve the problem. I have login aplication. When user waiting for login i want use processindicator. I used the second thread but it does not work
Main loader fxml
MainController
#FXML public StackPane MainStackPane;
public void initialize(URL arg0, ResourceBundle arg1) {
FXMLLoader loader = new FXMLLoader(this.getClass().getResource("/LoginForm/Login.fxml"));
Pane pane = null;
try {
pane = loader.load();
} catch (IOException e) {System.out.println(e.getMessage());}
LoginController login = loader.getController();
login.setMainController(this);
setScreen(pane, true);
}
public void setScreen(Pane pane, boolean clean){
MainStackPane.getChildren().addAll(pane);
}
LoginForm:
private MainController mainController;
private void Zaloguj() throws IOException {
String cryptUser = null, cryptPass = null;
Test test = new Test(this.mainController);
test.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
LoginSQL sql = new LoginSQL();`
Byte LoginResult = sql.SQLselect(cryptUser, cryptPass);
...}
Class Test
public class test extends Service<Void>{
private MainController mainController;
public test(MainController mainController) {
this.mainController = mainController;
}
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
System.out.println("Service: START");
ProgressIndicator p = new ProgressIndicator();
Platform.runLater(new Runnable() {
#Override public void run() {
mainController.MainStackPane.getChildren().addAll(p);
}});
if (isCancelled()) {
mainController.MainStackPane.getChildren().remove(p);
}
return null;
};};}}
ProgressIndicator appears only in the next window after login. How to do it ?

JavaFX rendering happens in the main thread only. If you add the ProgressIndicator and then use Thread.sleep(), JavaFX won't render the indicator until Thread.sleep() is done. Also, if the login request hangs, JavaFX will also wait until the login request is complete before rendering.
What you have to do is to make sure to never interrupt/hang the main thread. Remove Thread.sleep, and also move your login request to a child thread. When the request is complete, notify the main thread so that it can remove the ProgressIndicator.

Related

JavaFX : Use an Indeterminate Progressbar in a splashScreen

I have a splash screen :
I need to have the animation of the progress bar (Indeterminate) but it doesn't work.
It's maybe due to because my thread is running in my initilize methode.
public class splashscreenController implements Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {
new SplashScreen().run();
}
class SplashScreen extends Task {
#Override
public Object call() {
Platform.runLater(new Runnable() {
#Override
public void run()
Parent root = null;
try {
Thread.sleep(3000);
root = FXMLLoader.load(getClass().getResource("../gui/NewUI.fxml"));
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
Stage stage = new Stage();
stage.initStyle(StageStyle.UNDECORATED);
assert root != null;
Scene scene = new Scene(root, 1280, 720);
stage.setScene(scene);
stage.show();
MainJavaFx.setPrimaryStage(stage);
((Stage) panParent.getScene().getWindow()).close();
}
});
return null;
}
}
}
There are 2 issues in your code:
new SplashScreen().run();
A Task does not provide functionality for running on a new thread. run is executed on the calling thread.
class SplashScreen extends Task {
#Override
public Object call() {
Platform.runLater(new Runnable() {
#Override
public void run() {
// placeholder for parts of your code
longRunningOperation();
guiUpdate();
}
});
return null;
}
}
Even if you execute this task on a seperate thread, the Runnable passed to Platfrom.runLater is executed on the JavaFX application thread and doing long-running operations from this runnable freezes the GUI.
Do all the long-running operations on the background thread instead and only do short updates using Platfrom.runLater.
new Thread(new SplashScreen()).start();
class SplashScreen extends Task {
#Override
public Object call() throws IOException, InterruptedException {
Thread.sleep(3000);
final Parent root = FXMLLoader.load(getClass().getResource("../gui/NewUI.fxml"));
Platform.runLater(new Runnable() {
#Override
public void run() {
Stage stage = new Stage();
stage.initStyle(StageStyle.UNDECORATED);
Scene scene = new Scene(root, 1280, 720);
stage.setScene(scene);
stage.show();
MainJavaFx.setPrimaryStage(stage);
((Stage) panParent.getScene().getWindow()).close();
}
});
return null;
}
}
Note that since you're not using the functionality provided by Task, you could simply implement Runnable with your class instead of inheriting from Task.

Repaint BorderPane (javaFx)

I have an application that can create a rectangle that decreases in size for example a lapse of time of 10 sec, but here is when I try to shrink the rectangle, the window bug (nothing is displayed in the scene) and wait until the countdown is finished to stop bugging (and then display the rectangle not diminished).
I tried to find on the Internet the equivalent of repaint in Swing but not average: /
this.requestLayout () -> I found this on the internet but it does not work.
Here is my code of my countdown:
public class Compteur {
DemoBorderPane p ;
public DemoBorderPane getPan() {
if(p==null) {
p = new DemoBorderPane();
}
return p;
}
public Compteur() {
}
public void lancerCompteur() throws InterruptedException {
int leTempsEnMillisecondes=1000;
for (int i=5;i>=0;i--) {
try {
Thread.sleep (leTempsEnMillisecondes);
}
catch (InterruptedException e) {
System.out.print("erreur");
}
System.out.println(i);
getPan().diminuerRect(35);
}
}
}
There is my Borderpane code :
public class DemoBorderPane extends BorderPane {
private Rectangle r;
public Rectangle getRect() {
if(r==null) {
r = new Rectangle();
r.setWidth(350);
r.setHeight(100);
r.setArcWidth(30);
r.setArcHeight(30);
r.setFill( //on remplie notre rectangle avec un dégradé
new LinearGradient(0f, 0f, 0f, 1f, true, CycleMethod.NO_CYCLE,
new Stop[] {
new Stop(0, Color.web("#333333")),
new Stop(1, Color.web("#000000"))
}
)
);
}
return r;
}
public void diminuerRect(int a) {
getRect().setWidth(getRect().getWidth()-a);
int c= (int) (getRect().getWidth()-a);
System.out.println(c);
this.requestLayout();
//this.requestFocus();
}
public DemoBorderPane() {
this.setBottom(getRect());
}
}
There is my Main code :
public class Main extends Application {
private DemoBorderPane p;
public DemoBorderPane getPan() {
if(p==null) {
p = new DemoBorderPane();
}
return p;
}
#Override
public void start(Stage primaryStage) {
Compteur c = new Compteur();
try {
//Group root = new Group();
Scene scene = new Scene(getPan(),800,600);
//scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
//root.getChildren().add(getPan());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
try {
c.lancerCompteur();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
/*Son s = null;
try {
s = new Son();
} catch (LineUnavailableException | IOException | UnsupportedAudioFileException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
s.volume(0.1);
s.jouer();
c.lancerCompteur();
s.arreter();*/
}
}
Thank ;)
As long as you keep the JavaFX application thread busy it cannot perform layout/rendering. For this reason it's important to make sure any methods that run on the application thread, like e.g. Application.start or event handlers on input events return fast.
lancerCompteur however blocks the application thread for 5 seconds so the only result you see is the final one after the method completes.
In general you can run code like this on a different thread and use Platform.runLater to update the ui.
In this case you could take advantage of the Timeline class which allows you to trigger an event handler on the application thread after a given delay:
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(getPan(), 800, 600);
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), event -> {
getPan().diminuerRect(35);
}));
timeline.setCycleCount(5);
timeline.play();
primaryStage.setScene(scene);
primaryStage.show();
}
You also use different instances of DemoBorderPane in your Main class and the Compteur class; the Rectangle shown in the scene was never subject to an update.
there's no need to call requestLayout in diminuerRect. This happens automatically when the Rectangle's size is modified.
Lazy initialisation is pointless, if you know for sure the getter will be invoked during the object's creation. DemoBorderPane.getRect is invoked from it's constructor so moving the initialisation to the constructor would allow you to get rid of the if check without affecting functionality.

JavaFX action event statement execution order

public class MainController {
#FXML
public Button browse_report;
#FXML
public Button browse_directory;
#FXML
public Button export;
#FXML
public Button close;
#FXML
public Label report;
#FXML
public Label directory;
#FXML
public Label processing;
#FXML
public TextField report_text;
#FXML
public TextField directory_text;
#FXML
public ProgressBar pg = new ProgressBar();
public void closeButton(ActionEvent e) {
System.exit(0);
}
public void getReport(ActionEvent e) {
FileChooser fc = new FileChooser();
File file=fc.showOpenDialog(null);
report_text.setText(file.getAbsolutePath());
}
public void getDirectory(ActionEvent e) {
DirectoryChooser dc = new DirectoryChooser();
File file =dc.showDialog(null);
directory_text.setText(file.getAbsolutePath());
}
public void Export(ActionEvent e) {
pg.setProgress(-1);
foo();
Alert alert = new Alert(AlertType.INFORMATION, "Spreadsheet Generated", ButtonType.CLOSE);
alert.showAndWait();
pg.setProgress(1);
}
The above Export() method, when executed on button click,appears to run the method statements out of order. The progress bar animation does not display until after the alert window pops up. How can I correct this?
Assuming your foo() method takes a long time to run, what is happening is that you are blocking the FX Application Thread, which prevents it from performing its usual duties, such as rendering the UI. So while the statements are (of course) executed in the order you write them, i.e. the progressProperty of pg is set to -1, then foo() is executed, then the Alert is shown, you won't actually see the results of those in the UI until the whole process completes (or, actually, in this case until you relinquish control of the FX Application Thread by calling showAndWait()).
The basic way to solve this is to run foo() in a background thread. If there is UI-related code you want to perform when that completes, the best way to do that is to use a Task and use its onSucceeded handler to perform the UI code at the end. So in general you do something like this:
public void Export(ActionEvent e) {
pg.setProgress(-1);
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
foo();
return null ;
}
};
task.setOnSucceeded(evt -> {
Alert alert = new Alert(AlertType.INFORMATION, "Spreadsheet Generated", ButtonType.CLOSE);
alert.showAndWait();
pg.setProgress(1);
});
new Thread(task).start();
}

Concurrency in Java(FX)

In my JavaFX application I want to display an intermediate ProgressIndicator, while loading data from a database for instance. I tried to solve this problem by setting a different fxml-file (indicator.fxml), containing a ProgressIndicator with intermediate property, into the main BorderPane. And after that, setting the desired Stage into the BorderPane.
The indicator.fxml gets displayed, but seems to be frozen, because it is not animating. PersondataController controller = loader.getController() takes some time to be finished.
#FXML
private void showPersonData(){
try{
FXMLLoader loader2 = new FXMLLoader();
loader2.setLocation(RootLayoutController.class.getResource("Indicator.fxml"));
AnchorPane progress = loader2.load();
mainApp.getRootLayout().setCenter(progress);´
Platform.runLater(new Runnable() {
#Override
public void run() {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(RootLayoutController.class.getResource("Persondata.fxml"));
PersondataController controller = loader.getController();
AnchorPane page = null;
try {
page = loader.load();
} catch (IOException e) {
e.printStackTrace();
}
mainApp.getRootLayout().setCenter(page);
}
});
}catch(Exception e){
e.printStackTrace();
}
Any ideas how I can solve this problem?
Finally solved my problem with your help. thank you guys.
Instead of the Platform.runLater() stuff I did:
Service<Void> backgroundThread = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
FXMLLoader loader2 = new FXMLLoader();
loader2.setLocation(RootLayoutController.class.getResource("Persondata.fxml"));
PersondataController = loader2.getController();
page = loader2.load();
return null;
}
};
}
};
backgroundThread.setOnSucceeded((evt) -> {
mainApp.getRootLayout().setCenter(page);
});
backgroundThread.start();

JavaFX Application returning NullPointerException when other Scene is called (just sometimes)

My application has a Login Scene and a Main View Scene, what is happening is when I do my login and MainView is called SOMETIMES I get this exception:
java.lang.NullPointerException
at javafx.scene.Scene.focusInitial(Scene.java:1879)
at javafx.scene.Scene.access$3600(Scene.java:170)
at javafx.scene.Scene$ScenePulseListener.focusCleanup(Scene.java:2181)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2221)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:363)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:460)
at com.sun.javafx.tk.quantum.QuantumToolkit$9.run(QuantumToolkit.java:329)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
at com.sun.glass.ui.win.WinApplication$3$1.run(WinApplication.java:73)
at java.lang.Thread.run(Thread.java:722)
The curious is it doesn't happening always, just sometimes.
My class:
public class TargetAppDesktop extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
Scene scene = new Scene(new AnchorPane());
LoginManager loginManager = new LoginManager(scene);
loginManager.showLoginScreen();
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent t) {
MainViewController.deleteTempFiles();
Platform.exit();
System.exit(0);
}
});
}
public static void main(String[] args) {
launch(args);
}
}
MY LOGIN MANAGER CLASS
public class LoginManager {
private Scene scene;
LoginManager(Scene scene) {
this.scene = scene;
}
public void logout() {
showLoginScreen();
}
void showLoginScreen() {
try {
FXMLLoader loader = new FXMLLoader(
getClass().getResource("Login.fxml"));
// scene.getStylesheets().add(this.getClass().getResource("Login.css").toExternalForm());
scene.setRoot((Parent) loader.load());
LoginController controller =
loader.<LoginController>getController();
controller.initManager(this);
} catch (IOException ex) {
Logger.getLogger(LoginManager.class.getName()).log(Level.SEVERE, null, ex);
}
}
void showMainViewScreen(Login loginTargetApp, Login loginGateway, Gateway gateway, File file, ArrayList<Integer> anoList) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("MainView.fxml"));
scene.setRoot((Parent) loader.load());
MainViewController controller = loader.<MainViewController>getController();
controller.initSessionID(this, scene, loginTargetApp, loginGateway, gateway, file, anoList);
} catch (Exception ex) {
Logger.getLogger(LoginManager.class.getName()).log(Level.SEVERE, null, ex);
}
}
void autheticated(Login loginTargetApp, Login loginGateway, Gateway gateway, File file, ArrayList<Integer> anoList) {
showMainViewScreen(loginTargetApp, loginGateway, gateway, file, anoList);
}
}
This problem was ocurring because I was trying to change my scene in another Thread, but it must to be changed in a Javafx Main Thread, so a simple Platform.runLater solved my problem.
More detail you can find here. (JIRA link)

Categories

Resources