Update label twice in one timer operation - java

I have a class Dmi,Swing components frame and a label.
I have Swing timer. In timer implementation I have two statements for setting text in the label, with a time interval, but I can only see the last setText operation.
import javax.swing.*;
import java.util.Timer.*;
import java.awt.event.*;
class Dmi implements ActionListener
{
Timer tim;
JFrame frame;
JLabel lbl;
Dmi()
{
frame=new JFrame("abc");
lbl=new JLabel("count");
frame.add(lbl);
frame.setVisible(true);
frame.pack();
tim=new Timer(1000,this);
tim.start();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent ae)
{
Thread th1=new Thread();
lbl.setText("abc");
try
{
th1.sleep(300);
}
catch (Exception e)
{
System.out.println(e);
}
lbl.setText("ddd:");
}
public static void main(String[] args)
{
new Dmi();
}
}

Start by taking a closer look at Concurrency in Swing and How to use Swing Timers
A Swing Timer will generate ActionEvents WITHIN the Event Dispatching Thread, which makes them really good for updating the UI from within.
However, your code...
public void actionPerformed(ActionEvent ae)
{
Thread th1=new Thread();
lbl.setText("abc");
try
{
th1.sleep(300);
}
catch (Exception e)
{
System.out.println(e);
}
lbl.setText("ddd:");
}
is blocking the Event Dispatching Thread (through the use of Thread#sleep), which will prevent the EDT from process new repaint events or any other user based events, making your program look as if it's hung.
You're either going to have to devise a solution which allows you to determine the amount of time the timer has been running and change the state based on that or possibly use a SwingWorker instead
For example...
import java.awt.EventQueue;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Dmi {
JFrame frame;
JLabel lbl;
Dmi() {
frame = new JFrame("abc");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
lbl = new JLabel("count");
frame.add(lbl);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Worker worker = new Worker();
worker.execute();
}
public class Worker extends SwingWorker<String, String> {
#Override
protected String doInBackground() throws Exception {
while (!isCancelled()) {
Thread.sleep(1000);
publish("abc");
Thread.sleep(300);
publish("def");
}
return null;
}
#Override
protected void process(List<String> chunks) {
lbl.setText(chunks.get(chunks.size() - 1));
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Dmi dmi = new Dmi();
}
});
}
}

Related

Executing methods in a running secondary thread. Java

I have some methods that need to be run from the same secondary thread (if the thread is different they don't work). I have the secondary thread running. Is it possible to execute these methods from this secondary running thread by invoking them from outside? I got good results extracting the content of the methods and putting it in a "state machine" inside the "run" method, but my question is if I have any other way to do this without extracting the content of the method and easier and cleaner.
Example:
package threadapp;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class ThreadApp {
public static void main(String[] args) {
Utils utils = new Utils();
}
}
class Utils {
JFrame frame;
JButton btn1;
public Utils() {
btn1 = new JButton("btn1");
frame = new JFrame();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setBounds(100, 100, 300, 200);
frame.setLayout(new FlowLayout());
frame.add(btn1);
frame.setVisible(true);
t1.setName("Thread that I need to use when press button");
t1.start();
btn1.addActionListener((ActionEvent e) -> {
execute = true;
});
}
public void ExecuteActionInTheSameSecondaryThread(){
// Large method to execute withowt extrac the content
}
///// It works!!! Content of the method//////
boolean execute = false;
////////////////////////
Thread t1 = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
///// It works!!! //////
if (execute) {
execute = false;
System.out.println("Button pressed in: " + Thread.currentThread().getName());
}
////////////////////////
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
}

How do I make a loop start and end with a key press and release?

Here is my code... How can I make it work so that it runs the loop while the user is holding a button and stops when the user releases the button?
public void nextPrimeNum()
{
x = false;
int b = 2;
ArrayList<Integer> next = new ArrayList<Integer>();
while(x)
{
next = factors(b);
if(next.size()==2)
{
System.out.println(b);
}
b++;
}
System.out.println("End");
}
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == 401)
{
x = true;
}
}
public void keyRealesed(KeyEvent e)
{
if(e.getKeyCode() == 402)
{
x = false;
}
}
GUI and multi-thread programming is inherently difficult.
So, this is as simple as it could be, without violating best practices too much.
You need several things:
A separate Thread for printing primes:
Its run method loops for ever, but pauses when the Space key is not pressed.
(see Defining and Starting a Thread for more info)
A KeyListener which will be called from AWT's event dispatch thread:
The event handling methods are designed to finish fast, so that other events
(like moving, resizing and closing the frame) still are handled fast.
(see How to Write a Key Listener
and The Event Dispatch Thread for more info)
A visible GUI component (JFrame) for adding the KeyListener
Some synchronization between the 2 threads (via synchronized, notify and wait)
so that the prime-printing starts/continues on keyPressed
and suspends on keyReleased
(see Guarded Blocks for more info)
Initialize and start the whole GUI by invoking initGUI.
(see Initial Threads for more info)
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class Main implements Runnable, KeyListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(Main::initGUI);
}
private static void initGUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JLabel("Press SPACE key for printing primes"));
frame.pack();
frame.setLocationRelativeTo(null); // center on screen
frame.setVisible(true);
Main main = new Main();
frame.addKeyListener(main);
Thread thread = new Thread(main);
thread.start();
}
private boolean spaceKeyPressed;
private boolean isPrime(int n) {
for (int i = 2; i < n; i++) {
if (n % i == 0)
return false;
}
return true;
}
#Override
public void run() {
for (int n = 2; /**/; n++) {
while (!spaceKeyPressed) {
synchronized (this) {
try {
wait(); // waits until notify()
} catch (InterruptedException e) {
// do nothing
}
}
}
if (isPrime(n)) {
System.out.println(n);
}
}
}
#Override
public void keyTyped(KeyEvent e) {
// do nothing
}
#Override
public synchronized void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
spaceKeyPressed = true;
notifyAll(); // cause wait() to finish
}
}
#Override
public synchronized void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
spaceKeyPressed = false;
notifyAll(); // cause wait() to finish
}
}
}
So, the answer is - it's complicated. It covers broad topics such as concurrency (in general), GUI development, best practices with the specific API (Swing) which are better covered in more detail by reading through the various tutorials (and experimenting)
Concurrency
Creating a GUI With JFC/Swing
Concurrency in Swing
Worker Threads and SwingWorker
How to Use Actions
How to Use Key Bindings
The example presents two ways to execute the "loop" (which is presented in the doInBackground method of the CalculateWorker class).
You can press and hold the JButton or press and hold the [kbd]Space[kbd] bar, both will cause the "main loop" to run, updating the JTextArea with the results...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextArea ta;
private CalculateWorker worker;
public TestPane() {
setLayout(new BorderLayout());
ta = new JTextArea(20, 20);
ta.setEditable(false);
add(new JScrollPane(ta));
worker = new CalculateWorker(ta);
JButton btn = new JButton("Press");
btn.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
System.out.println("...isRunning = " + worker.isRunning());
if (!worker.isRunning()) {
return;
}
System.out.println("...isPressed = " + btn.getModel().isPressed());
System.out.println("...isPaused = " + worker.isPaused());
if (btn.getModel().isPressed()) {
worker.pause(false);
} else {
worker.pause(true);
}
}
});
add(btn, BorderLayout.SOUTH);
worker.execute();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "Space.released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), "Space.pressed");
am.put("Space.released", new CalculateAction(false, worker));
am.put("Space.pressed", new CalculateAction(true, worker));
}
public class CalculateWorker extends SwingWorker<List<String>, String> {
private AtomicBoolean run = new AtomicBoolean(true);
private AtomicBoolean paused = new AtomicBoolean(false);
private ReentrantLock pausedLocked = new ReentrantLock();
private Condition pausedCondition = pausedLocked.newCondition();
private JTextArea ta;
public CalculateWorker(JTextArea ta) {
this.ta = ta;
pause(true);
}
public void stop() {
run.set(false);
pausedLocked.lock();
pausedCondition.signalAll();
pausedLocked.unlock();
}
public void pause(boolean pause) {
paused.set(pause);
pausedLocked.lock();
pausedCondition.signalAll();
pausedLocked.unlock();
}
public boolean isPaused() {
return paused.get();
}
public boolean isRunning() {
return run.get();
}
#Override
protected List<String> doInBackground() throws Exception {
List<String> values = new ArrayList<>(256);
long value = 0;
System.out.println("!! Start running");
while (run.get()) {
while (paused.get()) {
System.out.println("!! I'm paused");
pausedLocked.lock();
try {
pausedCondition.await();
} finally {
pausedLocked.unlock();
}
}
System.out.println("!! Start loop");
while (!paused.get() && run.get()) {
value++;
values.add(Long.toString(value));
publish(Long.toString(value));
Thread.sleep(5);
}
System.out.println("!! Main loop over");
}
System.out.println("!! Run is over");
return values;
}
#Override
protected void process(List<String> chunks) {
for (String value : chunks) {
ta.append(value);
ta.append("\n");
}
ta.setCaretPosition(ta.getText().length());
}
}
public class CalculateAction extends AbstractAction {
private boolean start;
private CalculateWorker worker;
public CalculateAction(boolean start, CalculateWorker worker) {
putValue(NAME, "Calculate");
this.start = start;
this.worker = worker;
}
#Override
public void actionPerformed(ActionEvent e) {
worker.pause(start);
}
}
}
}
Is there a simpler solution?
Of course, I always go for the most difficult, hard to understand solutions first (sarcasm)
While it "might" be possible to reduce the complexity, the example presents a number of "best practice" concepts which you would do well to learn and understand.
The solution could also be done differently depending on the API used, so, it's the "simplest" solution for the specific API choice.
I wanted to do it from the console!
Java can't do that - it's console support is rudimentary at best and doesn't support a concept of "key pressed/released" actions (since it's running in a single thread, it would be impossible for it to do otherwise).
There "are" solutions you might try, but they would require a third party library linked to native binaries to implement, which would (possibly) reduce the number of platforms it would run on

Java Swing GUI -how to have a thread to be sleeping all time and be awaken by a click?

sorry but it's the first time i use Threads.
i want Parlami class thread to sleep and be awaken only by the actionListener.
I tried this way but it isn't working, he still sleeps.
Is it right to use thread this way or should i use wait() ?
package parlami;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* #author giacomofava
*/
public class Parlami
{
public boolean finito = false;
public String s="";
public void ascolta()
{
int i=0;
while (i<=1500)
{
// dormi 50 millisecondi
try
{
Thread.sleep(50);
i+=40;
}
catch (InterruptedException e)
{
}
while (voce.SpeechInterface.getRecognizerQueueSize() > 0)
{
s = s+"\n"+voce.SpeechInterface.popRecognizedString();
}
}
}
public String scrivi()
{
return "Hai detto: "+s;
}
public void leggi()
{
voce.SpeechInterface.synthesize(s);
}
public void dormi(int milli)
{
try
{
System.out.println("i'm sleeping");
Thread.sleep(milli);
}
catch (InterruptedException ex)
{
System.out.println("i'm awake ");
ascolta();
}
}
}
this is the gui:
public class GUI extends JFrame
{
private Parlami p;
private JPanel nord, centro;
private JButton registra, leggi;
private JTextArea display;
public static void main(String[] args)
{
new GUI();
}
public GUI()
{
p=new Parlami();
initComponents();
}
private void initComponents()
{
voce.SpeechInterface.init("./lib", true, true,"./lib/gram", "vocabolario");
// N O R D
nord=new JPanel();
display=new JTextArea("");
display.setForeground(Color.GREEN);
display.setBackground(Color.BLACK);
nord.setBackground(Color.BLACK);
nord.add(display);
// C E N T R O
centro=new JPanel();
registra=new JButton("tieni premuto per registrare");
registra.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
Thread.currentThread().interrupt();// <-------- HERE I TRY TO AWAKE HIM
display.setText(p.scrivi());
}
});
centro.add(registra);
leggi=new JButton("leggi");
centro.add(leggi);
this.setLayout(new BorderLayout());
this.add(nord, BorderLayout.NORTH);
this.add(centro, BorderLayout.CENTER);
this.setSize(700,300);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
p.dormi(50000); // <-------- HERE I TELL HIM TO SLEEP
}
}
If you call Thread.sleep on the Swing event thread, you will put the entire application to sleep rendering it useless, but more importantly, there's no need to do this. You simply have the ActionListener activate whichever object needs activation as this is how event-driven programming works.
If you need a delay in a Swing application, use a Swing Timer, something that has been discussed over and over again on this site.
This is a basic concept of thread wait/notify associated with the topic of thread locks. Basically, you have some common object which is acting as the "lock", one thread "waits" on this thread and when another thread needs to, it "notifies" the monitors that some action has occurred to which they should/can respond.
It'd start by having a look at Lock Objects for more details.
Below is a very basic example of the concept, a Thread is allowed to run continuously, but which "waits" on the common lock. The ActionListener of the button "notifies" the lock when it is pressed, allowing the Thread to continue working until, once again, blocks at the "wait"
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Thread t = new Thread(new Runner());
t.start();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static final Object LOCK = new Object();
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
JButton btn = new JButton("Press me");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
synchronized (LOCK) {
LOCK.notifyAll();
}
}
});
add(btn);
}
}
public class Runner implements Runnable {
#Override
public void run() {
while (true && !Thread.currentThread().isInterrupted()) {
synchronized (LOCK) {
try {
System.out.println("Nothing to see here, just waiting");
LOCK.wait();
} catch (InterruptedException ex) {
}
}
System.out.println("Look at me, I'm busy");
}
}
}
}
Remember, Swing is single threaded, never perform any action which is blocking within the context of the Event Dispatching Thread, equally, never update the UI from outside the EDT.
If you need to update the UI for some reason from the other thread, then I suggest you have a look at SwingWorker, which will make your life much simpler. See Worker Threads and SwingWorker for more details.
You have an ActionListener which is notified when the button is activated, why do you need a monitor lock to perform the associated action? Does it take a noticeable amount of time to start the required action? You could just start a new thread when the button is clicked.
If you're waiting for some kind of timeout, then, to be honest, a Swing Timer is probably more suited to the task

Swing Progress Bar updates via Worker to EventDispatch thread

I have a JAVA6 GUI handling data import to our database. I have implemented a working JProgressBar. I understand that changes made to the GUI must be done via the event dispatch thread--which I do not think I am doing (properly/at all).
the background Worker thread, UploadWorker, is constructed by passing in the a JProgressBar created in the main program, and sets changes the value of the progress bar directly once it is finished:
// when constructed, this gets set to the main program's JProgressBar.
JProgressBar progress;
protected Void doInBackground() throws Exception {
write("<!-- Import starting at " + getCurrentTime() + " -->\n");
boolean chunked = false;
switch (importMethod) {
//do some importing
}
write("<!-- Import attempt completed at " + getCurrentTime() + "-->\n");
//here changes to the GUI are made
progress.setMaximum(0);
progress.setIndeterminate(false);
progress.setString("Finished Working");
return null;
}
This works fine, but sometimes(not always) throws me several NPE's in the std out, and users are complaining:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at javax.swing.plaf.basic.BasicProgressBarUI.updateSizes(Unknown Source)
...etc...
Anyway, I believe there is something I need to do to get these updates executed on the proper thread, correct? How?
There are a number of ways you could do this, you could use the process method of the SwingWorker to also update the progress bar, but for me, this couples your worker to the UI, which isn't always desirable.
A better solution is to take advantage of the SwingWorkers progress and PropertyChange support, for example....
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equalsIgnoreCase(evt.getPropertyName())) {
SwingWorker worker = (SwingWorker) evt.getSource();
switch (worker.getState()) {
case DONE:
// Clean up here...
break;
}
} else if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
// You could get the SwingWorker and use getProgress, but I'm lazy...
pb.setIndeterminate(false);
pb.setValue((Integer)evt.getNewValue());
}
}
});
worker.execute();
This means you could do this for ANY SwingWorker, so long as it was the worker was calling setProgress internally...
public static class ProgressWorker extends SwingWorker {
public static final int MAX = 1000;
#Override
protected Object doInBackground() throws Exception {
for (int index = 0; index < MAX; index++) {
Thread.sleep(250);
setProgress(Math.round((index / (float)MAX) * 100f));
}
return null;
}
}
The benefit of this is that the PropertyChange event notification is called from within the context of the of Event Dispatching Thread, making it safe to update the UI from within.
And fully runnable example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SwingWorkerProgressExample {
public static void main(String[] args) {
new SwingWorkerProgressExample();
}
public SwingWorkerProgressExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JProgressBar pb;
public TestPane() {
setLayout(new GridBagLayout());
pb = new JProgressBar(0, 100);
pb.setIndeterminate(true);
add(pb);
ProgressWorker worker = new ProgressWorker();
worker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equalsIgnoreCase(evt.getPropertyName())) {
SwingWorker worker = (SwingWorker) evt.getSource();
switch (worker.getState()) {
case DONE:
// Clean up here...
break;
}
} else if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
// You could get the SwingWorker and use getProgress, but I'm lazy...
System.out.println(EventQueue.isDispatchThread());
pb.setIndeterminate(false);
pb.setValue((Integer) evt.getNewValue());
}
}
});
worker.execute();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public static class ProgressWorker extends SwingWorker {
public static final int MAX = 1000;
#Override
protected Object doInBackground() throws Exception {
for (int index = 0; index < MAX; index++) {
Thread.sleep(250);
setProgress(Math.round((index / (float) MAX) * 100f));
}
return null;
}
}
}
You can just create a new Runnable that performs GUI updates and invoke it in a GUI thread using SwingUtilities.invokeLater

Java dialog does not dispose

Java noob here. My Swing class that extends JDialog does not dispose when the user presses the Windows Close button - java.exe stays in memory. I've stripped the code right down to this shell, I still get this behaviour.
I took a look at other samples, such as at Basic Java Swing, how to exit and dispose of your application/JFrame
When I commented out the two System.exit(0) lines in that sample code, the class in that sample still disposed correctly. What am I missing to make my class dispose?
import javax.swing.JFrame;
import javax.swing.JDialog;
public class WhyNoDispose extends JDialog{
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
WhyNoDispose frame = new WhyNoDispose("my title");
frame.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
frame.setVisible(true);
//System.exit(0);
}
catch (Exception e) {
e.printStackTrace();
}
}
});
}
public WhyNoDispose(String title) {
super(new JFrame(title), ModalityType.APPLICATION_MODAL);
pack();
}
}
You're creating a JFrame and never disposing it here:
public WhyNoDispose(String title) {
super(new JFrame(title), ModalityType.APPLICATION_MODAL); // *********
pack();
}
So since the JFrame is alive and a GUI has been rendered, the Swing event thread keeps on running.
If you instead make the JFrame behave so that the program exits on JFrame close, and then explicitly dispose of the JFrame, your program now exits:
import java.awt.Window;
import javax.swing.JFrame;
import javax.swing.JDialog;
public class WhyNoDispose extends JDialog {
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
WhyNoDispose frame = new WhyNoDispose("my title");
frame.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
JFrame win = (JFrame) frame.getOwner();
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
win.dispose();
// System.exit(0);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public WhyNoDispose(String title) {
super(new JFrame(title), ModalityType.APPLICATION_MODAL);
pack();
}
}
But this is very kludgy code, to say the least -- what if the owning window isn't a JFrame? What if it's null?
Another solution is to use no JFrame at all, so that when the JDialog is disposed, there's no persisting window left over to make the event thread persist:
import java.awt.Window;
import javax.swing.JFrame;
import javax.swing.JDialog;
public class WhyNoDispose extends JDialog {
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
WhyNoDispose frame = new WhyNoDispose("my title");
frame.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public WhyNoDispose(String title) {
super((JFrame)null, title);
pack();
}
}

Categories