1

I wanted to do the following:

  1. click a button
  2. colors a rectangle red
  3. waits 1 sec
  4. colors a rectangle blue

In the following code:


GraphicsContext gc;
Button myButton = new Button("Button!");
myButton.setOnAction(new EventHandler<ActionEvent>(){
        @Override
        public void handle(ActionEvent event){
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    gc.setFill(Color.RED);
                    gc.fillRect(0, 0, 100, 100);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                    gc.setFill(Color.BLUE);
                    gc.fillRect(0, 0, 100, 100);
                }
            });
        }
    });

This works fine. It creates a red box at 0,0 with width & height of 100. But when I try to modify say, a Label, it crashes.


GraphicsContext gc;
Pane root = new Pane();
Button myButton = new Button("Button!");
Label myLabel = new Label("HELLO!"); // added
root.getChildren.add(myLabel);
myButton.setOnAction(new EventHandler<ActionEvent>(){
        @Override
        public void handle(ActionEvent event){
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    gc.setFill(Color.RED);
                    gc.fillRect(0, 0, 100, 100);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                    gc.setFill(Color.BLUE);
                    gc.fillRect(0, 0, 100, 100);
                    myLabel.setText("WORLD!"); // modified here
                }
            });
        }
    });

The result I'm expecting is,

  1. click a button

  2. colors a rectangle red

  3. waits 1 sec

  4. colors a rectangle blue

  5. then changes the label text from HELLO! to WORLD!

But I'm getting an error. Why is this happening? Can I not modify any Pane elements in SwingUtilities?

Any advice will be helpful.


EDIT

This is the error I get when I press the button

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Not on FX application thread; currentThread = AWT-EventQueue-0
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:229)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
at javafx.scene.Parent$2.onProposedChange(Parent.java:367)
at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:575)
at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:204)
at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(LabelSkin.java:49)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$61(BehaviorSkinBase.java:197)
at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(MultiplePropertyChangeListenerHandler.java:55)
at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:103)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:144)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
at javafx.scene.control.Labeled.setText(Labeled.java:145)
at Lego$1$1.run(Lego.java:63)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
fabian
  • 80,457
  • 12
  • 86
  • 114
Humble Thinker
  • 111
  • 2
  • 10

2 Answers2

3

This is because any Java FX components must be managed by the JavaFX Application Thread due to the fact that they are not thread-safe (this is for the exact same reason that any Swing components must be managed by the AWT event dispatching Thread), so you need to modify your label indirectly using Platform.runLater(runnable) as next:

With Java 8

Platform.runLater(() -> myLabel.setText("WORLD!"));

With previous versions of Java

Platform.runLater(new Runnable() {
        public void run() {
            myLabel.setText("WORLD!"); 
        }
    }
);

We use SwingUtilities.invokeLater(runnable) to make the AWT event dispatching thread execute some code that will modify Swing components at some unspecified time in the future, it is the same idea with Platform.runLater(runnable) in case of Java FX components.

NB: Use only Swing components or only Java FX components, avoid mixing Swing and JavaFX components unless you have no other choices.

Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122
  • 1
    You are the man. That makes sense. I'm fairly new to GUI with java, so I was very confused between javafx and swing and stuff. – Humble Thinker Oct 11 '16 at 17:30
1

Never pause JFXAT, dont sleep on it , you kill responsiveness , create a thread , sleep on it , and then inside of it use Platform.runLater to update UI.

updateLbl(){
    new Thread(new Runnable(){
         Thread.sleep();//sleep //try catch
         Platform.runLater ... //inside update ui
    }).start();
}
Tomas Bisciak
  • 2,801
  • 5
  • 33
  • 57
  • I think I get what you're saying. If I have multiple buttons that both draw something, and if I run one of them with sleep, it pauses any other GUI updates (but still updates like texts and printf stuff). Is there a way for me to only pause that specific graphics? – Humble Thinker Oct 11 '16 at 17:31
  • 1
    As i sayd , never! Put JFXAT to sleep , create another thread that you make sleep instead , and on that thread you call runLater (after your sleep) to update UI , that way you dont pause your UI/JFXAT thread and action is delayed . – Tomas Bisciak Oct 11 '16 at 17:43
  • I got it to work with creating a new thread. But then I assume every time I want to sleep, I need to make a new thread inside of newly created thread? Say I want to pause one more time, can I just sleep in that thread again (I assume this one is correct) or do I make a new thread inside of that thread? – Humble Thinker Oct 11 '16 at 18:11
  • Feel free to keep it alive as long as you need it , once you dont need it it will get garbage collected , so if you wanna sleep one more time and update it to something else just put another sleep after your runLater and then use RunLater again to do another update .So you will -wait,update,-wait,update,There is no need to make another thread within that thread what purpose that would have?You only need that one thread to escape from JFXAT, if your method is called on JFXAT, just remember dont put that sleep() into RunLater, or on method that is invoked from JFXAT – Tomas Bisciak Oct 11 '16 at 18:16
  • Thank you! Lastly, what does AT stands for in JFXAT? – Humble Thinker Oct 11 '16 at 18:33