JAVA GUI Closing Application Dialog - java

Details:
I have a JAVA application that takes some time to shutdown. There is a call to close a port, that takes a really long time. I want to add a dialog box that indicates to the user that the application is shutting down. Normally, I would create a dialog box, start a thread to do long work and close dialog, then display the dialog. Once the work is done, the dialog would be close. This does not work for shutting down an application because it seems the window listener closes all windows (kind of makes sense, it supposed to do that). I'm not sure a way around this.
Code:
public void windowClosing(WindowEvent we)
{
shutDown();
}
public void shutdown()
{
final JDialog dialog = createDialog();
Thread t = new Thread
{
public void run()
{
saveProperties();
ClosePort();
dialog.setVisible(false);
System.exit(0);
}
};
t.start();
dialog.setVisible(true);
}

t.setDaemon(true);
as daemon threads stay alive even if the rest is gone.

Related

why i can't do anything in my application when Jbutton is pressed and performing its defined function in java?

this is how I'm trying to accomplish this:
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Hello there");
Thread.sleep(1000);
panel.updateUI();
}
});
I set Enter button as the default button so when I keep pressing it the button press maybe 100 times or more but because I'm using Thread.sleep(1000) it takes some time so I have time to type in my JtextField or even close the window but can't do anything.
also, I tried to put btnNewButton.addActionListener() in the run method of a thread but no difference.
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Hello there");
Thread.sleep(1000);
panel.updateUI();
}
});
}
});
thread.start();
// I used try catch block in real code
can anyone help me to solve the issue?
**I'm creating this application in eclipse using windowsBuilder.
This is because of something that is called EDT - the Event Dispatch Thread.
This special purpose thread is responsible for all the events in your GUI - including drawing (refreshing) and interactions (button click). This means, that exactly the same thread is used to draw your application as the one that is used to execute actionPerformed code. Since you are literally halting that thread for some amount of time, your application will be non responsive for that exact period of time.
Whatever is being executed on user interactions should be short and execute fast for the reason of not blocking the application. If you need to do some heavy stuff, there is a facility for that purpose called SwingWorker that allows you to easily do some processing in the background thread (pool) and schedule UI updates during execution (like update of progress bar)
In your second "thread" snippet, your thread is not doing anything beside adding actionListener to the button, and then it terminates (probably after less than 1ms ;)) - action callback is still executed by the EDT. If you would start that custom thread from inside of run method - then it would be indeed in parallel and GUI would not be frozen - but again, SwingWorker is the way to go here.
Not entirely sure what your code is supposed to do, but if you want to press the button and then 1000ms later it will check your field you should do something like this:
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
System.out.println("before 1000ms");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("after 1000ms");
System.out.println("Reading the text field ...");
}
});
thread.start();
System.out.println("after the thread");
Output:
after the thread
before 1000ms
after 1000ms
Reading the text field ...

Java FX : Unable to open popup from different FX thread

Hi I have application which runs both on GUI(Java FX) as well as command line.
When run as GUI, i show the status on text area. This works fine.
But the issue is when ever i try to show a error(via popup) from some different (non javafx) class, it shows me Java Fx - thread Exception not on FX thread.
Below is my code
This is my Java FX class where I wish to show popup.
public class DeploymentProcesController implements Initializable {
#FXML
private TextArea statusTextArea;
#Override
public void initialize(URL location, ResourceBundle resources) {
}
public void updateGUIMessage(String message) {
if (Platform.isFxApplicationThread()) {
statusTextArea.appendText(message);
} else {
Platform.runLater(new Runnable() {
#Override
public void run() {
statusTextArea.appendText(message);
}
});
}
}
public void displayAlertMessages(final String message) {
Platform.setImplicitExit(false);
Task<Void> task = new Task<Void>() {
#Override public Void call() {
Platform.runLater(new Runnable() {
public void run() {
Alert alert = new Alert(AlertType.INFORMATION, message, ButtonType.OK);
alert.showAndWait();
}
});
return null;
}
};
new Thread(task).start();
}
}
I have a non FX class which is the entry point. So Based on type of run (command line / GUI ) I update the status.
Here is how I am calling to update the status.
public void promptUser(String message,boolean isCommandLineRun){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
if(isCommandLineRun) {
System.out.println(simpleDateFormat.format(new Date()) + " - " + message);
} else {
controller.displayAlertMessages(message);
}
}
I have no issues when i call the updateGUIMessage method from non fx class. This is because the statusArea element is on FX thread(member of this fx class).
Also I have no issues to generate a alert box on some button click,
but to display an Alert box from a different class- I am having issues since as soon as I try to generate a alert box , the application crashes, saying not on fx thread.
I understand that the Alert box is a popup and therefore may be unhandled. But can anyone help me, I want to show user a alert box, from different class.
Assuming that you want some long running code to run before the popup is called,
There are two steps that need to be done when dealing with the Fx Buttons. The first thread lets the code run as a thread when the button is pushed. The second is the Platform.runlater() which is telling the Fx Platform to execute the code in that thread from the Platform. Note that the popup will not get executed until the runlater is reached in the outer thread.
Otherwise you can call the popup directly.
public void MyExampleBtn() {
Thread t = new Thread() {
// long running work to be done, threaded so as not to hang the main controls.
// ....
// work is done do the success popup
#Override
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Success!");
alert.setHeaderText(null);
alert.setContentText("This is my popup");
alert.showAndWait();
}
});
}
};
}
Everything in the UI has to be executed from the UI application thread. That is exactly what the error message means.
Fortunately you can simply wrap your call so that it is executed in the UI thread:
if(isCommandLineRun) {
System.out.println(simpleDateFormat.format(new Date()) + " - " + message);
} else {
Platform.runLater(() -> controller.displayAlertMessages(message));
}
Finally found the solution to it,
Since Java fx runs on single thread, everything has to be on same thread. For every task (such as popup) where there needs background to pause, we need to use FutureTask.
I found this article, here :
JavaFX2: Can I pause a background Task / Service?

Disable parent frame without stopping thread?

I'm developing a Swing app, and I need to run an infinite loop in the background (which runs until: 1) the cancel button of my JDialog is selected or 2) the input data it is searching for is found) while a modal dialog shows an indeterminate progress bar.
Something I've noticed is that if the JDialog is modal, then the SwingWorker will not execute its tasks until the JDialog is closed (and releases its deathgrip on the EDT, I guess...?). If the JDialog is not modal, then SwingWorker's tasks will execute happily in the background.
I've been doing some research, but I'm no thread/EDT expert and am having a hard time figuring the reason/solution.
Any input on this situation/threads/EDT/SwingWorker, or a suggested solution, would be greatly appreciated.
(Question pulled directly from: http://www.coderanch.com/t/346275/GUI/java/SwingWorker-Modal-JDialogs)
I tried the solution regarding the setVisible call of the JDialog like this user found to be the solution, but I still can't execute both threads simultaneously. Any help would be appreciated.
Relevant:
public Dialog(JFrame parentFrame, String equipmentName) {
super(parentFrame, "Progress");
this.hasRequestedCancel = false;
this.equipmentName = equipmentName;
add(createMainPanel());
setIconImage(Toolkit.getDefaultToolkit().getImage(SomeClass.class.getResource(ICON_PATH)));
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
setModalityType(ModalityType.DOCUMENT_MODAL);
pack();
setSize(550, 100);
setResizable(false);
setLocationRelativeTo(parentFrame);
setVisible(true);
}
And
SwingWorker<File, Void> worker = createSwingWorker(params, ...);
worker.execute();
And
private SwingWorker<File, Void> createSwingWorker(final File someFile, final SomeClass asdf, final String param3) throws IOException {
SwingWorker<File, Void> swingWorker = new SwingWorker<File, Void>() {
#Override
protected File doInBackground() throws IOException {
Dialog progressBar = new Dialog(SomeClass.this, SomeClass.this.equipManufacturerDevice);
try {
while(!someFile.exists() && !progressBar.hasRequestedCancel()) {
Thread.sleep(SomeClass.SLEEP_DURATION);
System.out.println("yo");
}
} catch (InterruptedException e) {
}
...
}
#Override
protected void done() {
...
}
};
return swingWorker;
}
The problem is that you are calling setVisible(true) inside the Dialog’s constructor which is a discouraged practice anyway (you just found one reason, why).
Separate the creation and opening of the dialog and you don’t have that problem anymore. The following sample code demonstrates how this can be achieved:
final Dialog d=new Dialog((Window)null);
d.setSize(300, 300);
d.setModal(true);
new SwingWorker<Object,Object>() {
#Override
protected Object doInBackground() throws Exception {
System.out.println("long running stuff");
TimeUnit.SECONDS.sleep(10);
System.out.println("end of long running stuff");
return null;
}
#Override
protected void done() {
d.dispose();
}
}.execute();
System.out.println("before setVisible(true)");
d.setVisible(true);// will block
System.out.println("after setVisible(true)");
What if you moved the data input logic from the main frame and kept it running on a separate, dedicated, background thread whose sole job is to listen for connections and handle them. This would leave your parent JFrame to handle UI interactions thereby giving you the freedom to freeze it when one of your JDialog has focus.

Mouse Clicks being cached?

I have a java swing application with a login screen. The login screen has a submit button for pressing after the user's credentials have been entered. When the button is pressed, the a wait cursor is thrown up over the window using its glass pane. There is also a default mouse adapter that does nothing for any mouse action.
private final static MouseAdapter mouseAdapter =
new MouseAdapter() {};
/** Sets cursor for specified component to Wait cursor */
public static void startWaitCursor(JComponent component) {
log.debug("startWaitCursor()");
RootPaneContainer root =
((RootPaneContainer) component.getTopLevelAncestor());
Component glass = root.getGlassPane();
glass.setCursor(WAIT_CURSOR);
glass.addMouseListener(mouseAdapter);
glass.setVisible(true);
//force repaint of glass pane in 20ms for more responsive GUI
glass.repaint(20);
}
public static void stopWaitCursor(JComponent component) {
log.debug("stopWaitCursor()");
RootPaneContainer root =
((RootPaneContainer) component.getTopLevelAncestor());
Component glass = root.getGlassPane();
glass.setCursor(DEFAULT_CURSOR);
glass.removeMouseListener(mouseAdapter);
//force repaint of glass pane in 20ms for more responsive GUI
glass.repaint(20);
glass.setVisible(false);
}
I had assumed that this setup protected me against multiple clicks/keypresses while the backend methods were taking place. I found out that this was not the case. So in the ButtonListener.actionPerformed, I put some logic like the following:
static boolean waiting = false;
class ButtonListener implements ActionListener {
ButtonListener() {
super();
}
public void actionPerformed(ActionEvent e) {
log.info("LoginWindow.ButtonListener.actionPerformed()");
LoginWindow.this.repaint(50);
if (!waiting) {
try {
waiting = true;
verifyLogin();
} finally {
waiting = false;
}
}
}
}
I found that this protected me against keypresses, but not mouse clicks! If I repeatedly press the submit button while verifyLogin() is executing, the mouse clicks seem to be being cached somewhere, and after verify login finishes, each mouse click is processed!
I am extremely puzzled about what is going on here. Does someone have an idea?
Update:
Hmm, by following the methodology suggested by Cyrille Ka: i.e. executing the verifyLogin() method in a separate thread and disabling the button, I now only get TWO events after multiple mouse clicks but the second one still annoys.
Code is now:
public void actionPerformed(ActionEvent e) {
loginButton.setEnabled(false);
log.infof("LoginWindow.ButtonListener.actionPerformed(). Event occurred at %1$tb %1$te %1$tY %1$tT.%1$tL",
new Date(e.getWhen()));
LoginWindow.this.repaint(50);
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
verifyLogin();
loginButton.setEnabled(true);
}});
}
but the second event still gets in. My log shows me that the second event took place about 280 ms after the first, but did not execute until 4 seconds later, in spite of the fact that setEnabled() was the first thing the actionPerformed() event did.
2013-11-13 10:33:57,186 [AWT-EventQueue-0] INFO
c.a.r.s.c.g.LoginWindow -
LoginWindow.ButtonListener.actionPerformed(). Event occurred at Nov 13
2013 10:33:57.175 2013-11-13 10:34:01,188 [AWT-EventQueue-0] INFO
c.a.r.s.c.g.LoginWindow -
LoginWindow.ButtonListener.actionPerformed(). Event occurred at Nov 13
2013 10:33:57.453
I suppose I could do a hack and discard events over a second old or something, but that feels ugly. This should not be so difficult, I keep thinking.
Update 2:
comment from JComponent.java for setEnabled()
* <p>Note: Disabling a lightweight component does not prevent it from
* receiving MouseEvents.
Since all of the Swing components are lightweight, and setEnabled does not prevent the component from receiving mouse events, what does prevent this?
I had assumed that this setup protected me against multiple clicks/keypresses while the backend methods were taking place. I found out that this was not the case.
The section from the Swing tutorial on The Glass Pane gives an example of how you might do this. Don't remember if it only handles MouseEvents or KeyEvents as well.
In any case you can also check out Disabled Glass Pane, which does handle both events.
I presume verifyLogin() is blocking until the login is done. By doing this, you are just blocking the Swing event dispatcher thread. The events from the OS still are queuing to be sent to your GUI when the thread will be available.
There are two ways to prevent your user clicking repeatidly:
Just disable the button: button.setEnabled(false); and enable it back when the process is finished.
Launch a modal dialog (for example with a wait animation) and remove it when the process is finished.
Edit: In general, you should return quickly from event listeners, since you don't want to block all your GUI, only certain part, and in any case it makes your app feel sluggish (the window won't repaint in the meantime if it is moved or other stuff). Use Thread to launch a task running a verifyLogin() and disable your button in the meantime.
This works:
class ButtonListener implements ActionListener {
long previousEventEnd;
public void actionPerformed(ActionEvent e) {
if (e.getWhen() <= previousEventEnd ) {
log.tracef("discarding stale event, event occurred at %1$tb %1$te %1$tY %1$tT.%1$tL",
new Date(e.getWhen()));
return;
}
log.infof("LoginWindow.ButtonListener.actionPerformed(). Event occurred at %1$tb %1$te %1$tY %1$tT.%1$tL",
new Date(e.getWhen()));
LoginWindow.this.repaint(50);
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
verifyLogin();
previousEventEnd = System.currentTimeMillis();
}
});
}
}
I have to admit I'm astonished. I usually defend Java to its detractors. Here I have no defense at this point. This should not be necessary.

Swing Worker Modal Dialog Won't Close

I have a SwingWorker thread that launches a modal dialog box (from a property change listener that listens to the StateValue of started) and the swing worker proceeds to do its work. However, it looks like the done method is not called because that is called on the EDT but the swing worker's modal dialog is blocking the EDT. So, I can't close the dialog from the EDT (or from the done method). Right now I'm just closing the dialog from the doInBackground at the end of that method, but that seems a little unsafe from the doInBackground since it's not on the EDT. What's the best way to handle this? thanks.
The dispatch loop should continue to dispatch the events associated with SwingWorker even when a modal dialog is displayed.
This works for me.
import javax.swing.*;
public class Unions {
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
runEDT();
}});
}
private static void runEDT() {
final JDialog dialog = new JDialog((JFrame)null, true);
new SwingWorker<Void,Void>() {
#Override protected Void doInBackground() throws Exception {
// But this is working.
Thread.sleep(3000);
return null;
}
#Override protected void done() {
dialog.setVisible(false);
}
}.execute();
dialog.setVisible(true);
}
}
For reference:
When a modal dialog is launched in Swing, the execution of that thread is stopped until the dialog is closed.
This is why your done() method was never called (doInBackground() couldn't finish and done() is only called after that).
Opening a modal-dialog from an action called by the EDT thread is slightly different. The EDT itself will continue to process events but the actual event thread code (the code of the action) which opens the modal dialog still gets blocked (and waits until the dialog is closed).
Naturally, in case of non-modal dialogs, this problem never surfaces.
By the way: You should never open a dialog from outside the EDT.
If the decision is made on a non-EDT thread, you need to use SwingUtilities.invokeLater() to actually open the dialog.
Sounds complicated but it is actually not, once you master the concept of the EDT.

Categories

Resources