just a question about SwingUtilities.InvokeLater().
To my understanding, any time I update my Swing interface I need to call SwingUtilities.InvokeLater to get onto the EDT. Does this need to be done if I am attempting to update my GUI from a button listener, as they button events are already on the EDT?
i.e, would i have to..
public void mouseClicked(MouseEvent e) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//GUI updates
}
});
}
or would I simply be able to...
public void mouseClicked(MouseEvent e) {
//GUI updates
}
Furthermore, Does the same logic apply if I am calling a method on an object that will update the GUI?
any time I update my Swing interface I need to call SwingUtilities.InvokeLater to get onto the EDT
Correct. This includes any time you update the model of the component, since this will also result in the repainting of the component.
Does this need to be done if I am attempting to update my GUI from a button listener, as they button events are already on the EDT?
Again correct. Since the code is automatically invoked on the EDT you do not need to manually add it to the EDT using the invokeLater().
You typically use the SwingUtilities.invokeLater() if your code is executing on a separate Thread and part of that logic needs to update a GUI component.
Updating this message :
Swing event handling code runs on a special thread known as the event dispatch thread. So all of the component(button, checkbox, radio button etc.,) actions are handled on EDT. So no need to have SwingUtilities.invokeLater() inside your button action as it always runs on EDT.
Tasks on the event dispatch thread must finish quickly; if they don't, unhandled events back up and the user interface becomes unresponsive.
So if you are planning to perform a long running task that could affect a GUI inside an action then better go for Worker Threads or Background Threads.
SwingWorker has doInBackground(), done() and process() methods to handle the long running tasks well without impacting the GUI.
Go through below links to get more info
Why does SwingUtilities.invokeLater() cause JButton to freeze?
What does SwingUtilities.invokeLater do?
https://www.javacodegeeks.com/2012/12/multi-threading-in-java-swing-with-swingworker.html
Related
I have the following Java Code which adds a JRadioButton to a JPanel and handles its mouse click event
JRadioButton offline = new JRadioButton();
offline.setText("Offline Mode");
modePanel.add(offline);
modePanel.setLayout(new GridLayout(2,1));
offline.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
offlineClicked(evt);
}
});
The function offlineClicked takes roughly around 1 min to be executed completely.
And until its execution is completed no other actions performed are handled.
All actions performed thereafter seem to go to a Eventqueue and handled FIFO when the offlineClicked has completed execution.
Due to this the UI seems to have gone into a hung state.
What can be done to make swing handle events concurrently and not wait till the last is executed completely.
When the mouselistener event is fired it runs on the event dispatch Thread (the swing gui thead that redraws the screen). If you put logic code in the gui thread then your gui would freeze until the logic completes and returns the gui thread back to swing. You can use swingworker or another option is to simply start a new thread and let the gui thread return so it can let other gui events process. In the new thread do your time consuming logic, it's running off of the event loop so swing won't freeze as it's running async. You MUST run all swing code on the dispatch thread so when the logic is done since you are no longer on the dispatch thread you have to add it to the event queue.
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
// you can now safely use swing components
new frame.setVisible(true);
}
} );
The purpose of the following code is to handle menu option choice. Because processing in method update() takes quite a bit time, I want to display info before this takes place.
My info is simply an anchor pane with label in it, which normally is set to be not visible. Unfortunately, the code below sets anchor pane visible only for a very short time after update() terminates. How can I do it properly?
public void updateRates(ActionEvent event)
{
updateInfo.setVisible(true);
update();
updateInfo.setVisible(false);
}
Most user interface libraries are single threaded. That thread is created by and controlled by the UI system. All listeners are called on that thread. When the UI thread is held up, such as when your update() call is running, the UI does not redraw at all, and keyboard events and mouse events will “pile up” in the event queue, appearing to be ignored, until the thread is allowed to continue running (at which point all of them will be processed immediately, in order).
For this reason, lengthy operations must never execute on the UI thread. One option is to run a Task in a different thread:
Task<Boolean> updater = new Task<Boolean>() {
#Override
public Boolean call() {
updateValue(true);
update();
return false;
}
};
updateInfo.visibleProperty().bind(updater.valueProperty());
new Thread(updater).start();
You could also create a Thread from a plain Runnable:
updateInfo.setVisible(true);
Runnable updater = new Runnable() {
#Override
public void run() {
update();
Platform.runLater(() -> updateInfo.setVisible(false));
}
};
new Thread(updater).start();
Platform.runLater is required in this case, because user interface objects, including all JavaFX Nodes, may only be accessed and modified in the UI thread. If your update() method is manipulating any Nodes, it will need to use Platform.runLater to do so.
The Task class has built-in support for showing work progress in the UI, but since you haven’t included the code for your update() method, I can’t tell you whether that support would be useful in your situation.
I had a problem earlier where I was trying to add objects to my canvas but the SwingUtilities.invokeLater hadn't really done its job yet. I really need to be able to add things reliably to this JPanel but I keep running into this same error. Is there a better alternative to the below code?
private void startNewGame() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
gameFrame = new InvadersGameFrame();
}
});
}
See SwingUtilities.invokeAndWait(Runnable) which:
Causes doRun.run() to be executed synchronously on the AWT event dispatching thread. This call blocks until all pending AWT events have been processed and (then) doRun.run() returns. This method should be used when an application thread needs to update the GUI.
I have tried a lot, but can't seem to get it to work.
I was told to use EDT with the following example.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Modify the GUI here
}
});
I have read on this topic a lot and still don't understand. I get what a thread is, but the .invokeLater still makes no sense to me. Honestly if you can explain in detail this it would be a big help!
Goal of Program: To get the randomly generated key that is constantly created every second to update itself afterward in the GUI.
So there is an EDT (Event Dispatch Thread). All actions that appear on your screen are executed by the EDT. There is only one EDT per Swing application.
You are in some arbitrary thread and you want to update the GUI through that thread? Well like I said there is only one EDT for each swing application, so you have to tell that EDT to display the label (or whatever context you want).
The idea here, is you push this Runnable onto a queue that the EDT pulls from. Eventually, your runnable will be processed by the EDT when all other actions before it are completed.
I recommend you get the book Filthy Rich Clients. There's a chapter where they explain Swing's threading model to great detail.
Basically in Swing, any code that modifies the GUI should be executed in the Event Dispatcher Thread. The SwingUtilities class that you are using there provides you with an easy way to post events to the event queue that is then dispatched by the EDT. That's what the invokeLater method does, it takes a new Runnable() as argument which is ultimately executed on the EDT.
From the book:
The invokeLater() implementation takes
care of creating and queuing a special
event that contains the Runnable. This
event is processed on the EDT in the
order it was received, just like any
other event. When its time comes, it
is dispatched by running the
Runnable’s run() method.
This is a pretty common element of all GUI programming. You have one thread that handles drawing the GUI, getting input, and running callbacks. If another thread tries to change the GUI related objects, it will conflict with the GUI thread. Say, for example, it was half way through drawing something and you change the color from a different thread.
All invokeLater does is queue up something for the GUI thread to run. By "later" it's really runs almost instantly but the current thread doesn't have to wait for it. The GUI thread may be doing a draw or waiting for a callback to return which would delay executing the code you gave it.
Needs to be a member so we can change it and still use it from an inner class
protected long secret=0;
... this needs to be in your code somewhere it'll get run...
JFrame f = new JFrame("foo");
new Thread(){
public void run() {
for(;;){
try {
sleep(1000);
} catch Interrupted (Exception ix){
return;
}
// TODO update your secret key here
// please don't use random()
SwingUtilities.invokeLater(new Runnable() {
public void run() {
f.setTitle("secret "+x);
}
});
}
}
}).start();
....
Only ever update Swing from the EDT so that it paints properly.
When you are in the EDT ( running code in an event handler) you can call paintImmediately() if you really must.
If you're all looking to do is update the UI on a known schedule, try something like the following. This assumes that a JFrame is the component you wish to update every 1 second.
private static final int WAIT_LENGTH = 1000; // 1 second
private JFrame frame = new JFrame();
// Thread will update the UI (via updateUI() call) about every 1 second
class UIUpdater extends Thread {
#Override
void run() {
while (true) {
try {
// Update variables here
}
catch (Exception e) {
System.out.println("Error: " + e);
}
finally {
frame.repaint();
Thread.sleep(WAIT_LENGTH);
}
}
}
}
To start this thread:
UIUpdater t = new UIUpdater();
t.start();
I've built a form with Netbeans's visual editor. When I press one of the buttons it should do the following :
set it to disabled
perform a task that takes some time
when the task finishes the button will be enabled again
However, the following happens:
the button remains in a pressed state until the task finishes
when the task finishes, the enabling/disabling of buttons will be very fast (they will happen, but you won't notice them)
This behaviour is not something I want. I tried using repaint on the JButton, on the JFrame and even on the JPanel containing the button, but I can't seem to get it to do what I want. Some hints?
When you do work in a button callback, you are stalling the GUI painting thread until it completes.
What you need to do is spawn a thread to do the long running task, and then have that thread use SwingUtilities.invokeLater() to update the UI when it completes. Not using invokeLater is not thread safe, and is generally bad mojo.
A basic example is:
button.setEnabled(false);
new Thread(new Runnable() {
public void run() {
// Do heavy lifting here
SwingUtilies.invokeLater(new Runnable() {
public void run() {
button.setEnabled(true);
}
});
}
}).start();
When you do things in a button callback, you are essentially stalling the gui painting thread - not just for the button, but for ANY gui painting. (Try covering the interface with another window and then exposing it again - it won't repaint until the task is finished!)
What you need to do is spawn a thread to do the long running task, and then have that thread use SwingUtilities.invokeLater() to do the enabling of the button. invokeLater forces the button enable to happen in the gui painting thread.
You may want to set a busy cursor or otherwise lock the interface while the long-running thread is operating.
The Concurrency in Swing tutorial from Sun is well worth a read. Excellent explanation and background reading, including the event dispatching thread, using worker threads, etc
You need to do the task that takes some time in a different thread.
The reason the button is blocking is because the work is being done in the same thread that draws the button. Once the work is done the button can do the rest of what you tell it to.
If you use a different thread the thread will go do the task while the drawing code can continue drawing the form.