In my java swing application am having a Jframe and Jlabel for displaying current time.
here am using a thread for displaying time in jlablel which is added to the frame.my doubt is that when i dispose the jframe what will happen to the thread whether its running or stopped.
If you have NOT marked your thread as daemon by calling yourThread.setDaemon(true), it will keep running even if main thread in your application has finished. Remember you have to call setDaemon before starting the thread.
Refer my answer to some previous question for details.
The correct way in your case, I believe, would be you maintain a 'stop' flag which is watched by your timer thread. Timer thread should exit on reading this flag as 'false'. You can add a WindowListener to your jframe and on the window closed event set the 'stop' flag to true
Heres example code for what I am suggesting :
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
public class JFrameTest {
public static void main(String[] args) {
final Timer t = new Timer();
t.start();
JFrame jf = new JFrame("GOPI");
jf.setVisible(true);
jf.setSize(100, 100);
jf.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
t.stopTimer();
}
});
System.out.println("JFrameTest.main() DONE");
}
}
class Timer extends Thread {
boolean stop = false;
#Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (stop)
break;
System.out.println("Counting :" + i);
}
System.out.println("Timer exit");
}
public void stopTimer() {
stop = true;
}
}
Your thread will keep running.
You need to either do as suggested by Gopi or you could use System.exit(0) in close operation of your JFrame.
NOTE: I am assuming here that Your application needs to end if this Frame is closed.
Related
I understand that Swing GUIs themselves use Threads, but I am trying to use a separate thread to run my simulation. I created a class that implements Runnable and uses a custom Thread as most simple Runnable examples do. My run() method basically runs my simulation, updating every second (which works great), but I'm now trying to implement buttons that can pause/resume the simulation. My Start button successfully starts the thread, and my Pause button successfully pauses the thread. However, when Pause is selected, the entire GUI is paused, and you can see the button as still being selected and I am unable to select any buttons or interact with the GUI at all. As soon as I call wait() on my custom thread, my entire GUI halts, even though I'm using a Thread separate from this GUI. Why does calling wait() freeze up my GUI? How can I pause just this specific Thread and not the entire GUI?
Please note that the Start button should be what makes the program resume. Here's my code for the GUI:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GridFrame extends JFrame {
private static final long serialVersionUID = 2857470112009359285L;
private MyGridPanel grid;
private Simulation sim;
GridFrame(Simulation sim, int w, int h, int rows, int cols) {
this.sim = sim;
setTitle("Simulation");
setSize(w, h);
grid = new MyGridPanel(w, h, rows, cols);
add(grid, BorderLayout.CENTER);
//Build bottom panel
JPanel buttons = new JPanel();
buttons.setLayout(new GridLayout(1,3));
JButton start = new JButton("Start");
JButton pause = new JButton("Pause");
JButton reset = new JButton("Reset");
start.setActionCommand("Start");
start.addActionListener(new ButtonActionListener());
pause.setActionCommand("Pause");
pause.addActionListener(new ButtonActionListener());
reset.setActionCommand("Reset");
reset.addActionListener(new ButtonActionListener());
buttons.add(start);
buttons.add(pause);
buttons.add(reset);
add(buttons, BorderLayout.SOUTH);
}
public MyGridPanel getGrid(){
return grid;
}
private class ButtonActionListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
switch(e.getActionCommand()){
case "Start":
System.out.println("Start");
sim.start();
break;
case "Pause":
System.out.println("Pause");
sim.pause();
break;
case "Reset":
System.out.println("Reset");
break;
}
}
}
}
And here is my Runnable:
public class Simulation implements Runnable{
private Thread t;
private GridFrame frame;
private boolean paused;
public Simulation(){
frame = new GridFrame(this, 300, 300, 10, 10);
frame.setVisible(true);
paused = true;
}
public void start () {
if(t == null){
//Thread has not been created. Simulation has not started to run yet
System.out.println("Starting thread.");
t = new Thread(this);
t.start();
paused = false;
}
else if(paused){
//Simulation and thread already started to run but was paused. This should use notify() to resume the thread.
resume();
paused = false;
}
}
public void resume(){
synchronized(t){
t.notify();
}
}
public void pause(){
synchronized(t){
try {
t.wait();
paused = true;
} catch (InterruptedException e) {
System.out.println("Exception when trying to pause simulation");
e.printStackTrace();
}
}
}
#Override
public void run() {
while(true){
frame.getGrid().step();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Thread interrupted while simulation was running.");
e.printStackTrace();
}
}
}
public static void main(String[] a) {
Simulation s = new Simulation();
}
}
Calling wait and notify on a Thread object behaves no differently than it does on any other object. Specifically, as you've noticed, it does not send a signal to the executing thread that it should pause, but rather it will block the calling thread (your UI thread in this case) until it receives a notify message.
You will need to implement a messaging system (such as a blocking queue) from your UI thread to your background thread in order to get what you want.
I have test method, which start GUI window and next start a endless loop. I want to finish test method when GUI is closing. Any ideas how can I reach it? I try to set a boolean variable and when quit button is pressed I change it to false so loop should be finish but when I look into logs test status is started.
boolean testRunning = true;
JButton buttonQuit;
#Test
public void start() {
MainFrame.getInstance().setVisible(true);
if (showHelpDialog) {
HelpDialog.getInstance().setVisible(true);
}
while(testRunning) {
}
}
And when I pressed quit button testRunning variable is set to false.
I think your problem is, that your blocking the Thread with your UI by executing your loop.
I made an little example with an JFrame. This frame has a JButton as big as the frame. In an Thread is and Loop working until the Button is pressed:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Test {
//to setThe state of the loop
public static boolean continueLoop = true;
public static void main(String[] args) {
//Create a Frame
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
Dimension d = new Dimension(400, 400);
frame.setSize(d);
//Add a button to close the programm or end the loop
JButton b = new JButton("Close");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
continueLoop = false;
//Enable this if you want to close the programm
//System.exit(0);
}
});
// Start a Thread with your endless loop in it
Thread t = new Thread(new Runnable() {
#Override
public void run() {
int i = 1;
while(continueLoop)
{
try {
Thread.sleep(500);
System.out.println("Try: " + i);
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
// Add a button and set de Frame visible
frame.add(b);
frame.setVisible(true);
}
}
Hope that helps!
PS: this is the fastest example i could think of. Note that there are better ways to add a state-controled loop to your UI. For example I used static variables for my example - you should not do that in your application - except it really necessary.
I am trying to control a while loop in my program to stop and start based on a user input. I have attempted this with a button and the "start" part of it works but then the code goes into an infinite loop which I cant stop without manually terminating it. The following is all my code:
Header Class
package test;
import javax.swing.JFrame;
public class headerClass {
public static void main (String[] args){
frameClass frame = new frameClass();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(150,75);
frame.setVisible(true);
}
}
Frame Class
package test;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class frameClass extends JFrame {
private JButton click;
public frameClass(){
setLayout(new FlowLayout());
click = new JButton("Stop Loop");
add(click);
thehandler handler = new thehandler();
click.addActionListener(handler);
}
private class thehandler implements ActionListener{
public void actionPerformed(ActionEvent e){
if(e.getSource()==click){
looper loop = new looper();
looper.buttonSet = !looper.buttonSet;
}
}
}
}
Looping class
package test;
public class looper {
public static boolean buttonSet;
public looper(){
while (buttonSet==false){
System.out.println("aaa");
}
}
}
How do I fix this and stop if from going into the infinite loop please?
Swing is a single threaded framework, this means that while the loop is running, the Event Dispatching Thread is been blocked and can't process new events, including repaint requests...
You need to start your Looper class inside it's own thread context. This would also mean that your loop flag would need to be declared volatile or you should use an AtomicBoolean so that state can be inspected and modified across thread boundaries
For example...
public class Looper implements Runnable {
private AtomicBoolean keepRunning;
public Looper() {
keepRunning = new AtomicBoolean(true);
}
public void stop() {
keepRunning.set(false);
}
#Override
public void run() {
while (keepRunning.get()) {
System.out.println("aaa");
}
}
}
Then you might be able to use something like...
private class thehandler implements ActionListener {
private Looper looper;
public void actionPerformed(ActionEvent e) {
if (e.getSource() == click) {
if (looper == null) {
looper = new Looper();
Thread t = new Thread(looper);
t.start();
} else {
looper.stop();
looper = null;
}
}
}
}
to run it...
Take a look at Concurrency in Swing and Concurrency in Java for some more details
Also beware, Swing is not thread safe, you should never create or modify the UI from out side the context of the EDT
The problem is you start an infinite loop and try to terminate it in the same thread. That doesn't work because the VM executes one task in a thread after another. It would execute the command to stop the looper directly after the loop is finished but an infinite loop never finishes. So it cannot be stopped like this.
You need to create a second Thread for the looper. This way you can stop it from your main thread.
I'm trying to:
display a text in a jLabel,
wait for two seconds,
then write a new text in the jLabel
this should be simple, but I get a strange bug:
the first text is never written, the application just waits for 2 seconds and then displays the final text. here is the example code:
private void testButtonActionPerformed(java.awt.event.ActionEvent evt) {
displayLabel.setText("Clicked!");
// first method with System timer
/*
long t0= System.currentTimeMillis();
long t1= System.currentTimeMillis();
do{
t1 = System.currentTimeMillis();
}
while ((t1 - t0) < (2000));
*/
// second method with thread.sleep()
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {}
displayLabel.setText("STOP");
}
with this code, the text "Clicked!" is never displayed. I just get a 2 seconds - pause and then the "STOP" text.
I tried to use System timer with a loop, or Thread.sleep(), but both methods give the same result.
Just to provide more background on Andrew Thompson's comment: the EDT is responsible for handling gui updates. If you block it using Thread.sleep(...) those updates are blocked as well. That's why you don't see the first text - the EDT just can't do the update on the label.
Here's a runnable example which does what you're after. As Andrew Thompson's comment stated, a SwingWorker is a good way to approach this problem.
The basic principal is to never block the Event Dispatch Thread. That's the thread responsible for repainting the GUI and responding to user interaction, so if you do something computationally expensive on the EDT, your GUI will stop responding.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingWorker;
public class ButtonTest {
public static void main(String[] args) {
// create a frame and a button
JFrame frame = new JFrame();
final JButton button = new JButton("Button");
frame.add(button);
// add an action listener to the button
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// change the button text right away
button.setText( "Clicked" );
// create a SwingWorker which simply waits 2000 milliseconds
// simulating a computation being performed
SwingWorker<String, Object> worker = new SwingWorker<String, Object>() {
#Override
public String doInBackground() {
// it's safe to call Thread.sleep( ) here
// doInBackground is executed on a separate worker
// thread
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
return "Done";
}
#Override
protected void done() {
// done() is executed back on the Swing thread
// so it's safe to updated the state of the button
try {
button.setText(get());
} catch (Exception e) { }
}
};
// run the worker
worker.execute();
}
});
frame.setSize( 300, 300 );
frame.setVisible( true );
}
}
You are messing with the event dispatcher thread.
That will cause un-expected UI behavior as you are seeing. If you plan to do these type of animations, make sure to read up on what #Andrew Thompson suggested and also, see if you can read this - Filthy rich clients
Better to use a Swing Timer as shown in curde-example below:(yes, it is crude, I did not worry about stopping the timer etc):
public class DelayTest extends JPanel{
JLabel messageLabel = new JLabel();
JButton actionButton = new JButton("Click Me");
String[] messages = {"Clicked", "Stop!"};
int i=0;
public DelayTest(){
super();
add(messageLabel);
add(actionButton);
actionButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
if(i<=1)
messageLabel.setText(messages[i++]);
}
});
timer.start();
}
});
}
}
Edit
Why not stop the Timer:
#Override
public void actionPerformed(ActionEvent evt) {
if (i <= 1) {
messageLabel.setText(messages[i++]);
} else {
((Timer)evt.getSource()).stop();
}
}
});
This post relates to my last one regarding a timer. I decided the easiest thing to do for immediate results was to just write a Counter thread that counts down from a certain time (in this case 5 seconds) and if the counter reaches 0, the JFrame closes and let's the user know that time has expired.
I'm running into some trouble, however. I cannot seem to make the JFrame close when the counter reaches 0. I'm not sure if I'm missing something stupid or if I am misunderstanding the way threads work and the way JFrames work. Here is the code, let me know what you think.
On a side note, I understand it would probably be most efficient to use a swing.Timer, but I just don't quite grasp the nature of them yet. I'm under self-imposed time constraints (I'm not a student or anything, I just like to stay motivated) and so I'm "jerry-rigging" this thing for now.
Anyway, on to the code!
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
public class RacerDoom extends JFrame {
boolean timesUp=false;
public RacerDoom() {
//create JFrame
super("Racer Doom Squared");
setSize(WIDTH,HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
if(timesUp==true) {
dispose();
JOptionPane.showConfirmDialog(null, "Time's Up! Click Okay to try again!");
}
Counter c1 = new Counter();
c1.start();
//Counter
private class Counter extends Thread {
public Counter() {}
public void run() {
for(int i=5;i>=0;i--) {
if(i==0) {
timesUp=true;
}
System.out.println(i);
try{
Thread.sleep(1000);
}
catch(InterruptedException e){}
}
}
}
...
EDIT: I have the timer implemented and working. It does exactly what I need it to, but I can't get the timer.stop(); command to work. I get the error "The local variable timer may not have been initialized.
Like I said, the timer works, it just never stops working until the program is terminated. Here is the constructor code for the JFrame, where the timer is located.
int counter = 0;
public RacerDoom() {
//create JFrame
super("Racer Doom Squared");
setSize(WIDTH,HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
final Timer timer=new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(counter>=10) {
timer.stop(); //the error occurs here
dispose();
JOptionPane.showConfirmDialog(null, "Time's Up!");
}
else{
counter++;
}
System.out.println(counter);
}
});
//inner thread
Move1 m1 = new Move1();
m1.start();
timer.start();
}
Thats easy to do with the help of a swing timer.. See this code sample:
final java.swing.Timer timer=new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(counter>5) {
timer.stop();
<dispose the fram here>
}else{
counter++;
}
}
});
timer.start();
I put this code in the constructor of my JFrame which will run in the Event despatch thread. If you dont want hang up your GUI, make sure that you run this timer on another thread and when you are disposing the JFrame wrap the call with SwingUtilities.invokeLater() - This ensures that the call gets queued on the event despatch thread.
I think your code is not working for the same reason, that you trying to something that does not get queued up in the event despatch thread. Here's an article that will get you going
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html