Add level in JavaFX during loading operation - javafx

I have the following UI
So , you insert a keyword and then press "Cerca" (it's like Search in Italian) , then the Google Custom Search Api show the first 10 pictures. During the Custom search API are processing the results (the pictures) I want to show an other picture like this
(I know it's big but the dimension is not the main point now). My idea is simple, I want to put the picture one "level"(don't know exactly how to call ) over the UI, then the picture will be not visible in 3 case: 1) When the API will end their job 2)If I don't have results 3) If I get an exception. My question is, which is the best approach to do this ? And then, Should I use Threads?
I hope I was clear
UPDATE:
This is the code of "cerca" button
cerca.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
// Task<Boolean> task = new Task <Boolean>(){
//
// #Override
// protected Boolean call() throws Exception {
// // TODO Auto-generated method stub
// return null;
// }
//
//
// };
String searchKeyWord = userTextField.getText();
result = getSearchResult(searchKeyWord);
for ( i=0; i<result.size(); i++)
{
System.out.println("" +result.get(i));
ImageView resultview;
resultview = new ImageView(result.get(i));
resultview.setFitWidth(130);
resultview.setFitHeight(130);
// resultview.setStyle("-fx-border:6; -fx-border-color: green;");
if(j==4)
{
j=0;
k++;
}
resultgrid.add(resultview, j,k );
j++;
VBox vbox = new VBox();
resultgrid.setHgap(50);
resultgrid.setVgap(50);
// resultgrid.setStyle("-fx-border:1; -fx-border-color: red;");
vbox.getChildren().add(resultgrid);
vbox.setSpacing(10);
vbox.setPadding(new Insets(90, 0, 10, 220)); //TOP RIGHT BOTTOM LEFT
// content.setAlignment(resultgrid, Pos.TOP_RIGHT);
getChildren().add(vbox);
final int ind = i;
resultview.setOnMouseClicked((ev) ->{
if (ev.getClickCount()==2)
{
image = SwingFXUtils.fromFXImage(resultview.getImage(), null);
parent.setCrop(image);
}
});
}
}
});

Have a look at Task. It has setOnSucceeded() and setOnFailed() which will come handy in your case.
You can basically create a new Task when the search takes place. The search will run in background and you can show the loading UI in the screen.
If the task completes successfully, you can load the new screen with the results.
If the task fails, you can show an error message near the search TextField.
In case of exception, you can catch it and write the necessary code.

Related

How to disable the arrow button in JavaFX combo box

I have a project I am working on. I am trying to make a dictionary. For that, I have a .csv file with about 55000 words.I am using the trie data structure which has a startsWith() method which checks whether there is a word in the .csv file which matches the given prefix. I had managed to get it to work to find all words that match the given prefix and display them. Now, I have to develop this into a JavaFX app.
So, I thought of using a ComboBox which has its editable attribute set to true so that I could type into it and then the handler associated with the textProperty() of its editor would display all the words starting with given prefix in the listview of the combobox.
Now, the problem I have is that whenever I click the arrow button of the combobox the application stops responding (I think it's because the list view tries to resize itself to fit the items which are 55000).
So, what I want to know is how to disable the arrow button entirely. I have tried to set its background-color to transparent but even then it can still be clicked I want to make it so that it is disabled and transparent basically the combobox ends up looking like a text field.
If there are better, more efficient ways of implementing a dictionary I would appreciate it if you could guide me.
The ListView is a virtual control that only shows a certain number of cells at a time, it doesn't need to "resize itself to the number of items" in any way that would lock up your GUI.
Does this demo program do what you want?
public class Main extends Application {
public static void main(String[] args) throws IOException, URISyntaxException {
launch(args);
}
#Override
public void start(Stage stage) {
List<String> rawWords = Collections.emptyList();
try {
URI wordURI = new URI("https://www-cs-faculty.stanford.edu/~knuth/sgb-words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(wordURI.toURL().openStream(), StandardCharsets.UTF_8));
rawWords = reader.lines().collect(Collectors.toCollection(() -> new ArrayList<>(6000)));
} catch (IOException | URISyntaxException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
// make the list at least as big as in the question
while(rawWords.size() < 55000) {
ArrayList<String> nextWords = new ArrayList<>(rawWords.size() * 2);
nextWords.addAll(rawWords);
nextWords.addAll(rawWords);
rawWords = nextWords;
}
Collections.sort(rawWords);
ObservableList<String> wordList = FXCollections.observableArrayList(rawWords);
FilteredList<String> filteredList = new FilteredList<>(wordList);
ComboBox<String> combo = new ComboBox<>(filteredList);
combo.setEditable(true);
combo.getEditor().textProperty().addListener((obs, oldVal, newVal) -> {
filteredList.setPredicate(s -> newVal == null || newVal.isEmpty() || s.startsWith(newVal));
});
VBox vbox = new VBox(8,new Label("Dictionary ComboBox"),
combo,
new Label("\n\n\n\nThis space intentionally left blank.\n\n\n\n"));
vbox.setPadding(new Insets(8));
Scene scene = new Scene(vbox, 400, 300);
stage.setTitle("Demo - Filtered Combobox List");
stage.setScene(scene);
stage.show();
}
}

Modifying interface with key interactions in javafx

I'm trying to code a 2048 game using JavaFX and I'm facing a problem.
#Override
public void start(Stage primaryStage){
primaryStage.setResizable(false);
Scene scene = new Scene(firstContent());
primaryStage.setScene(scene);
primaryStage.show();
scene.setOnKeyPressed(new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent e){
KeyCode key = e.getCode();
if((key.equals(KeyCode.UP))){
System.out.println("recieved UP");
Scene scene = new Scene(createContent());
primaryStage.setScene(scene);
primaryStage.show();
} else if(key.equals(KeyCode.DOWN)){
System.out.println("recieved DOWN");
}
}
});
}
So here I open my window initialised with firstContent (basically it creates an array of empty tiles, and fills two of them with 2 or 4 randomly), display it and start listening for key presses. The idea is to have a behavior for each arrow key (UP DOWN LEFT RIGHT) which will move the tiles accordingly. This is done by the following createContent() method :
public Parent createContent(){
String c = "";
List<Integer> known = new ArrayList<Integer>();
Pane root = new Pane();
root.setPrefSize(740, 700);
Random rand = new Random();
int pos1 = rand.nextInt(15);
if(tiles.get(pos1) != new Tile("")){
known.add(pos1);
pos1 = rand.nextInt(15);
if(known.contains(pos1)){
known.add(pos1);
pos1 = rand.nextInt(15);
}
}
for(int i = 0; i < NB_TILES; i++){
tiles.add(new Tile(c));
}
tiles.set(pos1, new Tile("2048"));
for(int i = 0; i < tiles.size(); i++){
// boring stuff to set the tile display to the right size
}
return root;
}
Now for the problem : when the application is running, if I press the down arrow, I do get on my terminal the "recieved DOWN" text as many times as I press the key as expected. But if I press the up arrow, the application will only recieve it once and the application seems to be frozen (meaning if I press down again, nothing happens).
As you could guess, I want to be able to call my method for each key press to be able to move my tiles around and utltimately combine them to get a playable version of 2048... Anyone know why my app gets frozen ?
If needed, I can provide other bits of code but I think I provided the essential. Just know that firstContent() works basically the same as createContent for now except it genereates two random numbers to get the first tiles of the game.
Thanks in advance for your help.

reading and writing file in javafx program

I'm writing a program writes and read the file. it has 50 numbers and if users enter number more than 50 it should ask the users to enter it again. my question is for the error it should show the message box but again return to the text field for input. Also one more question, for example, user1 is entering 1 and user2 is also entering 1, it should tell the user2 "please enter another number it's already entered." how can I do it in writing and reading files.
this is my code:
public class JavaFXApplication24 extends Application {
private int seatInput;
#Override
public void start(Stage primaryStage) {
StackPane pane = new StackPane();
HBox hbox = new HBox();
Label num = new Label("Please enter the number from 1 to 50: ");
TextField numInput = new TextField();
File file = new File("NumberBook.txt");
Button ok = new Button("ok");
ok.setOnAction((new EventHandler<ActionEvent>(){
public void handle(final ActionEvent event ){
try{
if ((numInput.getText().equals(1)&& numInput.getText().equals(50))){
}else{
String input = JOptionPane.showInputDialog("Please enter again");
}
}
catch (HeadlessException | NumberFormatException e) {
}
try {
PrintWriter output = new PrintWriter(file);
} catch (IOException ex) {
System.out.printf("error", ex);
}
try {
Scanner input1 = new Scanner(file);
System.out.printf("Entered: %s", seatInput);
} catch (FileNotFoundException ex) {
System.out.printf("error", ex);
}
}
}));
hbox.getChildren().addAll(num,numInput,ok);
pane.getChildren().addAll(hbox);
Scene secondScene = new Scene(pane, 600, 400);
// New window (Stage)
Stage newWindow = new Stage();
newWindow.setTitle("Booking and Timing");
newWindow.setScene(secondScene);
newWindow.show();
}
}
From a quick glance here are some problems with your code:
1) if ((numInput.getText().equals(1)&& numInput.getText().equals(50))). There are 2 problems here. Firstly, as fabian pointed out, this statement will always evaluate to false. Secondly, from your question I think you want the user to enter a number from 1 to 50, but here you're checking if they enter 1 and 50 (which also means it's always false).
2) JOptionPane.showInputDialog("Please enter again") Because your application is JavaFX, you should use the built in Dialog instead.
Now to answer your questions, you can call requestFocus on the text field right after the error message is displayed. Your second question seems like you want some networking functionality in the application, which adds a whole lot of complexity to it. You might need to do some research on how to do it in Java.

JavaFX - Progress properties don't work?

I'm trying to get the progress value of my Picture Viewer when another picture is loading. I've tried two simple ways to do it, but it didn't work out for me.
First I was using the progressProperty from the Image class:
public boolean nextImageClicked()
{
if(PICTURE_INDEX < picturePaths.size() - 1)
{
String path = picturePaths.get(PICTURE_INDEX + 1).toURI().toString();
Image newImage = new Image(path);
newImage.progressProperty().addListener((observable, oldValue, newValue) -> System.out.println("Current progress: "+newValue));
GUI.getImageView().setImage(newImage);
adjustImageViewBounds();
PICTURE_INDEX += 1;
return true;
}
else return false;
}
It didn't print out anything on the console because the progress value doesn't change bizarrely. So I've tried to put all the work stuff in a Task<Void> and getting the progress value through the Task:
public boolean nextClicked()
{
if(PICTURE_INDEX < picturePaths.size() - 1)
{
Task<Void> task = new Task<Void>()
{
#Override protected Void call() throws Exception
{
String path = picturePaths.get(PICTURE_INDEX + 1).toURI().toString();
Image newImage = new Image(path);
GUI.getImageView().setImage(newImage);
adjustImageViewBounds();
PICTURE_INDEX += 1;
return null;
}
};
task.setOnRunning(e -> System.out.println(task.getProgress()));
task.progressProperty().addListener((observable, oldValue, newValue) ->
{
System.out.println(newValue);
});
task.run();
return true;
}
else return false;
}
Also didn't work out as hoped.
task.setOnRunning(e -> System.out.println(task.getProgress()));
I implemented this to see the default value, it printed out "-1".
What have I to change to let the console return single progress values like "0.1", "0.14" ?
You need the Image to load in the background, so that the call to the constructor returns before the image is completely loaded. By default, it will block until it is loaded (so the progress property will be 1 by the time you add the listener to it):
public boolean nextImageClicked()
{
if(PICTURE_INDEX < picturePaths.size() - 1)
{
String path = picturePaths.get(PICTURE_INDEX + 1).toURI().toString();
// note additional parameter:
Image newImage = new Image(path, true);
newImage.progressProperty().addListener((observable, oldValue, newValue) -> System.out.println("Current progress: "+newValue));
GUI.getImageView().setImage(newImage);
adjustImageViewBounds();
PICTURE_INDEX += 1;
return true;
}
else return false;
}
For a Task's progress to change, you need to explicitly call updateProgress(...) on the task. The only way to know what to pass in would be to observe the image's progress and pass it to the task's progress, so you would just have a more convoluted version of the code above. This is not a good use case for a task, since Image already supports background loading out of the box.
Don't try to do this on your own. You've got no idea, how much of the image has been loaded, unless you find the size of the image before loading and load the image from a steam observing the progress of the stream, which would be unnecessarily complicated. BTW: The Image constructor you use returns when the image is completely loaded. You can specify the image to be loaded asynchronically by using the right constructor however. Image provides a progress property to observe the loading progress:
#Override
public void start(Stage primaryStage) {
ImageView iv = new ImageView();
ProgressBar pb = new ProgressBar();
Button btn = new Button("Load Image");
btn.setOnAction((ActionEvent event) -> {
// ca. 6 MB image loaded from web
Image image = new Image("http://eoimages.gsfc.nasa.gov/images/imagerecords/79000/79793/city_lights_africa_8k.jpg", true);
pb.progressProperty().bind(image.progressProperty());
iv.setImage(image);
});
ScrollPane sp = new ScrollPane(iv);
VBox.setVgrow(sp, Priority.ALWAYS);
VBox root = new VBox(btn, pb, sp);
root.setFillWidth(true);
Scene scene = new Scene(root);
primaryStage.setMaximized(true);
primaryStage.setScene(scene);
primaryStage.show();
}

How to wait for user's action in JavaFX (in the same stage)

Do you know how to wait for the user's input in a for loop? I don't mean the showAndWait() method, because I am not opening a new dialogue stage for the user. So for example, each round of the for loop should be waiting for the user to push a button before going ahead with the next round.
How is it possible? Many thanks!
UPDATE:
Now it came to my mind, that it would work with a while(buttonNotPressed){} but is it a good solution? I mean the while loop is running in this case as crazy until the user won't push the button. Or doest it work somehow similarly with wait methods?
Imagine it as a session:
User starts session with handleStart() You give the user 5 questions, one after one. In every iteration, the user can answer the upcoming question and he can save or submit the answer by handleSaveButton() You process the answer as you want, and go ahead with the next iteration. The point is, that the iteration must stop, until the save button hasn't been pressed.
Don't do it like that. The FX toolkit, like any event-driven GUI toolkit, already implements a loop for the purposes of rendering the scene graph and processing user input each iteration.
Just register a listener with the button, and do whatever you need to do when the button is pressed:
button.setOnAction(event -> {
// your code here...
});
If you want the action to change, just change the state of some variable each time the action is performed:
private int round = 0 ;
// ...
button.setOnAction(event -> {
if (round < 5) {
System.out.println("Round "+round);
System.out.println("User's input: "+textArea.getText());
round++ ;
}
});
I recently ran into a similar problem where I wanted something to be executed with an interval (if that's what you mean), until the user fired an event. I found 3 ways to do this:
UPDATE
You should use the stop/cancel method for the custom runnable and timer or else the thread will still be running when you exit the application. Timeline seems do it by itself.
Using a Timer:
Timer timer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
System.out.println("Printed every second.");
}
};
timer.scheduleAtFixedRate(task, 0, 1000);
//timer.cancel();
With a TimeLine:
Timeline tl = new Timeline(new KeyFrame(Duration.millis(1000), e -> {
System.out.println("Timeline");
}));
tl.setCycleCount(Timeline.INDEFINITE);
tl.play();
//tl.stop();
Or making your own runnable class:
public class Runner implements Runnable {
private final Thread thread = new Thread(this);
private boolean run;
#Override
public void run() {
while(run) {
try {
System.out.println("Printed from loop");
Thread.sleep(1000);
} catch (InterruptedException e) {
run = false;
}
}
}
public void start() {
run = true;
thread.start();
}
public void stop() {
if(run) {
thread.interrupt();
System.out.print("Thread has stopped.");
}
}
}
And then when a person clicks fx. a button the event would stop using the example James_D posted:
Button btn = new Button("Button");
btn.setOnAction(e -> {
timer.cancel();
tl.stop();
runner.stop();
});
In my case, for inside for, had to create 2 index in class, use:
//start method
Timer timer = new Timer();
TimerTask task = new TimerTask()
{
public void run()
{
Platform.runLater(()->{
//... code to run after time, calling the same mehtod, with condition to stop
});
}
};
timer.schedule(task, time);
//end method
Had to use recursive method, incrementing the index with conditions, cause the tasks were been schedule all at the same time, without wait time.
I do not know if it is rigth, but was the solution that i found.
Hope it helps.
ALTERNATIVE SOLUTION W/O PAUSING:
I'm creating a game where I want the user to pick the game difficulty before the game starts. Instead of trying to pause the program midway through, I just put the next step of the code in a separate method which you call once a button is clicked:
private static difficulty;
public static void main(String[] args) {
try {
Application.launch(args);
} catch (UnsupportedOperationException e) {
}
}
public void start(Stage startStage) {
HBox buttons = new HBox();
Button easyButton = new Button("Easy");
Button mediumButton = new Button("Medium");
Button hardButton = new Button("Hard");
buttons.getChildren().addAll(easyButton, mediumButton, hardButton);
buttons.setAlignment(Pos.CENTER);
hbox.getChildren().addAll(buttons);
Scene startScene = new Scene(buttons, 200, 200);
startStage.setScene(startScene);
startStage.show(); // MENU
EventHandler<ActionEvent> playEasy = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
difficulty = 1; // SET DIFFICULTY
startStage.close(); // CLOSE MENU
play(); // RUN GAME ON EASY
}
};
EventHandler<ActionEvent> playMedium = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
difficulty = 2; // SET DIFFICULTY
startStage.close(); // CLOSE MENU
play(); // RUN GAME ON MEDIUM
}
};
EventHandler<ActionEvent> playHard = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
difficulty = 3; // SET DIFFICULTY
startStage.close(); // CLOSE MENU
play(); // RUN GAME ON HARD
}
};
easyButton.setOnAction(playEasy);
mediumButton.setOnAction(playMedium);
hardButton.setOnAction(playHard);
}
public void play() {
// WRITE GAME CODE HERE
}
To solve your specific problem, you could probably pass the startStage into the play method and then just update the scene there...but regardless I do hope this helps someone whos having trouble on how to use buttons! :)

Resources