friends. New to stackoverflow, here, and also to JavaFX. Apologies if I mess up any nomenclature and if this is a little long-winded, but I did whittle my code down to a minimal "working" example. (the code below hangs up unless you comment out the while loop, as mentioned in the code itself)
I'm trying to figure out how I can have a Slider
change its value continuously after clicking a "wake" button and stop changing value after clicking a "sleep" button. For the sake of argument, say it changes value randomly to an integer between 0 and 99.
From what I've read so far (and I've read a lot, but these are the two most recent examples), it seems I should be able to use Platform.runLater()
or updateMessenger()
to avoid running an infinite-till-my-sleep-button-breaks-it loop on the UI thread. I so far have failed in implementing the former, and I'm still not sure how to implement the latter.
I also tried learning about property bindings so that I could have the Slider.valueProperty
change in the UI thread (changing a UI element in the UI thread makes sense... right?) while changing an intermediate variable, which I called state
and created from a class stateObject
, on a background thread. After the background thread calculates the value I want to give to the slider, it changes the state
's value, which is bound to the Slider
's value. I thought separating the value calculation into a background thread and Slider
value-changing into the UI thread would do the trick, but I keep getting the hang-up or infamous IllegalStateException
exception, Not on FX application thread
I'm not wholly sure how the Platform.runLater()
magic is supposed to work, but I still get a hang-up whether I run my runnable interface inside that or a Thread()
.
Any help or explanation as to why these attempts have failed/how to make things work would be greatly appreciated.
Cheers, Mike
import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.geometry.*;
//Not sure which of these, if any, I actually need
import javafx.event.*;
import javafx.concurrent.*;
import javafx.beans.property.*;
public class MWE extends Application {
//Main method
public static void main(String[] args) {
launch(args);
}
//Class fields
Button sleepBtn, wakeBtn;
Slider displaySldr;
boolean wakeful = true;
//state property class
class stateObject {
//Store the property
private DoubleProperty state = new SimpleDoubleProperty();
//Define getter for property value
public final double getState() {
return state.get();
}
//Define setter for property value
public final void setState(double val) {
state.setValue(val);
}
//Define getter for property itself
public DoubleProperty stateProperty() {
return state;
}
}
//Create state
stateObject state = new stateObject();
//Start method
@Override
public void start(Stage primaryStage) {
//Create top-level pane to store nodes into for the scene
VBox mainPane = new VBox(20);
mainPane.setAlignment(Pos.CENTER);
//Create buttons
sleepBtn = new Button("Sleep");
sleepBtn.setOnAction(e -> NPSleep());
wakeBtn = new Button("Wake");
wakeBtn.setOnAction(e -> NPWake());
//Lay out buttons
VBox buttonsBox = new VBox(10);
buttonsBox.getChildren().addAll(wakeBtn,sleepBtn);
buttonsBox.setAlignment(Pos.CENTER);
//Create slider to be change continuously by clicking wakeBtn once
//Slider should stop changing when sleepBtn is clicked
displaySldr = new Slider(0,100,50);
displaySldr.setMajorTickUnit(25);
displaySldr.setMinorTickCount(24);
displaySldr.setSnapToTicks(true);
displaySldr.setShowTickMarks(false);
displaySldr.setShowTickLabels(true);
//Make property event. When 'state' changes, change displaySldr value
state.stateProperty().addListener(
(observable,oldvalue,newvalue) ->
displaySldr.setValue(newvalue.intValue())
);
//Organize labels and sliders
VBox LSPane = new VBox(10);
LSPane.getChildren().addAll(displaySldr);
LSPane.setAlignment(Pos.CENTER);
//Organize sub-panes into top-level pane, mainPane
mainPane.getChildren().addAll(LSPane,buttonsBox);
//Set the scene
Scene mainScene = new Scene(mainPane);
//Set the stage
primaryStage.setTitle("oh god please work");
primaryStage.setScene(mainScene);
primaryStage.show();
}
private void NPWake() {
Runnable wakeyWakey = () -> {
while (wakeful == true) { //Commenting out the while loop and
//leaving the command allows program to run fine
//but slider changes only once, not continuously
state.setState( Math.floor(Math.random()*100) );
try{
Thread.sleep(300);
} catch (InterruptedException ie) {
}
}
};
Platform.runLater(wakeyWakey);
// Thread wakeThread = new Thread(wakeyWakey); //
// wakeThread.start(); // This doesn't work either
}
private void NPSleep() {
if (wakeful == true) {
wakeful = false;
}
}
}