Wait and then get text box input without GUI freezing

I hope I do not duplicate the question, but I could not find it specifically for my problem. I am developing a small flash card application using JavaFX to create a GUI. The program should work as follows:

  • the user selects the settings, then presses the start button.
  • gui displays a question and a text box for user input.
  • user inputs answer within X seconds or gui automatically proceeds to the next question - alternatively, the user can immediately go to the next question by pressing the next button.
  • The graphical interface displays the score and the average value.

Problems with getText() from the user's text field are processed as soon as the start button is pressed, preventing the user from entering an answer. How to make the program wait X seconds or press the next button before processing the user's response? Here is my code:

 //start button changes view and then runs startTest() start.setOnAction(e -> { setLeft(null); setRight(null); setCenter(test_container); running_program_title.setText(getDifficulty().name() + " Test"); buttons_container.getChildren().clear(); buttons_container.getChildren().addAll(next, quit, submit); startTest(); }); 

Here is the problem code ... at least as I see it.

 //startTest method calls askAdd() to ask an addition question void startTest() { int asked = 0; int correct = 0; while (asked < numberOfQuestions) { if(askAdd()){ correct++; asked++; } } boolean askAdd() { int a = (int) (Math.random() * getMultiplier()); int b = (int) (Math.random() * getMultiplier()); //ask question question.setText("What is " + a + " + " + b + "?"); //code needed to pause method and wait for user input for X seconds //retrieve user answer and return if its correct return answer.getText().equalsIgnoreCase(String.valueOf(a+b)); } 

I tried using Thread.sleep(X) , but it freezes gui, no matter how I specify it, and then goes through the addAsk() method and the loop before going to the test screen. (I know, because I had a program designed to print questions and answers to the console). He shows the last question and all.

I did not include the following button code because I cannot get gui to go to the test page.

Any help on any of the code is appreciated.

+5
source share
2 answers

This can be achieved in various ways.

PauseTransition is one of many suitable solutions. It waits for X time interval , and then runs Task . It can start , restart , stop at any moment .

Here is an example of how it can be used to achieve a similar result.

enter image description here

Full code

 import javafx.animation.PauseTransition; import javafx.application.Application; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.stage.Stage; import javafx.util.Duration; import java.util.stream.IntStream; public class Main extends Application { int questionIndex = 0; int noOfQuestions = 10; @Override public void start(Stage stage) { VBox box = new VBox(10); box.setPadding(new Insets(10)); Scene scene = new Scene(new ScrollPane(box), 500, 200); ObservableList<String> questions = FXCollections.observableArrayList("1) Whats your (full) name?", "2) How old are you?", "3) Whats your Birthday?", "4) What starsign does that make it?", "5) Whats your favourite colour?", "6) Whats your lucky number?", "7) Do you have any pets?", "8) Where are you from?", "9) How tall are you?", "10) What shoe size are you?"); ObservableList<String> answers = FXCollections.observableArrayList(); final PauseTransition pt = new PauseTransition(Duration.millis(5000)); Label questionLabel = new Label(questions.get(questionIndex)); Label timerLabel = new Label("Time Remaining : "); Label time = new Label(); time.setStyle("-fx-text-fill: RED"); TextField answerField = new TextField(); Button nextQuestion = new Button("Next"); pt.currentTimeProperty().addListener(new ChangeListener<Duration>() { @Override public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) { time.setText(String.valueOf(5 - (int)newValue.toSeconds())); } }); box.getChildren().addAll(questionLabel, answerField, new HBox(timerLabel, time), nextQuestion); nextQuestion.setOnAction( (ActionEvent event) -> { answers.add(questionIndex, answerField.getText()); //Check if it is the last question if(questionIndex == noOfQuestions-1) { pt.stop(); box.getChildren().clear(); IntStream.range(0, noOfQuestions).forEach(i -> { Label question = new Label("Question : " + questions.get(i)); question.setStyle("-fx-text-fill: RED"); Label answer = new Label("Answer : " + answers.get(i)); answer.setStyle("-fx-text-fill: GREEN"); box.getChildren().addAll(question, answer); }); } // All other time else { //Set new question questionLabel.setText(questions.get(++questionIndex)); answerField.clear(); pt.playFromStart(); } }); pt.setOnFinished( ( ActionEvent event ) -> { nextQuestion.fire(); }); pt.play(); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } } 
+1
source

For a timer, you must (IMO) use Timeline . Here is an example:

 public class MultiGame extends Application { ProgressBar progressBar; final int allowedTime = 5; //seconds final DoubleProperty percentOfTimeUsed = new SimpleDoubleProperty(0); final Timeline timer = new Timeline( new KeyFrame( Duration.ZERO, new KeyValue(percentOfTimeUsed, 0)), new KeyFrame( Duration.seconds(allowedTime), new KeyValue(percentOfTimeUsed, 1)) ); public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { BorderPane root = new BorderPane(); progressBar = new ProgressBar(); progressBar.progressProperty().bindBidirectional(percentOfTimeUsed); root.setTop(progressBar); Button answer = new Button("Answer"); answer.setOnAction(ae -> restart());// the on answer handler Button skip = new Button("Skip"); skip.setOnAction(ae -> restart());// the skip question handler HBox mainContent = new HBox(15, new Label("Your Question"), new TextField("The answer"), answer, skip); root.setCenter(mainContent); timer.setOnFinished(ae -> restart());// the end of timer handler primaryStage.setScene(new Scene(root)); primaryStage.show(); restart(); } void restart() { timer.stop(); timer.playFromStart(); } void pause() { timer.pause(); } void resume() { timer.play(); } } 

You just need to take the text from the input between the beginning of the timeline and the restart method.

0
source

All Articles