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);
}
} );
Related
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
I'm creating a board game using a GUI and JFrames/JPanels where you can play against the computer. I have a method called showPieces() which updates board GUI by changing the image icons on an array of buttons (which are laid out in a grid format). Once the icons have been updated the revalidate() and repaint() methods to update the GUI.
The showPieces() method has a parameter that needs to be passed to it every time it is called.
The main issue I'm having is I want the human to make a move, update the GUI, wait 1 second, the computer makes it's move and then loop until someone wins.
My basic code is the following:
do{
human.makeMove();
gui.showPieces(data);
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
computer.makeMove()
gui.showPieces(data);
}while(playing);
This cause the issue where when the human player makes their move, the GUI will freeze for one second and then after the delay, both moves are made at the same time.
I hope it makes sense, but I'm a novice with Java and may have to look more into threading as I don't understand it well enough.
Thread.sleep() is done on the Event Dispatch Thread which will lock the GUI.
So If you need to wait for a specific amount of time, don't sleep in the event dispatch thread. Instead, use a timer.
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
};
new Timer(delay, taskPerformer).start();
As with most all similar Swing questions, you're putting your entire Swing GUI to sleep by calling Thread.sleep(...) on the GUI's event thread (the EDT or Event Dispatch Thread), and when during this period the GUI will not be able to update its images or interact with the user whatsoever. The solution here is not to use Thread.sleep(...) but rather to use a Swing Timer to cause your 1 second delay.
Swing Timer Tutorial.
I'm creating a board game using a GUI and JFrames/JPanels where you can play against the computer. I have a method called showPieces() which updates board GUI by changing the image icons on an array of buttons (which are laid out in a grid format). Once the icons have been updated the revalidate() and repaint() methods to update the GUI.
The showPieces() method has a parameter that needs to be passed to it every time it is called.
The main issue I'm having is I want the human to make a move, update the GUI, wait 1 second, the computer makes it's move and then loop until someone wins.
My basic code is the following:
do{
human.makeMove();
gui.showPieces(data);
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
computer.makeMove()
gui.showPieces(data);
}while(playing);
This cause the issue where when the human player makes their move, the GUI will freeze for one second and then after the delay, both moves are made at the same time.
I hope it makes sense, but I'm a novice with Java and may have to look more into threading as I don't understand it well enough.
Thread.sleep() is done on the Event Dispatch Thread which will lock the GUI.
So If you need to wait for a specific amount of time, don't sleep in the event dispatch thread. Instead, use a timer.
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
};
new Timer(delay, taskPerformer).start();
As with most all similar Swing questions, you're putting your entire Swing GUI to sleep by calling Thread.sleep(...) on the GUI's event thread (the EDT or Event Dispatch Thread), and when during this period the GUI will not be able to update its images or interact with the user whatsoever. The solution here is not to use Thread.sleep(...) but rather to use a Swing Timer to cause your 1 second delay.
Swing Timer Tutorial.
I read that all the code which constructs Swing components and handles Events must be run by the Event Dispatch Thread. I understand how this is accomplished by using the SwingUtilities.invokeLater() method. Consider the following code where the GUI initialization is done in the main method itself
public class GridBagLayoutTester extends JPanel implements ActionListener {
public GridBagLayoutTester() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
JButton button = new JButton("Testing");
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.WEST;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 1;
button.addActionListener(this);
add(button, gbc);
}
public void actionPerformed(ActionEvent e) {
System.out.println("event handler code");
}
public static void main(String[] args) {
JFrame frame = new JFrame("GridBagLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(new GridBagLayoutTester(), BorderLayout.CENTER);
frame.setSize(800, 600);
frame.pack();
frame.setVisible(true);
System.out.println("Exiting");
}
}
How is it that this code works perfectly ? We are constructing JFrame and calling a host of other methods in the main thread. I do not understand where exactly the EDT is coming into picture here (what code is it executing ?). The constructor of the GridBagLayoutTester class is also being called from the main method which means the EDT is not running it.
In short
When is the EDT being started ? (does the JVM start the EDT along with the main method if at all the EDT is started while running this code ?)
Does the event handler code for the button run on the EDT ?
The code works perfectly because you are constructing the frame in the main thread, before the EDT has an opportunity to interact with it. Technically, you shouldn't do this ever, but technically you can under this specific circumstance because you cannot interact with the JFrame until it becomes visible.
The main point to know is that Swing components are not thread safe. This means that they cannot be modified from more than one thread at the same time. This is solved by ensuring that all modifications come from the EDT.
The EDT is a thread that's dedicated to user interaction. Any events generated from the user are always run on the EDT. Any user interface updates run on the EDT. For example, when you call Component.repaint(), you can call this from any thread. This simply sets a flag to mark the component as needing a paint, and the EDT does it on its next cycle.
The EDT is started automatically and is tied quite closely into the system implementation. It is handled well within the JVM. Typically, it correlates to a single thread in the windowing system that handles user interaction. Of course, this is quite implementation-dependent. The nice thing is that you don't have to worry about this. You just have to know - if you interact with any Swing components, do it on the EDT.
Likewise, there is one other thing that's important. If you are going to do any long-duration processing or blocking for an external resource, and you are going to do it in response to an event generated by the user, you have to schedule this to run in its own thread off the EDT. If you fail to do this, you will cause the user interface to block while it waits for the long-duration processing to run. Excellent examples are loading from files, reading from a database, or interacting with the network. You can test to see if you are on the EDT (useful for creating neutral methods which can be called from any thread) with the SwingUtilities.isEventDispatchThread() method.
Here are two snippets of code which I use quite frequently when writing Swing programming dealing with the EDT:
void executeOffEDT() {
if (SwingUtilities.isEventDispatchThread()) {
Runnable r = new Runnable() {
#Override
public void run() {
OutsideClass.this.executeOffEDTInternal();
}
};
new Thread(r).start();
} else {
this.executeOffEDTInternal();
}
}
void executeOnEDT() {
if (SwingUtilities.isEventDispatchThread()) {
this.executeOnEDTInternal();
} else {
Runnable r = new Runnable() {
#Override
public void run() {
OutsideClass.this.executeOnEDTInternal();
}
};
SwingUtilities.invokeLater(r);
}
}
The Event Dispatch Thread, as it name implies, is called by Swing every time an event needs to be processed.
In the example you gave, the "Testing" button will automatically call the actionPerformed method when an action event needs to be processed. So, the content of your actionPerformed method will be invoked by the Event Dispatch Thread.
To answer your two final questions:
The EDT is started automatically when the Swing framework loads. You don't have to care about starting this thread, the JRE handles this task for you.
The event handler code is run by the EDT. All events your Swing interface generates are pooled and the EDT is responsible for executing them.
1) I don't know if in new JFrame or in setVisible but it initialize on demand and that's what the end of the main method (over the main process thread) doesn't terminate the process. the EDT was launched and is in a loop blocked waiting the next event.
2) Definitively. That loop receives from the OS the event, find the JButton and tells it that the event was fired. The button then calls the listeners. All that happens in the EDT.
You could review the Swing code that you call when you want to kill the process (or closing the main window) for looking where the EDT is terminated... that can give you a clue (I'll do it later! :)
EDT thread is started after that you firstly call setVisible(true); if it is not started already, ofc. Or if you call SwingUtilities.invokeAndWait() or SwingUtilities.invokeLater() methods.
See http://www.leepoint.net/JavaBasics/gui/gui-commentary/guicom-main-thread.html
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.