How can I update JFrame objects during WindowClosing event? - java

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();
}
});

Related

Show ProgressBar with windowClosing(WindowEvent we)

I have a JFrame. Frame was executed on EDT. A window closing event is being added to that frame using window adapter. What i need is; There is a background task that delete files generated by the application; and that task starts running when the close button of frame is being clicked. I want to show progressbar for that background tasks?
public class CloseApplication extends WindowAdapter{
#Override
public void windowClosing(WindowEvent we) {
new Thread(new Runnable() {
#Override
public void run() {
delete.deleteDirectory(a);
delete.deleteDirectory(b);
delete.deleteDirectory(c);
delete.deleteDirectory(d);
}
}).start();
}
}
I tried to add progress bar to the process but it didn't displayed. I then called it in new thread; still no success. Can you give me any idea that how this can be done?
Either way i use to call it in new thread it dont work. The reason is; background task executes in new thread and windowClosing comes to an end and close the application. If i call it without it; it makes the UI unresponsive.
Thanks in advance.

How is a JDialog's owner still updating it's UI even when it's EDT is blocked?

I have the following example code:
public class MainWindow extends JFrame implements ActionListener {
public MainWindow() {
JButton openButton = new JButton('Open');
openButton.addActionListener(this);
add(openButton);
}
public void actionPerformed(ActionEvent e) {
// Create and show a modal dialog.
JDialog dialog = new JDialog(this, true);
dialog.setVisible(true);
}
}
When I click the openButton it calls actionPerformed(ActionEvent e) on the event dispatch thread and dialog.setVisible(true) blocks it.
But with more sophisticated frames I notice that they still update their UI from non user generated events such as a Timer action.
In any other case that I block the EDT my UI completely hangs, but when dialog.setVisible(true) blocks the EDT the owner's UI continues to update.
So my question is how does this work?

Swing: How to prohibit interaction outside of JFrame/JDialog?

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.

(Java) Appearing/Disappearing JLabel in a JPanel only disappears on resize

I'm working in Java, and I have a JPanel in a JFrame. In that JPanel, among other things, I have a JLabel that I want to make appear and disappear at will. I've tried setting visibility to true/false, adding and removing it from the JFrame and JPanel, and, having looked online, I tried validate()ing and revalidate()ing ad infinitum. What can be done here to solve this problem?
In general, calling the setVisible method is sufficient to make a Swing component to be shown or hidden.
Just to be sure that it works, I tried the following:
public class Visibility {
private void makeGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JLabel l = new JLabel("Hello");
final JButton b = new JButton("Hide Label");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
l.setVisible(false);
}
});
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(l, BorderLayout.CENTER);
f.getContentPane().add(b, BorderLayout.SOUTH);
f.setSize(200, 200);
f.setLocation(200, 200);
f.validate();
f.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Visibility().makeGUI();
}
});
}
}
The above program is able to affect the visibility by clicking on a JButton.
Could it be a Threading Issue?
My next suspicion was that perhaps a Thread that is not on the event dispatch thread (EDT) may not be affecting the display immediately, so I added the following after initializing the JLabel and JButton.
Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
b.setVisible(!b.isVisible());
try {
Thread.sleep(100);
} catch (InterruptedException e) { /* Handle exception /* }
}
}
});
t.start();
With the new Thread running, it changed the toggled the visibility of the JLabel every 100 ms, and this also worked without a problem.
Calling a Swing component off the event dispatch thread (EDT) is a bad thing, as Swing is not thread-safe. I was a little surprised it worked, and the fact that it works may just be a fluke.
Repaint the JPanel?
If the JLabel's visibility is only being affected on resizing, it probably means that the JLabel is being drawn only when the JPanel is being repainted.
One thing to try is to call the JPanel's repaint method to see if the visibility of the JLabel will change.
But this method seems to be just a band-aid to a situation, if the main cause is due to a thread off the EDT is attempting to make changes to the GUI. (Just as a note, the repaint method is thread-safe, so it can be called by off-EDT threads, but relying on repaint is a workaround than a solution.)
Try using SwingUtilities.invokeLater
Finally, probably the thing I would try is the SwingUtilities.invokeLater method, which can be called (and should only be called) from a thread running separate from the EDT, if it wants to affect the GUI.
So, the earlier Thread example should be written as:
Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
b.setVisible(!b.isVisible());
}
});
} catch (Exception e1) { /* Handle exception */ }
try {
Thread.sleep(100);
} catch (InterruptedException e) { /* Handle exception */ }
}
}
});
t.start();
If the change to the GUI is indeed occurring on a separate thread, then I would recommend reading Lesson: Concurrency in Swing from The Java Tutorials in order to find out more information on how to write well-behaving multi-threaded code using Swing.
setVisible() or removing it should work fine, make sure you are doing it from the event dispatch thread though. There are utility methods in EventQueue for running blocks in that thread.
http://helpdesk.objects.com.au/java/how-do-i-update-my-gui-from-any-thread
You would need to call revalidate() on the parent JPanel if you need its components to be re-laid out.
If you can post an example that demonstrates the problem I can have a look at it for you.

Component in a JScrollPane stops receiving KeyEvents

I am putting a component ( derivative on JPanel ) inside a JScrollPane.
scrollPane = new JScrollPane(component);
since the component occasionally changes size, I have to occasionally do :
SwingUtilities.invokeLater(new Runnable(){
public void run()
{
scrollPane.getViewport().setView(component);
component.repaint();
}
});
Also, the component can receive KeyEvents
component.addKeyListener(this);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
component.setFocusable(true);
component.requestFocusInWindow();
}
});
However, the component never receives any Key Events. Even the code in keyTyped() does not execute ( I put a System.out.println() there).
What is more baffling, is, in debug mode, I can pause the main thread, when the AWT event thread would accept key events. But during normal execution, it does not work.
Can anyone suggest what I am doing wrong ?
Making my component java.swing.Scrollable, and replacing scrollPane.getViewport().setView(component);
with component.revalidate solved my prioblem.
Thanks ordnungswidrig!

Categories