I have a thread that I start when I press the start button. What I want to do is repaint the labels so they contain the information that my thread makes changes to. The only problem I am facing is that the jLabels repaint only after the thread is done running. Can someone give me any sugestions on how I can make it repaint while the thread is runnong? Thanks.
Here is a snippet of my code:
Store m = new Store(); //Store extends Thread
private void startActionPerformed(java.awt.event.ActionEvent evt) {
....
//I get the simulation time of the store from a textbox
//the thread runs for this number of seconds
//when it is done, the store is closed(the open variable is set to false)
....
m.start();
while (m.isOpen()) {
queue0.setText(String.valueOf(m.clientiCasai(0)));
//here I will have more queues
....
noOfClients.repaint(); //this is the label that should show the number of clients in queue 0
}
}
The problem is that the actual painting is also done during the EDT's event loop; your while() loop is basically preventing the EDT from proceeding.
One possible workaround would be to have an extra thread that would take care of updating the label.
Your startActionPerformed() method should not run on EventDispatchThread (EDT) which is the Thread that shall be used for all Swing Modification operations. If you block the EDT your UI will not repaint, it will freeze.
Your noOfClients.repaint() call should be done on EDT, but also your call setting the new value to queue0 label should be on EDT.
For simplification. If you your queue0.setText() call on EDT, the repainting will be done for you, so you can remove it.
This can be achieved by calling:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
queue0.setText("<text>");
}
});
To solve your problem you could either pass a reference to the instance holding the method startActionPerformed() to your Store and call it from there when needed or you could start another Thread that monitors the Store progress and propagates it to the Swing EDT.
Related
I im creating a simple testing app that runs a check every hour on the selected directory/s using thread.sleep() through JFileChooser. But when i select the directory and the method runs the ui panel goes grey and the swing bits disappear. The thread seems to be putting the ui to sleep as well as the method its calling.
if (option == JFileChooser.APPROVE_OPTION) {
selectedDirectory = chooser.getSelectedFiles();
try {
while (true) {
runCheck(selectedDirectory);
Thread.sleep(1000*5);//1000 is 1 second
}
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
e1.printStackTrace();
}
}
Im looking for a way around this issue so that i can print the results of the checks being run in the ui .setText(result)
You are correct about the code putting the UI to sleep. Since sleep is called on the Event Dispatch Thread (the thread responsible for running the gui) the UI stops processing events and 'goes to sleep'.
I think what you want is a javax.swing.Timer.
Timer t = new Timer(1000 * 5, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do your reoccuring task
}
});
This will cause your reoccurring task to be performed off of the EDT, and thus it wont leave your ui unresponsive.
If the code you have posted runs on the EventDispatchThread, then there is no way Swing can redraw the GUI. You're blocking (sleeping in) the thread that's supposed to handle that!
This is because you are running you check in the main GUI thread and are using an infinite loop. This check is a background task and should be executed in it's own thread so that the GUI can still receive and react to input by the user.
You also do not need to write your own implementation, Java has a Timer object.
Edit: There is also a Swing specific Timer object. This will have the action occur in the GUI thread, so if your task is long, it can cause the GUI to still lock up while the action is occurring (but not while it is waiting).
In Java, I'm using an ActionListener for an array of JButtons. I would like for an earlier part of the ActionListener to set a new ImageIcon to a JButton, that change to be displayed immediately, then near the end of the ActionListener to set the JButton's ImageIcon back to null after a second long delay.
My problem is that none of the changes that happen to the JButton get displayed in the GUI window that it is set in until the ActionListener is completely finished, making the change in the JButton's ImageIcon unnoticeable. Is there any way to make an ActionListener commit a change to a JButton before it has finished executing the entire ActionListener, or should I be going about this differently?
The reason this is happening:
Swing repaints the buttons on the same thread (EDT) as the ActionListener is ran on. Hence if it is executing you ActionListener it cannot repaint since the thread is busy - as simple as that. You may have noticed that while your action listener is executing you also can't properly move your frames around etc. (GUI freezes up).
The solution:
Move heavy processing outside the EDT. It doesn't belong there anyway. As you could have guessed - use a background thread/thread pool for that. A good guide to it is Swing tutorial for concurrency
Notes:
As portrayed in the guide you do not want to modify components outside the EDT. As such the easiest strategy is to make a Runnable to execute on a background thread, start it, change the picture on the button and return without waiting for the task to finish.
public void actionPerformed(ActionEvent ae) {
Runnable task = new Runnable() {..};
executor.execute(task);
button.setIcon(newIcon);
return;
}
Note that this doesn't lock up the EDT for the task, hence allowing Swing to change the picture immediately.
This of course means that the user has no idea if the task has finished or not (And if there were any exceptions)! It is in the background after all! Hence there is an extra state of your execution: GUI is responsive and non-frozen, button is changed, but task is still running. In most applications this may be a problem (the user will spam the button or your background tasks may interleave). In that case you may want to use a SwingWorker to have a "processing" state as well.
public void actionPerformed(ActionEvent ae) {
new TaskWorker().execute();
button.setIcon(loadingIcon); //Shows loading. Maybe on button, maybe somewhere else.
return;
}
private class TaskWorker extends SwingWorker<Void, T> {
public Void doInBackground() {
//Do your task in the background here
}
protected void done() {
try {
get();
button.setIcon(doneIcon);
catch (<relevant exceptions>) {
button.setIcon(failedIcon);
}
}
}
Here done() is called on the EDT when doInBackground() is finished.
You could create a new Thread or a Thread from a Thread pool if you have one. And with that let the task work on a seperate Thread so that the ActionListener returns immediatly. And then in the other Thread you do your code and repaint the button. This is by the way a threory I'm not sure if it will work.
I have a JLabel which I want to change momentarily, here is the code I have written to do so:
infoLabel.setText("Added");
try {
TimeUnit.MILLISECONDS.sleep(300);
}
catch(InterruptedException ex)
{
}
infoLabel.setText("Action"); //funny part is when I comment this line it works
My default text for the label is 'Action'
Swing is a single threaded frame work, that means, if you do anything that stops this thread, then it can't respond to any new events, including paint requests.
Basically, TimeUnit.MILLISECONDS.sleep(300) is causing the Event Dispatching Thread to be put to sleep, preventing it from processing any new paint requests (amongst other things).
Instead, you should use a javax.swing.Timer
Take a look at
Concurrency in Swing
How to use Swing Timers
For more details
For example...
infoLabel.setText("Added");
Timer timer = new Timer(300, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
infoLabel.setText("Action");
}
});
timer.setRepeats(false);
timer.start();
Note, 300 milliseconds is a really short time, you might like to start with a value a little larger like 2000, which is 2 seconds ;)
You're sleeping the Swing event thread putting the entire GUI to sleep. Don't do that. Use a Swing Timer instead.
Your application is run on a single thread, so when you sleep the thread, you prevent it from making any GUI updates.
Are you sure you are doing things properly? By doing everything (including sleep) in the GUI thread, it will always be busy and never get back to Java in order to let the GUI be redrawn.
Search for EDT (Event dispatch thread) for more info. Here is one question on the subject: Processing code doesn't work (Threads, draw(), noLoop(), and loop())
To expound a little more, I have a GUI that looks like:
Then I have an action listener on the OK button that starts like:
//OK Button Action Listener
private void okButtonActionPerformed(ActionEvent e) {
//Enable/Disable Buttons
okButton.setEnabled(false);
cancelButton.setEnabled(true);
updateCheckbox.setEnabled(false);
//Move on to a series of other methods here...
Which should, in theory, make this happen:
However, instead, I get the following until ALL methods and other things connected to the OK button are completed:
This obviously can't happen, because the idea is to make the cancel button available and the OK button and several other tick-boxes unavailable for the duration of the program (Image 2), where, instead, it freezes in a half-state (Image 3). Is there any way to combat this?
Every time you execute logic from the GUI you should be using the SwingWorker in the following way:
SwingWorker myWorker= new SwingWorker<String, Void>() {
#Override
protected String doInBackground() throws Exception {
//Execute your logic
return null;
}
};
myWorker.execute();
If you want to update the GUI from inside this logic use InvokeLater:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//To update your GUI
}
});
With this you can be sure that both your logic and your GUI stay responsive.
Edit:
You could also use invokeAndWait if this suits your needs more. Link to related answer
//Move on to a series of other methods here...
Make sure you don't block the GUI thread (EDT) with long-running operations. Instead use SwingWorker.
Are you doing some processing on the same thread that's handling the GUI? You might want to look into a SwingWorker thread to do the heavy stuffin the background so your UI remains responsive if so.
Yup, that's because you have blocked with EDT with your other methods.
You need to use another Thread to do the work in the background otherwise the GUI will be blocked.
In Swing applications it is recommended that any long running tasks are carried out on a SwingWorker.
Take a look at the documentation for an introduction.
A SwingWorker will carry out a task that it is given and it can report back to the GUI when it is done. This will be done in a non-blocking way so that you can still use the GUI while the task it being carried out.
If you want to be able to cancel the background task you need to keep a reference to the SwingWorker so that you can call the cancel method. In this case the work method needs to be interruptable, otherwise the task cannot be cancelled.
I im creating a simple testing app that runs a check every hour on the selected directory/s using thread.sleep() through JFileChooser. But when i select the directory and the method runs the ui panel goes grey and the swing bits disappear. The thread seems to be putting the ui to sleep as well as the method its calling.
if (option == JFileChooser.APPROVE_OPTION) {
selectedDirectory = chooser.getSelectedFiles();
try {
while (true) {
runCheck(selectedDirectory);
Thread.sleep(1000*5);//1000 is 1 second
}
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
e1.printStackTrace();
}
}
Im looking for a way around this issue so that i can print the results of the checks being run in the ui .setText(result)
You are correct about the code putting the UI to sleep. Since sleep is called on the Event Dispatch Thread (the thread responsible for running the gui) the UI stops processing events and 'goes to sleep'.
I think what you want is a javax.swing.Timer.
Timer t = new Timer(1000 * 5, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do your reoccuring task
}
});
This will cause your reoccurring task to be performed off of the EDT, and thus it wont leave your ui unresponsive.
If the code you have posted runs on the EventDispatchThread, then there is no way Swing can redraw the GUI. You're blocking (sleeping in) the thread that's supposed to handle that!
This is because you are running you check in the main GUI thread and are using an infinite loop. This check is a background task and should be executed in it's own thread so that the GUI can still receive and react to input by the user.
You also do not need to write your own implementation, Java has a Timer object.
Edit: There is also a Swing specific Timer object. This will have the action occur in the GUI thread, so if your task is long, it can cause the GUI to still lock up while the action is occurring (but not while it is waiting).