So my program has multiple classes and after one of them has run, it'd like it so it appends the text area in the main class GUI with a 'finished' message
ta.append("Search Complete\n");
and this is the code that needs to complete
statCl.addActionListener(new ActionListener(){
public void actionPerformed (ActionEvent e) {
try {
ta.append("Searching...\n");
task.execute();
} catch (Exception IOE) {}
}
});
So it is in the class where task where actual code runs.
Any advice or help would be amazing, thanks.
If the task.execute() method doesn't start launch an operation in another thread, then the GUI will be freezed, and nothing will apear in the text area until the operation is finished. So you might just have
ta.append("Searching...\n");
task.execute();
ta.append("Finished");
If the operation is launched in a new thread, then this thread should append in the text area, but it should make sure this append is done in the event dispatch thread (EDT). Your code could thus look like this :
public class Task {
private JTextArea ta;
public Task(JTextArea ta) {
this.ta = ta;
}
public void execute() {
Thread t = new Thread(new Runnable() {
// perform the long operation
// at the end, update the text area, in the EDT
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ta.append("finished");
}
});
}
t.start();
}
}
You might also look at SwingWorker, which is designed just for that (and other things like progress update). There is a code example in its class javadoc which does just what you're trying to do.
You should not be performing long-running task on EDT (event dispatching thread):
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
Swing does all it's work on EDT, so you should not block EDT, e.g. run intensive tasks on it. Note: all event handlers are executed on EDT.
So there are two challenges:
Run intensive tasks in a background thread.
Update GUI, which must be done on EDT.
Use SwingWorker to solve this two issues.
Related
Related to my previous question: Call repaint from another class in Java?
I'm new to Java and I've had a look at some tutorials on SwingWorker. Yet, I'm unsure how to implement it with the example code I gave in the previous question.
Can anyone please explain how to use SwingWorker with regards to my code snippet and/or point me towards a decent tutorial? I have looked but I'm not sure I understand yet.
Generally, SwingWorker is used to perform long-running tasks in Swing.
Running long-running tasks on the Event Dispatch Thread (EDT) can cause the GUI to lock up, so one of the things which were done is to use SwingUtilities.invokeLater and invokeAndWait which keeps the GUI responsive by which prioritizing the other AWT events before running the desired task (in the form of a Runnable).
However, the problem with SwingUtilities is that it didn't allow returning data from the the executed Runnable to the original method. This is what SwingWorker was designed to address.
The Java Tutorial has a section on SwingWorker.
Here's an example where a SwingWorker is used to execute a time-consuming task on a separate thread, and displays a message box a second later with the answer.
First off, a class extending SwingWorker will be made:
class AnswerWorker extends SwingWorker<Integer, Integer>
{
protected Integer doInBackground() throws Exception
{
// Do a time-consuming task.
Thread.sleep(1000);
return 42;
}
protected void done()
{
try
{
JOptionPane.showMessageDialog(f, get());
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
The return type of the doInBackground and get methods are specified as the first type of the SwingWorker, and the second type is the type used to return for the publish and process methods, which are not used in this example.
Then, in order to invoke the SwingWorker, the execute method is called. In this example, we'll hook an ActionListener to a JButton to execute the AnswerWorker:
JButton b = new JButton("Answer!");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
new AnswerWorker().execute();
}
});
The above button can be added to a JFrame, and clicked on to get a message box a second later. The following can be used to initialize the GUI for a Swing application:
private void makeGUI()
{
final JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new FlowLayout());
// include: "class AnswerWorker" code here.
// include: "JButton" b code here.
f.getContentPane().add(b);
f.getContentPane().add(new JButton("Nothing"));
f.pack();
f.setVisible(true);
}
Once the application is run, there will be two buttons. One labeled "Answer!" and another "Nothing". When one clicks on the "Answer!" button, nothing will happen at first, but clicking on the "Nothing" button will work and demonstrate that the GUI is responsive.
And, one second later, the result of the AnswerWorker will appear in the message box.
Agree:
Running long-running tasks on the Event Dispatch Thread (EDT) can cause the GUI to lock up.
Do not agree:
so one of the things which were done is to use SwingUtilities.invokeLater and invokeAndWait which keeps the GUI responsive..
invokeLater still runs the code on the EDT, and can freeze your UI!! Try this:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
At least I, cannot move my mouse once I click the button which triggers the actionPerformed with the above code. Am I missing something?
I have something I can't understand: my Swing GUI contains a 'play' and 'pause' button. I have also a static variable that defines 'ON' and 'OFF' states. (The main program generates the GUI).
By cliking on 'play' I change the state of my static variable to 'ON' and I launch a time-consuming process in a thread that also modifies the GUI. As long as the static variable is 'ON' loops in the same process. Clicking on 'pause' would change the static variable to OFF.
But by clicking on 'play' the GUI is freezing and consequently:
The GUI doesn't update
The process can't be 'paused' with my 'pause' button.
I have heard about EDT and SwingWorker but I you have a simple way to do it I take it.
Thank you for your help and forgive my bad english...
The problem is that you're doing the intensive, time-consuming work on the same thread responsible for updating the GUI. SwingWorker allows you to move time-consuming tasks to a separate thread of execution, thereby leaving the UI thread to do its thing uninhibited.
However, it does add a further complication: affinity. Calling methods on UI components generally requires that you do so from the UI thread. Therefore, you need to use special functionality to get back to the UI thread from the worker thread. SwingWorker also gives you this ability.
I suggest you read through this documentation.
You need to read Concurrency in Swing to understand how the EDT and SwingWorkers operate.
All GUI updates are executed on the EDT so when you click a GUI component any method that this calls will be executed on the EDT. If this is a time consuming process then this will block the EDT from executing any futher GUI updates. Hence your GUI is freezing and you can't click the pause button.
You need to use SwingWorker to execute the time consuming process on another thread. The link I provided above details how to do this.
You should not start long-running processes in Swing’s event handler because it will freeze your GUI, you know that now. :) Start it in a new thread. You only need to use a SwingWorker if you’re planning on manipulating the GUI from the worker thread (because Swing is not thread-safe).
This is a pretty straightforward reason: while Java is working on your time-consuming process, it isn't able to update the GUI. Solution: run the time-consuming process in a separate thread. There are a bunch of ways to program that, and it would probably depend somewhat on how your program is written.
The event dispatch thread (EDT) is the only thread in which it's safe to read or update the GUI.
The pause button should be setting the on/off variable in the event dispatch thread.
The time-consuming operation, and the loop, should not be in the EDT. (The loop should also not be running continuously doing nothing but check the variable, or it can easily eat all your CPU. If it has nothing else to do it should check, and then call Thread.sleep() for some length of time (say 100ms).)
If you can prove that the on/off variable is being set to OFF, but that nonetheless it's always read as ON, it may be that the variable's value is not being copied from the EDT to the worker thread. Make it volatile, or synchronize access to it, or use an AtomicReference, or read it in the EDT using SwingUtilities.invokeAndWait().
SwingWorker probably is the simplest way to go, here. Implement your time-consuming operation, and the on/off check, in the doInBackground() method, and your GUI update in the done() method.
public enum State {
RUNNING, STOPPED
}
public class ThreadSafeStateModel {
private State state = State.STOPPED;
public synchronized void stop() {
state = State.STOPPED;
}
public synchronized void start() {
state = State.RUNNING;
}
public boolean isRunning() {
return state == State.RUNNING;
}
}
public class ExpensiveProcessWorker extends SwingWorker<Void, Void> {
private final ThreadSafeStateModel model;
public ExpensiveProcessWorker(ThreadSafeStateModel model) {
this.model = model;
}
#Override // Runs in background
protected Void doInBackground() throws Exception {
while (model.isRunning()) {
// do one iteration of something expensive
}
return null;
}
#Override // Runs in event dispatch thread
protected void done() {
// Update the GUI
}
}
public class StopButton extends JButton {
public StopButton(final ThreadSafeStateModel model) {
super(new AbstractAction("Stop") {
#Override
public void actionPerformed(ActionEvent e) {
model.stop();
}
});
}
}
public class StartButton extends JButton {
public StartButton(final ThreadSafeStateModel model) {
super(new AbstractAction("Start") {
#Override
public void actionPerformed(ActionEvent e) {
model.start();
new ExpensiveProcessWorker(model).execute();
}
});
}
}
(A lot could be done to clean this up depending on the real application, but you get the idea.)
#Override
public void actionPerformed(ActionEvent e) {
new Thread() {
public void run() {
//your code which runs on click event
}
}.start();
}
I have something I can't understand: my Swing GUI contains a 'play' and 'pause' button. I have also a static variable that defines 'ON' and 'OFF' states. (The main program generates the GUI).
By cliking on 'play' I change the state of my static variable to 'ON' and I launch a time-consuming process in a thread that also modifies the GUI. As long as the static variable is 'ON' loops in the same process. Clicking on 'pause' would change the static variable to OFF.
But by clicking on 'play' the GUI is freezing and consequently:
The GUI doesn't update
The process can't be 'paused' with my 'pause' button.
I have heard about EDT and SwingWorker but I you have a simple way to do it I take it.
Thank you for your help and forgive my bad english...
The problem is that you're doing the intensive, time-consuming work on the same thread responsible for updating the GUI. SwingWorker allows you to move time-consuming tasks to a separate thread of execution, thereby leaving the UI thread to do its thing uninhibited.
However, it does add a further complication: affinity. Calling methods on UI components generally requires that you do so from the UI thread. Therefore, you need to use special functionality to get back to the UI thread from the worker thread. SwingWorker also gives you this ability.
I suggest you read through this documentation.
You need to read Concurrency in Swing to understand how the EDT and SwingWorkers operate.
All GUI updates are executed on the EDT so when you click a GUI component any method that this calls will be executed on the EDT. If this is a time consuming process then this will block the EDT from executing any futher GUI updates. Hence your GUI is freezing and you can't click the pause button.
You need to use SwingWorker to execute the time consuming process on another thread. The link I provided above details how to do this.
You should not start long-running processes in Swing’s event handler because it will freeze your GUI, you know that now. :) Start it in a new thread. You only need to use a SwingWorker if you’re planning on manipulating the GUI from the worker thread (because Swing is not thread-safe).
This is a pretty straightforward reason: while Java is working on your time-consuming process, it isn't able to update the GUI. Solution: run the time-consuming process in a separate thread. There are a bunch of ways to program that, and it would probably depend somewhat on how your program is written.
The event dispatch thread (EDT) is the only thread in which it's safe to read or update the GUI.
The pause button should be setting the on/off variable in the event dispatch thread.
The time-consuming operation, and the loop, should not be in the EDT. (The loop should also not be running continuously doing nothing but check the variable, or it can easily eat all your CPU. If it has nothing else to do it should check, and then call Thread.sleep() for some length of time (say 100ms).)
If you can prove that the on/off variable is being set to OFF, but that nonetheless it's always read as ON, it may be that the variable's value is not being copied from the EDT to the worker thread. Make it volatile, or synchronize access to it, or use an AtomicReference, or read it in the EDT using SwingUtilities.invokeAndWait().
SwingWorker probably is the simplest way to go, here. Implement your time-consuming operation, and the on/off check, in the doInBackground() method, and your GUI update in the done() method.
public enum State {
RUNNING, STOPPED
}
public class ThreadSafeStateModel {
private State state = State.STOPPED;
public synchronized void stop() {
state = State.STOPPED;
}
public synchronized void start() {
state = State.RUNNING;
}
public boolean isRunning() {
return state == State.RUNNING;
}
}
public class ExpensiveProcessWorker extends SwingWorker<Void, Void> {
private final ThreadSafeStateModel model;
public ExpensiveProcessWorker(ThreadSafeStateModel model) {
this.model = model;
}
#Override // Runs in background
protected Void doInBackground() throws Exception {
while (model.isRunning()) {
// do one iteration of something expensive
}
return null;
}
#Override // Runs in event dispatch thread
protected void done() {
// Update the GUI
}
}
public class StopButton extends JButton {
public StopButton(final ThreadSafeStateModel model) {
super(new AbstractAction("Stop") {
#Override
public void actionPerformed(ActionEvent e) {
model.stop();
}
});
}
}
public class StartButton extends JButton {
public StartButton(final ThreadSafeStateModel model) {
super(new AbstractAction("Start") {
#Override
public void actionPerformed(ActionEvent e) {
model.start();
new ExpensiveProcessWorker(model).execute();
}
});
}
}
(A lot could be done to clean this up depending on the real application, but you get the idea.)
#Override
public void actionPerformed(ActionEvent e) {
new Thread() {
public void run() {
//your code which runs on click event
}
}.start();
}
I'm making a simple game in Java using swing and am having problems with my GUI freezing up sporadically (due to threading issues most probably) after a button press that is supposed to trigger a switch in JPanels.
I posted a related thread here, which has more details about the actual code I'm currently using (although I did update the countdown and get that working fine). From answers to that thread, it seems like usingSwingUtilities.invokeLater() or invokeAndWait() might be what I need to solve the problem, but I'm not sure where in my code it is necessary or exactly how to implement it.
I don't know that much about threading and could use any help (preferably somewhat detailed and with some sample code) that I can get. Let me know if any further details would be useful.
See: Tutorial: Concurrency in Swing
Generally speaking, the Event Dispatch Thread is a single thread, chugging through the event queue, processing one at a time.
SwingUtilities.invokeLater(..)
puts a Runnable on this queue. So it will be processed by the EDT when the EDT finishes everything on the queue before it (This is why sleeping on the queue blocks other events like repainting). It's relatively unusual to call invokeLater(..) from the EDT itself, though there are situations where it is useful (usually as a hack). I don't think I have had a legitimate use for SwingUtilities.invokeAndWait(..) in the last 6 years. Maybe once.
javax.swing.Timer can be configured to fire once or periodically. When it fires, it puts an event on the EDT queue. If you have computationally-intensive processing that need to be done, consider using javax.swing.SwingWorker to do the computation on another thread, and give you back the result in a thread-safe manner (this is also comparatively rare).
A good point to look is the docs. In your case, this explains how SwingUtilities.invokeLater() works and where to use it:
Causes doRun.run() to be executed asynchronously on the AWT event
dispatching thread. This method should be used when an application
thread needs to update the GUI.
So, in your actions that modifies the GUI you must use the invokeLater method to assure that the GUI wont freeze.
Another good resource is the Java tutorials. They cover concurrency in Swing.
I have create a WorkerThread class which take care of Threads and GUI current/main thread . i have put my GUI application in construct() method of WorkerThread when an event fire to start XXXServer then all threads are activate and GUI work smoothlly wihout freeze. have a look.
/**
* Action Event
*
* #see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent ae) {
log.info("actionPerformed begin..." + ae.getActionCommand());
try {
if (ae.getActionCommand().equals(btnStart.getText())) {
final int portNumber = 9990;
try {
WorkerThread workerThread = new WorkerThread(){
public Object construct(){
log.info("Initializing the XXXServer ...");
// initializing the Socket Server
try {
XXXServer xxxServer = new XXXServer(portNumber);
xxxServer.start();
btnStart.setEnabled(false);
} catch (IOException e) {
// TODO Auto-generated catch block
log.info("actionPerformed() Start button ERROR IOEXCEPTION..." + e.getMessage());
e.printStackTrace();
}
return null;
}
};workerThread.start();
} catch (Exception e) {
log.info("actionPerformed() Start button ERROR..." + e.getMessage());
e.printStackTrace();
}
} else if (ae.getActionCommand().equals(btnStop.getText())) {
log.info("Exit..." + btnStop.getText());
closeWindow();
}
} catch (Exception e) {
log
.info("Error in ServerGUI actionPerformed==="
+ e.getMessage());
}
}
In order to invoke an action in the existing WorkerThread, one would intuitively send a user defined event using SwingUtilities.invokeLater() to a JFrame's actionPerformed() method as
class TestFrame extends JFrame implements ActionListener
{
...
private class Performer implements Runnable
{
ActionEvent event;
Performer(ActionEvent event)
{
this.event = event;
}
#Override
public void run()
{
actionPerformed(event);
}
}
synchronized protected void invokeLater(ActionEvent event)
{
SwingUtilities.invokeLater(new Performer(event));
}
public void actionPerformed(ActionEvent event)
{
...
}
}
Now, TestFrame.invokeLater() called in any Thread will be processed in TestFrame.actionPerformed() in existing WorkerThread .
Related to my previous question: Call repaint from another class in Java?
I'm new to Java and I've had a look at some tutorials on SwingWorker. Yet, I'm unsure how to implement it with the example code I gave in the previous question.
Can anyone please explain how to use SwingWorker with regards to my code snippet and/or point me towards a decent tutorial? I have looked but I'm not sure I understand yet.
Generally, SwingWorker is used to perform long-running tasks in Swing.
Running long-running tasks on the Event Dispatch Thread (EDT) can cause the GUI to lock up, so one of the things which were done is to use SwingUtilities.invokeLater and invokeAndWait which keeps the GUI responsive by which prioritizing the other AWT events before running the desired task (in the form of a Runnable).
However, the problem with SwingUtilities is that it didn't allow returning data from the the executed Runnable to the original method. This is what SwingWorker was designed to address.
The Java Tutorial has a section on SwingWorker.
Here's an example where a SwingWorker is used to execute a time-consuming task on a separate thread, and displays a message box a second later with the answer.
First off, a class extending SwingWorker will be made:
class AnswerWorker extends SwingWorker<Integer, Integer>
{
protected Integer doInBackground() throws Exception
{
// Do a time-consuming task.
Thread.sleep(1000);
return 42;
}
protected void done()
{
try
{
JOptionPane.showMessageDialog(f, get());
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
The return type of the doInBackground and get methods are specified as the first type of the SwingWorker, and the second type is the type used to return for the publish and process methods, which are not used in this example.
Then, in order to invoke the SwingWorker, the execute method is called. In this example, we'll hook an ActionListener to a JButton to execute the AnswerWorker:
JButton b = new JButton("Answer!");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
new AnswerWorker().execute();
}
});
The above button can be added to a JFrame, and clicked on to get a message box a second later. The following can be used to initialize the GUI for a Swing application:
private void makeGUI()
{
final JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new FlowLayout());
// include: "class AnswerWorker" code here.
// include: "JButton" b code here.
f.getContentPane().add(b);
f.getContentPane().add(new JButton("Nothing"));
f.pack();
f.setVisible(true);
}
Once the application is run, there will be two buttons. One labeled "Answer!" and another "Nothing". When one clicks on the "Answer!" button, nothing will happen at first, but clicking on the "Nothing" button will work and demonstrate that the GUI is responsive.
And, one second later, the result of the AnswerWorker will appear in the message box.
Agree:
Running long-running tasks on the Event Dispatch Thread (EDT) can cause the GUI to lock up.
Do not agree:
so one of the things which were done is to use SwingUtilities.invokeLater and invokeAndWait which keeps the GUI responsive..
invokeLater still runs the code on the EDT, and can freeze your UI!! Try this:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
At least I, cannot move my mouse once I click the button which triggers the actionPerformed with the above code. Am I missing something?