What is the proper way to terminate a Swing application from the code, and what are the pitfalls?
I'd tried to close my application automatically after a timer fires. But just calling dispose() on the JFrame didn't do the trick - the window vanished but the application did not terminate. However when closing the window with the close button, the application does terminate. What should I do?
Your JFrame default close action can be set to "DISPOSE_ON_CLOSE" instead of EXIT_ON_CLOSE (why people keep using EXIT_ON_CLOSE is beyond me).
If you have any undisposed windows or non-daemon threads, your application will not terminate. This should be considered a error (and solving it with System.exit is a very bad idea).
The most common culprits are java.util.Timer and a custom Thread you've created. Both should be set to daemon or must be explicitly killed.
If you want to check for all active frames, you can use Frame.getFrames(). If all Windows/Frames are disposed of, then use a debugger to check for any non-daemon threads that are still running.
I guess a EXIT_ON_CLOSE
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
before System.exit(0) is better since you can write a Window Listener to make some cleaning operations before actually leaving the app.
That window listener allows you to defined:
public void windowClosing(WindowEvent e) {
displayMessage("WindowListener method called: windowClosing.");
//A pause so user can see the message before
//the window actually closes.
ActionListener task = new ActionListener() {
boolean alreadyDisposed = false;
public void actionPerformed(ActionEvent e) {
if (frame.isDisplayable()) {
alreadyDisposed = true;
frame.dispose();
}
}
};
Timer timer = new Timer(500, task); //fire every half second
timer.setInitialDelay(2000); //first delay 2 seconds
timer.setRepeats(false);
timer.start();
}
public void windowClosed(WindowEvent e) {
//This will only be seen on standard output.
displayMessage("WindowListener method called: windowClosed.");
}
Try:
System.exit(0);
Crude, but effective.
May be the safe way is something like:
private JButton btnExit;
...
btnExit = new JButton("Quit");
btnExit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
Container frame = btnExit.getParent();
do
frame = frame.getParent();
while (!(frame instanceof JFrame));
((JFrame) frame).dispose();
}
});
The following program includes code that will terminate a program lacking extraneous threads without explicitly calling System.exit(). In order to apply this example to applications using threads/listeners/timers/etc, one need only insert cleanup code requesting (and, if applicable, awaiting) their termination before the WindowEvent is manually initiated within actionPerformed().
For those who wish to copy/paste code capable of running exactly as shown, a slightly-ugly but otherwise irrelevant main method is included at the end.
public class CloseExample extends JFrame implements ActionListener {
private JButton turnOffButton;
private void addStuff() {
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
turnOffButton = new JButton("Exit");
turnOffButton.addActionListener(this);
this.add(turnOffButton);
}
public void actionPerformed(ActionEvent quitEvent) {
/* Iterate through and close all timers, threads, etc here */
this.processWindowEvent(
new WindowEvent(
this, WindowEvent.WINDOW_CLOSING));
}
public CloseExample() {
super("Close Me!");
addStuff();
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
CloseExample cTW = new CloseExample();
cTW.setSize(200, 100);
cTW.setLocation(300,300);
cTW.setVisible(true);
}
});
}
}
If I understand you correctly you want to close the application even if the user did not click on the close button. You will need to register WindowEvents maybe with addWindowListener() or enableEvents() whichever suits your needs better.
You can then invoke the event with a call to processWindowEvent(). Here is a sample code that will create a JFrame, wait 5 seconds and close the JFrame without user interaction.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ClosingFrame extends JFrame implements WindowListener{
public ClosingFrame(){
super("A Frame");
setSize(400, 400);
//in case the user closes the window
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
//enables Window Events on this Component
this.addWindowListener(this);
//start a timer
Thread t = new Timer();
t.start();
}
public void windowOpened(WindowEvent e){}
public void windowClosing(WindowEvent e){}
//the event that we are interested in
public void windowClosed(WindowEvent e){
System.exit(0);
}
public void windowIconified(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowActivated(WindowEvent e){}
public void windowDeactivated(WindowEvent e){}
//a simple timer
class Timer extends Thread{
int time = 10;
public void run(){
while(time-- > 0){
System.out.println("Still Waiting:" + time);
try{
sleep(500);
}catch(InterruptedException e){}
}
System.out.println("About to close");
//close the frame
ClosingFrame.this.processWindowEvent(
new WindowEvent(
ClosingFrame.this, WindowEvent.WINDOW_CLOSED));
}
}
//instantiate the Frame
public static void main(String args[]){
new ClosingFrame();
}
}
As you can see, the processWindowEvent() method causes the WindowClosed event to be fired where you have an oportunity to do some clean up code if you require before closing the application.
Take a look at the Oracle Documentation.
Starting from JDK 1.4 an Application terminates if:
There are no displayable AWT or Swing components.
There are no native events in the native event queue.
There are no AWT events in java EventQueues.
Cornercases:
The document states that some packages create displayable components without releasing them.A program which calls Toolkit.getDefaultToolkit() won't terminate. is among others given as an example.
Also other Processes can keep AWT alive when they, for what ever reason, are sending events into the native event queue.
Also I noticed that on some Systems it takes a coupple of seconds before the Application actually terminates.
I think, the idea is here the WindowListener - you can add any code there that you'd like to run before the thing shuts down
In response to other comments, DISPOSE_ON_CLOSE does not seem to properly exit the application - it only destroys the window, but the application will continue running. If you want to terminate the application use EXIT_ON_CLOSE.
Related
I'm new into programming and I'm having some trouble with this.
The problem is, I'm using the Swing palette to create an assignment where I'm using a JDialog to display a timer at the same time of another frame, when I dispose this frame to change to another and return to the previous one the timer in the JDialog overlaps the first one that was running, and I couldn't managed to fix it.
Here's the code.
MAIN
public static void main(String[] args) {
Panel0 screen=new Panel0();
screen.setTitle("");
screen.setLocationRelativeTo(screen);
screen.setVisible(true);
}
1st FRAME
public class Panel0 extends javax.swing.JFrame {
Panel s=new Panel();
private void fisica1ActionPerformed(java.awt.event.ActionEvent evt) {
s.time();
s.setTitle("FISIC I");
s.setLocationRelativeTo(s);
s.setVisible(rootPaneCheckingEnabled);
s.dialog.setVisible(rootPaneCheckingEnabled);
dispose();
}
2nd FRAME
public class Panel extends javax.swing.JFrame {
private void EndActionPerformed(java.awt.event.ActionEvent evt) {
dialog.dispose();
dialog.setDefaultCloseOperation(0);
Panel0 pan=new Panel0();
pan.setLocationRelativeTo(p1);
pan.setVisible(rootPaneCheckingEnabled);
dispose();
}
void time(){
t=new Timer(1,new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startTime<0) {
startTime=System.currentTimeMillis();
}
long now = System.currentTimeMillis();
long clockTime = now - startTime;
if (clockTime >= duration) {
clockTime = duration;
t.stop();
}
SimpleDateFormat sdf=new SimpleDateFormat("mm:ss:SS");
clock.setText(sdf.format(duration-clockTime));
}
});
t.setInitialDelay(0);
if (!t.isRunning()) {
startTime = -1;
t.start();
}
}
I omitted the inizialization of the Timer and such, because I don't think that's the problem.
To clarify something: Once I close the 2nd frame the 1st opens and gives me options to repeat this process over and over, and everytime the JDialog named "dialog" overlaps with its data (you can see the numbers of the clock overlaping).
dispose() does not means that you will "destroy" the object or clear its state. It means that you will release graphics resources attached to that frame (low level window handle and stuff). It still can be reused with setVisible(true)
I assume that you want to reuse our popup - this is just fine, but I think that you are forgetting to stop the "disposed" timer thus every new timer you create on action will be exposed to so called "racing conditions".
Timers are simple background task and they must be stopped explicitly - it will not be done by itself.
Every call to s.time(); starts new timer without stopping previous one.
Simply speaking: you have multiple timers updating the same text field.
Solution: Stop previous timer before running new OR restart previous timer.
I want it to stay open and wait for the event to happen but as soon as it opens it closes how do i fix this?
static void hi()
{
System.out.println("g");
}
public static void main(String[] args)
{
}
#Override
public void KeyPressed(KeyEvent e) {
hi();
}
#Override
public void KeyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void KeyTyped(KeyEvent e) {
// TODO Auto-generated method stub
} <code>
Edit: wait even when i do have a frame when i press a key it dos not run hi()
You do not create any window to get an event. Your main function is empty, so your program does nothing: its process closes after it starts.
Look at Creating a GUI with Swing tutorial. You'll find the simplest Hello World GUI application in Compiling and Running Swing Programs section, see HelloWorldSwing.java.
It's not enough to create a frame: you have to register KeyListener on frame or another component. Here's the complete example:
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class PressKey implements KeyListener, Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new PressKey());
}
#Override
public void run() {
JFrame frame = new JFrame("Press a key");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addKeyListener(this);
frame.setSize(300, 150);
frame.setVisible(true);
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
System.out.println("Key pressed: " + e.getKeyChar());
}
}
How does it work? The function main creates an instance of PressKey class and passes it to SwingUtilities.invokeLater utility method. This is required because Swing is not thread-safe and all modifications to GUI components must be performed on the Event Dispatch Thread (EDT). So invokeLater schedules a job onto EDT; as the result PressKey.run() will be run on EDT: it's where we create the frame and register KeyListener attached to the frame, then we show the frame. When the frame is shown on the screen, press any key: you will see the corresponding character printed in the console. If you press a functional key, or another key that does not generate an input character, you won't see anything printed because in this case KeyEvent.KEY_TYPED is not generated.
For more info, see How to Write a Key Listener.
I am assuming you have copied this code out of a tutorial? There are a couple of things missing. As Alexey mentioned, the 'main' method is the entry point for your program, and if you have no code in your main method then your program does nothing. You should be doing something like creating a new thread, frame, window, etc whatever it is that you are wanting your key listeners to attach to.
In the broader context of creating a thread that runs forever, you could do something like
while(true) {
Thread.sleep(100);
}
although you would probably want to change 'true' to 'myflag' which can be set from elsewhere in your code to terminate the thread.
Hello guys I am doing a thread to update a ball over JFrame so I repaint the screen... and then paint the ball update its position .. and then draw the screen again ... draw the ball and the same cycle ... here is the code
private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {
Thread t = new Thread()
{
public void run()
{
while(true)
{
repaint();
b2.update(ob,2);
b2.paint(ob.getGraphics());
b2.setT(b2.getT() + 1);
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
System.out.println("Error in Sleeping");
}
}
}
};
t.start();
}
but the problem is that I don't see the ball... the paint of the screen always overrides the ball and the ball is like down under the Jframe ..
If you want to have animations in Swing, the recommended class to use is the javax.swing.Timer . This class allows you to perform operations on the Event Dispatch Thread at regular intervals.
The Swing Timer tutorial
An animation example posted here on SO (which is linked in the Swing wiki here on SO btw)
Some General Rules
Swing is not thread safe, you should only ever update UI components from within the context of the Event Dispatching Thread.
You do not control the paint process, the repaint manager does. You can request updates to occur by calling repaint, but you should never call update and paint directly when trying to update the display.
The Graphics context used by the paint sub system is a shared resource and is not guaranteed to be the same between paint cycles, you should never maintain a reference to it. You should also not rely on the results from JComponent#getGraphics this method is capable of returning null.
An Example Solution
You have a number of options, depending on what you want to ultimately achieve.
You could use a SwingWorker, but given the fact that all your going to is enter an infinite loop and it would easier to use SwingUtilities#invokeLater then actually use the publish method, this approach would actually be more work.
You could also use a Thread, but you'd end up with the same problems as using a SwingWorker
The simpliset solution, for what you're presented, is actually a javax.swing.Timer
public class Blinky {
public static void main(String[] args) {
new Blinky();
}
public Blinky() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BlinkyPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected class BlinkyPane extends JPanel {
private JLabel blinkyLabel;
private boolean blink = false;
public BlinkyPane() {
setLayout(new GridBagLayout());
blinkyLabel = new JLabel("I'm blinking here");
blinkyLabel.setBackground(Color.RED);
add(blinkyLabel);
Timer timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
blink = !blink;
if (blink) {
blinkyLabel.setForeground(Color.YELLOW);
} else {
blinkyLabel.setForeground(Color.BLACK);
}
blinkyLabel.setOpaque(blink);
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 100);
}
}
}
You can take a look at Swing Timer and Concurrency in Swing for more info
If you access GUI components outside the EDT (Event Dispatch Thread) then you might encounter strange problems, Contrary if you perform long running tasks in the EDT then also you will get problems.
Check this post for more info on GUI Threading in Java
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
It's been a long time since I had to deal with concurrent programming in Java (it was in concurrent programming college classes, actually) and I'm having what to seem some pretty basic problem. The sample code below might seem kinda weird since I'm not using standard JDK for UI controls, but it goes like this:
//class Screen
public class Screen{
private Frame rootContainer;
public Screen(Frame rootContainer){
this.rootContainer = rootContainer;
this.createGui();
}
private void createGui(){
Button btn = new Button("Call");
btn.setBounds(20, 20, 100, 20);
rootContainer.add(btn);
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ConcurrentDialog cr = createDialog();
cr.start();
//Suposedly only to be called after the Exit button in the dialog is clicked
((Button)e.getSource()).setLabel("Called");
((Button)e.getSource()).repaint();
}
});
}
private ConcurrentDialog createDialog(){
return new ConcurrentDialog(rootContainer, this);
}
}
//Class ConcurrentDialog
public class ConcurrentDialog extends Thread {
private Frame rootContainer;
private Screen screen;
public ConcurrentDialog(Frame rootContainer, Screen screen){
this.rootContainer = rootContainer;
this.screen = screen;
}
public void run(){
createDialog();
synchronized(screen){
try {
screen.wait();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
private void createDialog(){
Dialog dialog = new Dialog(rootContainer, true);
dialog.setBounds(20, 20, 110, 35);
Button btn = new Button("Exit");
btn.setBounds(5, 5, 100, 20);
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Button source = (Button)e.getSource();
Dialog dialog = (Dialog)source.getParent();
synchronized(screen){
screen.notify();
}
dialog.dispose();
dialog.getOwner().remove(dialog);
dialog = null;
}
});
dialog.add(btn);
dialog.show();
}
}
Before anyone asks, yes, I'm trying to implement a modal dialog (in fact, I should rename ConcurrentDialog to ModalDialog). As I said before, I'm not using swing (just because I CAN'T... Embeded VM's are usually platform-specific when it comes to UI and that's my case) and this particular library doesn't have a native Modal Dialog (meaning no JOptionPane.showMessageDialog love for me) so I'm building one from scratch.
Anyway, here's the problem: It seems that the wait() method is executed much later than ((Button)e.getSource()).setLabel("Called"). A workaround that I found is setting btn as a global attribute with public access and refactor the run() method to this:
public void run(){
createDialog();
synchronized(screen){
try {
screen.wait();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
screen.getBtn().setLabel("Called");
screen.getBtn().repaint();
}
It works this way, but in my "real world" scenario, this implementation would cause quite a mess when it comes to coupling. Any pointers?
your cr.start() returns immediately.
What you want is to put the wait(screen) instead of after cr.start(), and remove wait(screen) from run() method.
This way the thread will show a dialog and exit. Once the dialog is closed, screen will be notified, and your createGui().actionPerformed() will wake up.
you can't use multiple threads with swing/awt, and you can't "wait" on the thread which is running the gui, or it will stop working. the way to do asynchronous work which involves the gui is to spawn a separate thread (as you are doing), and then have that thread use SwingUtilities.invokeLater() whenever it needs to "update" the gui.