I have a long task and I show "please wait" message during its execution.
I use SwingWorker for it. But sometimes long task is not long, so I want to show message with 1 second delay, but I don't know how to do it.
SwingWorker<Void, Void> mySwingWorker = new SwingWorker<Void, Void>(){
#Override
protected String doInBackground() throws InterruptedException
/** Execute some operation */
}
#Override
protected void done() {
dialog.dispose();
}
};
mySwingWorker.execute();
JProgressBar progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
JPanel panel = new JPanel(new BorderLayout());
panel.add(progressBar, BorderLayout.CENTER);
panel.add(new JLabel("Please wait......."), BorderLayout.PAGE_START);
dialog.add(panel);
dialog.pack();
dialog.setLocationRelativeTo(win);
dialog.setVisible(true);
}
Before you start the SwingWorker, start a Swing Timer with (at least) a one second delay and set not to repeat.
Pass this Timer to your SwingWorker so it has access to it. When the worker's done method is called, stop the Timer
If the Timer is triggered, you would display your wait message.
With a little bit of effort, you could wrap the whole thing up in a self contained class, using the SwingWorker's PropertyListener support to detect when the worker was started and completed
Why dont you use Thread.sleep(1000); between each display of "please wait"
A simple way to do it would be to set
long startTime = System.getNanoTime();
then before displaying the "please wait" you could check if it was > 1 second (1×10^9 nanos)
this may not be the most efficient way, but it will work
Related
I have a few shutdown steps which need to execute during a WindowClosing event before being disposed. Everything is executing correctly, but I'd like to add the capability to provide shutdown status messages in an existing JLabel within the closing JFrame. Is it possible to update the JLabel text during a WindowClosing event?
Sure. Just make sure component is not disposed before you start interacting with it.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// update label here
}
});
but I'd like to add the capability to provide shutdown status messages in an existing JLabel within the closing JFrame
label.setText(....);
label.paintImmediately(label.getBounds());
The code in the listener executes on the Event Dispatch Thread so the GUI can't repaint itself until all the listener code is executed and by that time the GUI will be closed.
The paintImmediately(...) will allow the component to bypass the RepaintManager and paint itself right away.
I used the following code to execute the shutdown steps in the background and then close the JFrame.
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
public Void doInBackground() {
// shutdown steps go here and can update the JLabel text
frame.dispose();
return null;
}
};
worker.execute();
}
});
So, I am trying to figure out how to add pauses in my card game to make the CPU act like they are taking turns. But, there's too much code involved, so I think if we demonstrate the strategy on this code, it could work.
How can I make this code pause for, say 5 seconds after the button has been pushed and THEN print the message.
public class TimerTest extends JFrame implements ActionListener{
private static final long serialVersionUID = 7416567620110237028L;
JTextArea area;
Timer timer;
int count; // Counts the number of sendings done by the timer
boolean running; // Indicates if the timer is started (true) or stopped (false)
public TimerTest() {
super("Test");
setBounds(30,30,500,500);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(null);
area = new JTextArea();
area.setBounds(0, 0, 500, 400);
add(area);
JButton button = new JButton("Click Me!");
button.addActionListener(this);
button.setBounds(200, 400, 100, 40);
add(button);
// Initialization of the timer. 1 second delay and this class as ActionListener
timer = new Timer(5000, this);
timer.setRepeats(true); // Send events until someone stops it
count = 0; // in the beginning, 0 events sended by timer
running = false;
System.out.println(timer.isRepeats());
setVisible(true); // Shows the frame
}
public void actionPerformed(ActionEvent e) {
if (! running) {
timer.start();
running = true;
}
// Writing the current time and increasing the cont times
area.append(Calendar.getInstance().getTime().toString()+"\n");
count++;
if (count == 10) {
timer.stop();
count = 0;
running = false;
}
}
public static void main(String[] args) {
// Executing the frame with its Timer
new TimerTest();
}
}
What I really would like to do is to be able to insert a pause into my method that runs the actions of each CPU. But it sounds like, with the Timer class, you need to perform your post-pause actions in the Timer's action listener. I am mostly confused about why the Timer needs an actionListener. I don't need the timer to repeat itself after it is done.
If it would be better for me to post my own code, let me know. But I am not sure what parts would be useful, since I don't want to have tons of code in this thread.
Conceptually, you want to trigger some event after 5 seconds, Swing Timer is perfect for this AND it's thread safe, making it safe to update the UI from, without the need for more hoop jumping.
The Timer sets up it's own thread to wait in, so it won't block the EDT, this means that you need to wait till the timer is triggered before you can something, it won't block and wait where you call it, this is the reason it has an ActionListener, so you know when it's triggered.
You can use your existing Timer, assuming it's not doing anything else, but for argument sake, I'm creating a new one...
private Timer waitItOut;
private JButton button;
Then in your constructor, you set up the timer, the difference here is I've made it non-repeating, meaning it won't trigger every 5 seconds, but you can re-run it when you want...
waitItOut = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
doImportantWork();
}
});
waitItOut.setRepeats(false);
// I made this an instance field for demonstration purposes
button = new JButton("Click Me!");
button.addActionListener(this);
button.setBounds(200, 400, 100, 40);
add(button);
Then in your actionPerformed method, you simply start the timer...
public void actionPerformed(ActionEvent e) {
button.setEnabled(false);
waitItOut.restart();
}
And wait till it calls your "important method which should be run after the specified delay"...
public void doImportantWork() {
button.setEnabled(false);
// Writing the current time and increasing the cont times
area.append(Calendar.getInstance().getTime().toString()+"\n");
count++;
if (count == 10) {
count = 0;
running = false;
}
}
You should launch a new thread which does the following:
Sleep for 5 seconds
Sets your required message
Calls repaint() to signal that the message needs to be redrawn.
On a action performed situation, you can initiate certain functions like:
TimeUnit.SECONDS.sleep(1);
to do this. I see in your code that you are trying to manually count for time. You can let the compiler or cpu do this for you.
I'm trying to create a Java GUI which displays the current time. Currently, I can make it display the current time, but only on startup. It then simply remains on that time forever. The reason is that I cannot figure out how to make it automatically load in the new current time every second. Here is my relevant code thus far:
// Getting the current time...
long epoch = System.currentTimeMillis() / 1000;
String date = new java.text.SimpleDateFormat("HH:mm:ss").format(new java.util.Date(epoch * 1000));
// Creating the panel...
JLabel lblThetime = new JLabel(date);
sl_panel.putConstraint(SpringLayout.NORTH, lblThetime, 55, SpringLayout.SOUTH, lblIBeA);
sl_panel.putConstraint(SpringLayout.WEST, lblThetime, 139, SpringLayout.WEST, panel);
lblThetime.setFont(new Font("Avenir Next", Font.PLAIN, 40));
// Adding the time to the panel
panel.add(lblThetime);
// My refresher which doesn't work
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
removeAll();
validate();
repaint();
}
}, 1000, 1000);
I attempted to make a refresher using information from this thread, but to no avail, the window (when run) is just blank. I then tried making a different refresher using information from this thread, and created this:
// My refresher which doesn't work
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
contentPane.remove(lblThetime);
contentPane.add(lblThetime);
}
}, 1000, 1000);
With contentPane having been defined as private JPanel contentPane;
Which also didn't work, however only the time itself is blank, the rest of the content in the window (One other JLabel (just some text)) remains as normal.
Without any refresher it behaves as described above, whereby it just displays the time when it started and remains on that time forever.
I'm using Eclipse with WindowBuilder. (And I'm (probably evidently) a complete noob to Java GUI stuff xD)
I discovered the solution!
I tried all the solutions given as answers here, and none which gave code fully worked, however all answers pointed me in the right direction. I found the solution to the problem on a website which I have forgotten the name of, but I used its suggestion and came up with this final solution which worked:
// New timer which works!
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
String date = new java.text.SimpleDateFormat("HH:mm:ss").format(new java.util.Date(System.currentTimeMillis()));
lblThetime.setText(date);
}
};
new Timer(delay, taskPerformer).start();
Thank you to all who answered as without said answers I probably would not have been able to find this solution :D
First of all you are using java.util.Timer instead of javax.swing.Timer. You need use this second class when working with Swing components to ensure GUI updates are made on the Event Dispatch Thread. Also take a look to Concurrency in Swing tutorial.
As suggested in other answers, there is no need to remove/add JLabel each time you want to update its text. Just call JLabel.setText() method.
If you still want remove/add the JLabel each time, then be aware of this:
From Container.add() javadoc:
This method changes layout-related information, and therefore,
invalidates the component hierarchy. If the container has already been
displayed, the hierarchy must be validated thereafter in order to
display the added component.
Then you'll need to call Component.revalidate() method. Like this:
contentPane.remove(lblThetime);
contentPane.add(lblThetime);
contentPane.revalidate();
Instead of removing and adding the label to the panel you should change the string of the label every second.
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
String date = new java.text.SimpleDateFormat("HH:mm:ss").format(new java.util.Date() );
lblThetime.setText(date);
}
}, 1000, 1000);
// Creating the panel...
JLabel lblThetime = new JLabel(date);
You can try to use Swing Timer for that task, for example:
private static JLabel l;
public static void main(String[] args) {
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
l.setText(new Date().toString());
}
});
timer.start();
JFrame f = new JFrame();
l=new JLabel(new Date().toString());
f.getContentPane().add(l);
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
That example update JLabel with new date every second
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
label.setText(new Date().toString());
}
}, 1000, 1000);
Isn't it simpler?
Clue 1: really search for Timer classes in Java. Did you pick the correct one?
Clue 2: update the label text instead.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Java GUI JProgressBar not painting
I have a GUI that has the GUI Locked while processing an Action Event, so I need a progress bar to show up. I can get the JDialog to show up but the progress bar won't show up. I used SwingUtilities.invokeLater() and invokeAndWait() but to no avail. The progress bar will not show up. Any hints or help would be appreciated.
pb = new JProgressBar(0, 100);
pb.setPreferredSize(new Dimension(175, 40));
pb.setString("Working");
pb.setStringPainted(true);
JLabel label = new JLabel("Progress: ");
JPanel center_panel = new JPanel();
center_panel.add(label);
center_panel.add(pb);
dialog = new JDialog((JFrame) null, "Working ...");
dialog.getContentPane().add(center_panel, BorderLayout.CENTER);
dialog.pack();
dialog.setLocationRelativeTo(this); // center on screen
dialog.toFront(); // raise above other java windows
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
dialog.setVisible(true);
pb.setIndeterminate(true);
}
});
Thread.sleep(5000);
template = AcronymWizardController
.sharedInstance().readAndDislpayDocx(contdFile);
parseDocxText(contdFile);
pb.setIndeterminate(false);
savedFile.setText(contdFile.toString());
dialog.dispose();
Swing is a single threaded API, that is, all the UI updates and modifications are performed from within a single thread (known as the Event Dispatching Thread or EDT). Anything that blocks this thread will stop it from processing additional updates, like repaints.
You have a number of choices. Your immediate requirement is to move the long running task off the EDT. To do this you can either use a SwingWorker or a Thread.
From your description, a SwingWorker will be easier.
For a simple example, check out JProgressBar won't update
For more information, you should check out Concurrency in Swing
You other choice would be to use something like a ProgressMonitor, example here
I'd like to show a progressbar and block interaction with my application frame while a thread is being executed.
In another thread someone suggested using JDialog instead of JFrame and setModal(true). However, when doing so the Dialog blocks the entire application.
This is essentially my code:
MyDialog dlg = new MyDialog();
dlg.setModal(true);
dlg.setVisible(true);
//do some stuff....
//(never executed when setModal(true)
dlg.setVisible(false);
The easiest way to do it would be using JXLayer and LockableUI.
Look here for an example of how this can be done.
Also note, that JXLayer made it into Java 7, and is available as javax.swing.JLayer.
The other thing is, that you should not execute long-running tasks insite Event Dispatch Thread. Read about SwingWorker and learn to write multithreaded code for Swing.
That is the point of a modal dialog, no interaction will happen outside the "box". The modal popup also halts the thread while waiting for user input. If you want to do other stuff while showing the dialog you will either have to do it in the dialog itself or start a new thread to take care of it.
Hope that helps!
With modal dialog try something like this:
final JDialog dlg = new JDialog();
dlg.setModal(true);
dlg.setSize(500, 500);
dlg.addWindowListener(new WindowAdapter() {
#Override
public void windowActivated(WindowEvent e) { //or other method
new Thread(new Runnable() {
public void run() {
try {
//execute your long running task
} //you should catch exception
finally {
dlg.setVisible(false);
dlg.dispose();
}
}
}).start();
}
});
dlg.setVisible(true);
I can also set GlassPane on your JFrame which will intercept any event from the user.