How to update fxml Label from a background worker? - java

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.

Related

Is there any issue if i am adding style continuously to label without removing the previous style?

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"

JavaFX ImageView throwing IllegalStateException

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.

Java JProgressBar does not show up by setVisible(true)

I have a method like below.
ProgressWindow is a sub class of JFrame containing JProgressBar.
addProgress() increments a value in the JProgressBar.
If I call this method from a method in another class, a frame of ProgressWindow will show up but not JProgressBar and some JLabels inside the frame. They show up after the last line (System.out.println("finish")).
If I call this method in a main method in the class containing this method, then every component (Bar, labels...) instantly shows up.
What can I do for showing the window correctly?
static void search(){
ProgressWindow window = new ProgressWindow();
window.setVisible(true);
ExecutorService execs = Executors.newFixedThreadPool(Runtime
.getRuntime().availableProcessors());
Collection<Callable<Void>> processes = new LinkedList<>();
for (int i = 0; i < 100; i++) {
processes.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
progressWindow.addProgress(); // increment progress value
return null;
}
});
}
try {
execs.invokeAll(processes);
} catch (Exception e) {
e.printStackTrace();
} finally {
execs.shutdown();
}
System.out.println("finish");
The main problem is you seem to be calling search from the context of the Event Dispatching Thread.
The problem occurs because you are using execs.invokeAll which blocks until all the callables have finished running.
This means that the EDT is unable to process new events in Event Queue, including repaint events, this is why your UI is coming to a stand still...
There are a number of issues you are now going to face...
You should never update/modify a UI component from any thread other than the EDT
You should block the EDT for any reason
You seem to want to know when the search is complete, so you know need some kind of event notification...
The first thing we need is some way to be notified that the search has completed, this means you can no longer rely on search returning when the search is complete...
public interface SearchListener {
public void searchCompleted();
}
Next we need an intermeditate search method that builds the UI and ensure that the search is launched within it's own Thread...
static void search(final SearchListener listener) {
final ProgressWindow window = new ProgressWindow();
window.setVisible(true);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
search(listener, window);
}
});
t.start();
}
Then we need to modify the original search method to utilise the SearchListener interface to provide notification when the search is complete...
static void search(final SearchListener listener, final ProgressWindow window){
ExecutorService execs = Executors.newFixedThreadPool(Runtime
.getRuntime().availableProcessors());
Collection<Callable<Void>> processes = new LinkedList<>();
for (int i = 0; i < 100; i++) {
processes.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
// This method needs to ensure that
// what ever it does to the UI, it is done from within
// the context of the EDT!!
progressWindow.addProgress();
return null;
}
});
}
try {
execs.invokeAll(processes);
} catch (Exception e) {
e.printStackTrace();
} finally {
execs.shutdown();
}
System.out.println("finish");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
listener.searchCompleted();
}
});
}
Now, without the source code for addProgress, I might be tempted to use
processes.add(new Callable<Void>() {
#Override
public Void call() throws Exception {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
progressWindow.addProgress();
}
});
return null;
}
});
}
Instead...
Take a look at Concurrency in Swing for more details
Sounds like you what you're wanting to do is invoke the setVisible on the Swing UI thread, you can do this with invokeAndWait or invokeLater.
So something like:
final ProgressWindow window = new ProgressWindow();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
window.setVisible(true);
}
});

Label issues with GUI

I'm fairly new to Java and I am trying to make a GUI. This is the code in my GUI.java file. It contains a button and a label. When I click the button, the label is supposed to show "loading..." and enter a static void method in the Main class (in Main.java) called searcher and does stuff. After searcher is done, label becomes "".
Problem: Label doesn't change at all when I press press the button. Seems like neither the setText in the actionListener nor searcher() works. However, the other "stuff" I wanted it to do inside searcher() still works fine. I don't see any errors.
Note: If I try to call searcher() from the main it works fine.
GUI.java:
public class GUI extends JFrame implements ActionListener{
public JButton button = new JButton("Refresh!");
public JLabel label = new JLabel("");
public GUI(){
Container pane = getContentPane();
button.addActionListener(this);
button.setActionCommand("refresh");
pane.add(button);
pane.add(label);
}
}
public void actionPerformed(ActionEvent e) {
if ("refresh".equals(e.getActionCommand())) {
label.setText("Loading...");
Main.searcher(this, "", "");
label.setText("");
}
}
Main.java:
public class Main{
public static void searcher(GUI gu, String popup, String url) {
gu.label.setText("Loading...");
//do stuff
gu.label.setText("");
}
public static void main(String[] args) {
GUI gu = new GUI ();
}
}
EDIT: I've changed to code to use SwingWorker and propertylistener as suggested, but I'm having trouble now. Firstly, 'this' no longer refers to the GUI.. what should I pass in the searcher method to pass the current instance of class GUI?
I'm also getting this error and I'm not really sure how to fix it:
.\GUI.java:77: error: is not abstract and does not override abstract method propertyChange(PropertyChangeEvent) in PropertyChangeListener
PropertyChangeListener propChangeListn = new PropertyChangeListener() {^
public void actionPerformed(ActionEvent e) {
if ("refresh".equals(e.getActionCommand())) {
label.setText("Loading...");
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
public Void doInBackground() throws Exception {
Main.searcher(this, "", "http://maple.fm/api/2/search?server=0");
return null;
}
};
//worker.addPropertyChangeListener(new propertyChangeListener listener) {
PropertyChangeListener propChangeListn = new PropertyChangeListener() {
public void propertyChanged(PropertyChangeEvent pcEvt) {
if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
label.setText("");
}
}
};
worker.addPropertyChangeListener(propChangeListn);
worker.execute();
}
Yours is a classic Swing threading issue where you are tying the Swing event thread with a long-running process, preventing this thread from updating the GUI's graphics or from interacting with the user. The solution is the same as always -- use a background thread to do your long-running processing. If you used a SwingWorker for this, you could even add a PropertyChangeListener to it and then be notified when the worker has completed its task, allowing you to update the GUI with this information.
Google Concurrency in Swing and click on the first hit for more on this.
e.g.,
public void actionPerformed(ActionEvent e) {
if ("refresh".equals(e.getActionCommand())) {
label.setText("Loading...");
// create a SwingWorker object
final SwingWorker<Void, Void> worker = new Swingworker<Void, Void>() {
// long running code would go in doInBackground
public Void doInBackground() throws Exception {
Main.searcher(...);
return null;
}
}
// add a listener to worker to be notified when it is done
worker.addPropertyChangeListener(PropertyChangeListener listener) {
public void propertyChanged(PropertyChangeEvent pcEvt) {
// if the worker is done...
if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
label.setText("");
// you will probably want to call get() on your worker here
// and catch any exceptions that may have occurred.
}
}
}
// it may seem counter-intuitive, but you need to start the worker with
// execute() *after* setting all the above code up.
worker.execute();
}
}

Update components GUI in JavaFX

I need to update some components (Labels, ProgressBar, button) from an handle function in javafx.
The dialog is a modal dialog and it's used to perform sequentially some operations.
#FXML public void updateHandle(ActionEvent action){
buttonSend.setDisable(true);
/* Operation 1 */
progressBar.setProgress(0.05);
label.setText("Init..");
myInitFunction();
myVar = new var(); //global
/* Op2 */
progressBar.setProgress(0.10);
label.setText("Check connection..");
myConnFunction();
// ....
// ....
}
The problem is that all my functions are correctly processed, but the elements on the GUI didn't change.
EDIT
I tried to use Platform.runlater but it seems to don't work...
void updateLabelLater(final Label label, final String text) {
Platform.runLater(new Runnable() {
#Override public void run() {
label.setGraphic(null);
label.setText(text);
}
});
}
void updateProgressBar(final ProgressBar progressBar, final double val){
Platform.runLater(new Runnable() {
#Override public void run() {
progressBar.setProgress(val);
}
});
}
Is updateHandle running on the Event Thread? I didn't bother with FXML because of tooling issues so this isn't much of an answer. (But hopefully it helps!)
//Pseudo Code
public doLongRuningOperation(final Object ... objects){
final Thread longRunning = new Thread(){
public void run(){
update("this");
sleep(1000); //Pause/do something etc...
update("should");
sleep(1000);
update("update");
System.err.println("completed");
}
};
longRunning.start(); //Start this process
}
//Rejoin the UI Thread and update it...
public void update(final String text){
//reference to label 'lbl'
Platform.runLater(new Runnable(){
lbl.setText(text);
});
}

Categories