Updating Swing GUI with while loop and delay - java

I have a while loop that is supposed to update the program data and the GUI, but the GUI part freezes until the while loop is done executing.
Here is my code:
while(game.getCompTotal() < 17) {
try {
Thread.sleep(2500);
} catch (Exception ex){
ex.printStackTrace();
}
game.compHit(); // Program data update
updateCardPanels(); // GUI update -- Does not update until the while loop finishes.
if(game.checkCompBust()){
if(JOptionPane.showConfirmDialog(this, "Dealer Busted. Play again?") == JOptionPane.YES_OPTION){
init(); // Reset
}
}
}
I have tried using SwingUtilities.invokeLater() and a Swing Timer but I do not really get how SwingUtilities.invokeLater() works or how to use a Swing Timer on the EDT. My question is how can I have the GUI update at the same time as the program data in the while loop?
Here is my attempted invokeLater() code:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Timer t = new Timer(100, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
updateCardPanels();
}
});
t.setInitialDelay(0);
t.start();
}
});

Looks like your while loop is on EDT and thats why it freezes GUI while it is executing. Never call Thread.sleep on EDT.
All SwingUtilities.invokeLater does is puts the Runnable on a queue so that EDT would run it at some point in time. The SwingUtilities.invokeLater should be run from a non EDT thread. Starting Swing Timer with SwingUtilities.invokeLater is not required. You can start it directly from EDT.
Swing timer uses a separate thread that runs and periodically calls specified ActionListener on EDT thread.
You can replace the while loop with swing timer (lets say with 1sec interval) and check game.getCompTotal() < 17 condition inside its actionPerformed call.

Related

While loop to repeat actionPerformed of a JButton

I've got a JFrame with some JButtons. The "update" button (named JButton1) executes a number of queries which fill a form.
Now I'd like another JButton to be the "automatic update" (named JButton2), so I would like it to loop inside the JButton1ActionPerformed method, until JButton2 is no longer selected.
When I press JButton2 the window freezes.
Is my code right? Do you think I would need a stop button?
private void jToggleButton2ActionPerformed(java.awt.event.ActionEvent evt) {
long start = System.currentTimeMillis();
if(jToggleButton2.isSelected()){
start = System.currentTimeMillis();
do{
do{
jButton1ActionPerformed(evt);
}
while(System.currentTimeMillis()-start>3000);
}
while(jToggleButton2.isSelected());
}
if(jToggleButton2.isSelected()){
jToggleButton2.setSelected(false);
}
}
Swing is:
Single threaded; this means that you should not perform any long running or blocking operations within the context of the Event Dispatching Thread
Not thread safe; this means you should not update the UI or anything the UI might depend on outside of the EDT. It's also risky to try and address UI elements outside the EDT, as the thread model might return false values
Without more context to your problem, I might suggest using a Swing Timer. Two main reasons:
The ActionListener is called within the context of the EDT, making it safe to update the UI from within in
You can specify a delay between updates, which reduces the risk of overloading the EDT which will cause performance issues
For example...
private void jToggleButton2ActionPerformed(java.awt.event.ActionEvent evt) {
long start = System.currentTimeMillis();
if (jToggleButton2.isSelected()) {
// You could use an instance field instead and determine
// if the Timer is already running or not
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!jToggleButton2.isSelected()) {
((Timer) e.getSource()).stop();
return;
}
jButton1ActionPerformed(evt);
}
});
timer.start();
// Just want to point out that this doesn't make sense
// As if the button was selected, you'd still be in
// your previous loop
//if (jToggleButton2.isSelected()) {
// jToggleButton2.setSelected(false);
//}
}
}
Another solution might be to use a SwingWorker, which provides you with a means to perform long running or blocking operations off the EDT, but which also provides functionality to update the UI safely from within the EDT.
Take a look at:
Concurrency in Swing
Worker Threads and SwingWorker
How to Use Swing Timers
for more details
Lets put aside that you should not crete Thread like I will show you (but use SwingWorker for example) you need to do something like this:
private void jToggleButton2ActionPerformed(java.awt.event.ActionEvent evt) {
if(jToggleButton2.isSelected()){
new Thread(new Runnable(){
public void run(){
long start = null;
do{
start = System.currentTimeMillis();
jButton1ActionPerformed(evt);
}while(jToggleButton2.isSelected() &&jSystem.currentTimeMillis()-start>3000)
}
}).start();
}
Im not focusing on if your code is valid or not, its just shows how to run something in background, so you will not freeze your GUI.

I am a beginner and I am trying to make a card flip game in netbeans

On clicking an image I want another image to be displayed pause for one second and the original image to be restored and displayed againdisplayed again When I try to do it it action handler such as code given below it sets the image after sleep .I want the image to be displayed pause and the original to be displayed again how can I achieve that
private void jButton16ActionPerformed(java.awt.event.ActionEvent evt) {
jButton16.setIcon(new javax.swing.ImageIcon("C:\\Users\\x\\Documents\\O.png"));
try { //sleep 1 seconds
Thread.sleep(1000);
}
catch (InterruptedException e)
{
}
jButton16.setIcon(new javax.swing.ImageIcon("C:\\Users\\x\\Documents\\118px-AMIGO.jpg")); }
Swing is single threaded, you should never perform long running or blocking operations within the context of the Event Dispatching Thread
Swing is NOT thread safe, this means you should never update the UI (and anything the UI needs) outside of the context of the EDT
The simplest solution to your problem is to use a Swing Timer
private void jButton16ActionPerformed(java.awt.event.ActionEvent evt) {
jButton16.setIcon(new javax.swing.ImageIcon("C:\\Users\\x\\Documents\\O.png"));
Timer timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
jButton16.setIcon(new javax.swing.ImageIcon("C:\\Users\\x\\Documents\\118px-AMIGO.jpg"));
}
});
timer.setRepeats(false);
timer.start();
}
See Concurrency in Swing and How to use Swing Timers for more details
You should never invoke Thread.sleep() inside Event Dispatch Thread
because Thread.sleep() will block Event Dispatch Thread and your UI will be Freeze , instead you could use a Timer to achieve your goal
Refer below code,
jButton16.setIcon(new javax.swing.ImageIcon("C:\\Users\\x\\Documents\\O.png"));
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
jButton16.setIcon(new javax.swing.ImageIcon("C:\\Users\\x\\Documents\\118px-AMIGO.jpg"));
}
});
timer.setRepeats(false);
timer.start();

Delay routine in Swing on button click which should not stall the application

I am trying to do the following: click a button, button disappears for 2 seconds, text appears for 2 seconds and after those 2 seconds the visibility is reversed. So far I have done this:
btnScan.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
txtScanning.setVisible(true);
btnScan.setVisible(false);
try {
Thread.sleep(2000); //1000 milliseconds is one second.
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
btnScan.setVisible(true);
}
});
and the result is that as soon as I click the btnScan, the whole program freezes for 2 seconds before doing anything. How do I add the delay at the correct order?
You should not call sleep method in your code that dispatches the event. All the work related UI is handled by EDT(Event Dispatch Thread) and a sleep method will cause it to freeze and hence your Swing application will freeze.
To overcome it you should use a Timer. Run the timer and execute the UI manipulation using SwingUtilities.invokeLater so that it is handled by EDT.
import java.util.Timer;
// make it a member variable
Timer timer = new Timer();
........
public void actionPerformed(java.awt.event.ActionEvent evt) {
button.setVisible(false);
timer.schedule(new TimerTask() {
#Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
button.setVisible(true);
}
});
}
}, 2000);
}
Currently in your code, you are causing the EDT (event dispatcher thread) to pause with the invocation of Thread.sleep
Performing any long running tasks in the EDT will cause your UI to freeze.
To achieve what you desire, use a SwingWorker thread to perform your actions
This might help: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
Swing is a single threaded environment, anything that blocks this thread will prevent it from processing new events, including repaint requests.
Swing is also not thread safe, meaning img you should never create or update the UI from outside the context of the EDT.
In this case you can use a Swing Timer to trigger a callback to occur at some time in the future which (the notification) will be executed within the context of the EDT, making it safe to update the UI with
Take a look at Concurrency in Swing and How to us Swing Timers for more details
Making use of Swing timer, you can do something like this:
btnScan.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
txtScanning.setVisible(true);
btnScan.setVisible(false);
Timer timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent acv) {
btnScan.setVisible(true);
txtScanning.setVisible(false);
}
});
// setRepeats(false) to make the timer stop after sending the first event
timer.setRepeats(false);
timer.start();
}
});

Updating buttons inside or outside of SwingWorker?

I want to disable a number of buttons/menu items of my GUI while a SwingWorker thread is running. The thread is started when a button is clicked.
It looks like the while loop in my code causes the CPU load to go up significantly. Did I get something wrong about how to determine if a SwingWorker thread is still running?
The CPU load's definitely lower when I update the buttons/menu items inside the SwingWorker thread. However, I felt like that shouldn't be the SwingWorker thread's job. Should it?
JButton button = new JButton("Start");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
menu.setEnabled(false);
MySwingWorker worker = new MySwingWorker();
worker.execute();
while (true) {
if (worker.isCancelled() || worker.isDone()) {
menu.setEnabled(true);
break;
}
}
}
});
t.start();
}
});
Swing GUI objects should be constructed and manipulated only on the event dispatch thread (EDT). Doing so from t is incorrect. As suggested in examples cited here, condition your GIU elements on the EDT before starting the worker, and update the GUI in process() or done().

strange behaviour of javax.swing.Timer

in main if the following code is used
Timer timer = new Timer(200, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("boo");
}
});
timer.start();
Thread.sleep(3000);
boo will be printed every 200 milliseconds as expected.
While
Timer timer = new Timer(200, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("boo");
}
});
timer.start();
nothing will be output!
Presumably the code you're showing is in main(). When main() returns, the program exits before the timer thread has a chance to get going. The sleep gives the JVM enough time to create the other thread, which then allows the JVM to keep running.
Could it be that the Thread.sleep is on the main thread and that the reason nothing is printed in the second case is that the main thread goes away and the program exits?
Is that the complete program (is that the only code in your main method)? If yes, then in the second case the program ends before the timer goes off, so it won't print anything, because the program is finished almost immediately.
1) this code wokrs and in all cases is correct
Timer timer = new Timer(200, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("boo");
}
});
timer.start();
you have to check if javax.swing.Timer#setRepeats() have true value (default value), if isn't changed, otherwise you have a code that block Event Dispatch Thread, thenafter Swing's Timer too,
2) don't use Thread.sleep(int) during EDT, and untill Sleep ended caused this code block for EDT,

Categories