I've a ImageView within my view and try to display a WritableImage instance with it. I am drawing it within an outher thread and pass it to the view by listening to ObjectProperty's change event.
public void changed(ObservableValue<? extends Image> observable,
Image oldValue, Image newValue) {
this.imageView.setImage(newValue);
}
The imageView should be ready to recieve an image, it is shown by my mainView. But it is thrwoing an IllegalStateException from
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
Does anyone can explain this?
The exception basically tells you what the problem is: you are changing the state of part of the scene graph from a thread other than the FX Application Thread. The reason for this is that listener methods are invoked on the same thread that changes the property.
You have a couple of options for fixing this: one is to just use Platform.runLater(...). You can either do this in the listener:
public void changed(ObservableValue<? extends Image> observable,
Image oldValue, Image newValue) {
Platform.runLater(new Runnable() {
#Override
public void run() {
this.imageView.setImage(newValue);
}
});
}
or you can do the same thing to set the value of your property on the FX Application Thread.
You haven't shown much code, but it may also be possible for you to use a Task to compute the Image. So instead of something like:
new Thread(new Runnable() {
#Override
public void run() {
WritableImage image = new WritableImage(...);
/// draw on image....
myImageProperty.set(image);
}
});
you can do something like
Task<Image> imageTask = new Task<Image>() {
#Override
public Image call() {
WritableImage image = new WritableImage(...);
// draw on image....
return image ;
}
});
imageTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
myImageProperty.set(imageTask.getValue());
}
});
new Thread(imageTask).start();
(This is much cleaner in Java 8; I posted Java 7 compatible code as you used that style in the question).
Here you avoid the low-level API (Platform.runLater()), instead using one of the callback methods (setOnSucceeded) from Task, which is guaranteed to be called on the FX Application Thread.
Related
In my application continuously I am changing the UI depending on database values.
below is my code:
Here I am updating the UI depending on the database values.
public class Lane5 implements Runnable{
private final LSDU_MenuController lsduController;
String dbUrl;
public Lane(LSDU_MenuController aThis) {
this.lsduController5 = aThis;
}
while(true){
if(cmnd5.wim.equals("Y")){
javafx.application.Platform.runLater( new Runnable() {
#Override
public void run() {
lsduController.fiveWIM.setStyle("-fx-background-image: url('/images/Wim_T.png');-fx-background-position: center;-fx-background-repeat: no-repeat; -fx-background-color: none;");
lsduController.logTextArea.appendText(laneNameUi+" WIM Working State "+cmnd5.sdf.format(cmnd5.calendar.getTime())+"\n");
}
});
} else {
javafx.application.Platform.runLater( new Runnable() {
#Override
public void run() {
lsduController.fiveWIM.setStyle("-fx-background-image: url('/images/Wim_T.png');-fx-background-position: center;-fx-background-repeat: no-repeat; -fx-background-color: red;");
lsduController.logTextArea.appendText(laneNameUi+" WIM Not Working State "+cmnd5.sdf.format(cmnd5.calendar.getTime())+"\n");
}
});
}
}
Here I am Calling the Lane Class:
This is my controller class
public class LSDU_MenuController implements Initializable {
#Override
public void initialize(URL url, ResourceBundle rb) {
new Thread(new Lane5(this)).start();
}}
If I update the UI continuously like above by modifying with style sheet after some time I am getting below exception:
exception in thread main java.lang.outofmemoryerror java heap space
This one is related to JavaFx issue or my code issue.
If I update like as above that one gives this exception or not
"exception in thread main java.lang.outofmemoryerror java heap space"
I have a label in my fxml file:
<Label fx:id="labelA"/>
I want to update this in the controller during a background worker is executing. I have tried to do something like this:
public class FXHelloCVController
{
#FXML
private Label labelA;
#FXML
protected void startMatching(ActionEvent event)
{
SwingWorker<Boolean, String> worker = new SwingWorker<Boolean, String>()
{
#Override
protected Boolean doInBackground() throws Exception
{
for(int y=0; y<*something*; y++){
if(n.get(y)!=null){
...
publish(n.get(y).name);
...
}
}
return true;
}
#Override
protected void process(List<String> chunks) {
String n = chunks.get(chunks.size()-1);
labelA.setText(n);
}
};
worker.execute();
return;
}
But when the function "labelA.setText(n)" is called, it appears the following exception:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Not on FX application thread; currentThread = AWT-EventQueue-0
What is wrong?
Platform.runLater: If you need to update a GUI component from a non-GUI thread, you can use that to put your update in a queue and it will be handle by the GUI thread as soon as possible.
Platform.runLater(new Runnable() {
#Override public void run() {
// update an FXML node (e.g. label)
}
});
Source: Platform.runLater and Task in JavaFX
But you're using Swing and JavaFX, which isn't usually.
A SwingWorker is for Swing and not for JavaFX. Have a look at the documentation for the corresponding JavaFX concurrency mechanisms.
Looking to update GUI first thing upon click of a button however Platform.runLater executes at a later stage and am looking for the piece of code which updates the GUI to happen first thing upon click of a button.
Platform.runLater(new Runnable() {
#Override
public void run() {
//Update GUI here
}
});
Would highly appreciate if anyone can provide any inputs or recommendations.
Although the API specifies that Platform.runLater "runs the specified Runnable on the JavaFX Application Thread at some unspecified time in the future", it usually takes little to no time for the specified thread to be executed. Instead, you can just add an EventHandler to the button to listen for mouse clicks.
Assuming the controller implements Initializable
#FXML Button button;
#Override
public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
button.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
updateGUI();
}
});
}
private void updateGUI() {
// code
}
i have an application in java, and this have a one popup with javafx application (embed videos from Youtube). I see this correctly but when i close this popup, the javafx thread not close and javafx application running in background. This is my javafx class:
public class JavaFXClass extends Application {
#Override
public void start(final Stage stage) throws Exception {
final WebView webview = new WebView();
/*...*/
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
Platform.runLater( new Runnable() {
#Override
public void run() {
//I need stop javafx when this class close.
}
});
}
});
stage.show();
}
public static void LoadClass(String Data) { //I use this function to load class
/*...*/
launch(); //return error when i re-call this function (already launch).
}
If i put webview.getEngine().load(null); Platform.exit(); code in the "OnCloseRequest" works fine but an exception is created ("Attempt to call defer when toolkit not running")
i need use webview.getEngine().load(null); or similar because if i not use this, the video in webview remain playing in background. And if i not use Platform.exit() the main frame crashes (lock).
Sorry for my bad english, tried to write the best I could
use this:
[...]
stage.setOnCloseRequest(this.getCloseSystemEvent());
}
public EventHandler<WindowEvent> getCloseSystemEvent() {
return new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
Platform.exit();
}
};
}
Also, you should check the concurrency API. Your code prevents the runtime from closing the thread properly.
I want to set focus to a specific node in the content of a Tab. I added a ChangeListener to selectedItem property as follows (assume that the class contains a field named secondNode of type Node):
tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>() {
#Override
public void changed(ObservableValue<? extends Tab> observable, Tab oldValue, Tab newValue) {
if (newValue != null) {
Platform.runLater(new Runnable() {
#Override
public void run() {
secondNode.requestFocus();
}
});
}
}
});
However, this does not work. I assume the reason is because TabPane performs some additional actions, which affect focus, after the new tab has been selected (but, looking through TabPane source code, I can't figure out what). If I single-step through this code in a debugger, it works as expected, so it appears to be a race condition. If this is so, how can this be resolved?
You could try replacing the Runnable with a Task:
new Thread( new Task<Void>()
{
#Override
public Void call() throws Exception // This is NOT on FX thread
{
Thread.sleep(100);
return null;
}
#Override
public void succeeded() // This is called on FX thread.
{
secondNode.requestFocus();
}
}).start();