Calling JFileChooser twice in invokelater causes program not to exit - java

I am writing a program to collect information from two text files to add to tables in a database. In order to permit the user to select his own files I created a non-static method called chooseFile() that uses JFileChooser class to present a showOpenDialog (I've also tried it as a static method with the same results. If this sounds like I'm guessing, you're correct - I'm only so-so with programming).
My understanding is that calls to Swing classes in main() should be done using invokelater. This all worked fine (the JVM exits successfully) for one call to chooseFile(), but when I added a second call to chooseFile() the JVM keeps running indefinitely. However, the program exits normally with both calls to chooseFile() if I do so without using invokelater. Adding System.exit(0) after the second call in invokelater also allows the program to exit normally.
From what I have been able to gather, the program won't exit until all the (non-daemon) threads are closed. However, I thought that the purpose for using invokelater was to push all non-thread safe Swing related activities onto the EDT. Is using System.exit(0) the best practice here or is there something else I should know about?
Here is my SSCCE:
package ptMngr;
import java.io.IOException;
import java.nio.file.Path;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;
public class ParticipantManager {
private Path chooseFile() throws IOException{
JFileChooser chooser = new JFileChooser();
FileNameExtensionFilter filter = new FileNameExtensionFilter("Text Files", "txt","csv");
chooser.setFileFilter(filter);
int returnVal = chooser.showOpenDialog(null);
if(returnVal == JFileChooser.APPROVE_OPTION) {
System.out.println("You chose to open this file: " +
chooser.getSelectedFile().getName());
}
Path path = chooser.getSelectedFile().toPath();
return path;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args){
// ParticipantManager pm = new ParticipantManager();
// try {
// pm.chooseFile();
// pm.chooseFile();
//
// } catch (IOException ex) {
// Logger.getLogger(ParticipantManager.class.getName()).log(Level.SEVERE, null, ex);
// }
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ParticipantManager pm = new ParticipantManager();
try {
pm.chooseFile();
pm.chooseFile();
System.exit(0);
} catch (IOException ex) {
Logger.getLogger(ParticipantManager.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
}

In your call to showOpenDialog(null) you pass null as the parent component for the JFileChooser. It's been some time since I worked with JFileChooser, but I seem to remember, that a "magic" JFrame may be constructed, when null is passed in. It may be possible, that in this use case two "magic" JFrames are created and they prevent termination.
You may want to try to create a JFrame which serves as parent for both JFileChoosers so it does not create the automatic frames.
EDIT:
My first take would be this:
public static class ParticipantManager {
JFrame frame;
private JFrame getFrame() {
if (frame==null) {
frame = new JFrame();
frame.setSize(600, 400);
frame.setLocationRelativeTo(null);
// frame.setVisible(true); // <---- worked for me without this call too
}
return frame;
}
private void close() {
frame.dispose();
}
private Path chooseFile() throws IOException{
JFrame f = getFrame();
...
int returnVal = chooser.showOpenDialog(f);
...
...
pm.chooseFile();
pm.chooseFile();
pm.close(); // <----- close the frame
...

Related

Java swing GUI not showing up

I am using Java 11 on Debian 4. I am trying to build a very basic Java GUI. To start with I have the following code:
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
public class BasicSwing extends JFrame {
JPanel p = new JPanel();
JButton b = new JButton("Hello");
public static void main (String[] args) {
new BasicSwing();
}
public BasicSwing() {
super("Basic Swing");
setSize(400,300);
setResizable(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
p.add(b);
add(p);
setVisible(true);
}
}
I have the X11 server running. The code does not fail but the GUI does not show up. I am not using Netbeans and I compile and run the code just as I would run and compile any other java code, ie with javac and java commands.The code does not stop and does not throw any error. Am I missing something very basic? I have seen a lot of discussion on the GUI not showing up but I am unable to find a solution to this problem given my specific development environment.
Instead of calling the setVisible method inside of your JFrame extended class's constructor, You should make a call on it in your main function.
Do it this way:
public static void main (String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
BasicSwing mySwingApp = new BasicSwing();
mySwingApp.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
Please read more about why I use a java.awt.EventQueue here.
Update:
It's not good practice to directly create a class that extends JFrame. Also, please read this too, for more clarification.

How to exit 2D game simulation (only) without exiting the Java code?

I (new to Java) am working on a decades old Java project built using Golden T Studios Game dev jdk. I have a game in the project which runs a 2D simulation. The code structure in a nutshell is as follows:
package game;
import java.io.*;
import java.lang.reflect.Field;
import javax.swing.JFrame;
import com.golden.gamedev.GameLoader;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import sometcpimports.*;
public class MainGAME extends JFrame implements Runnable {
public static void main(String[] args) { //call new MainGAME;
}
public MainGAME() {//initiate game parameters;
//start new THREAD;
}
#Override
public void run() { //initiate new game and environment;
game = new GameLoader();
gameenv = new GameEnvironment(params); //This class is in another file "public class GameEnvironment extends Game {}"
//I don't clearly undertsand what the following lines do, so I'm mentioning them as is;
game.setup(gameenv, dimensions);
this.setVisible(false);
gameenv.setVisible(false);
game.start();
game.setVisible(true);
//tbd (code reaches this step)
}
}
My goal is to run the above simulation multiple times (with different inputs each time) and extract information after each run in a new main class as follows.
public class gamedriver {
public static void main(String[] args) {
MainGAME.params = some params;
MainGAME.main(); // runs the simulation;
//tbd1 (code doesn't reach this step)
}
}
The issue is that, on running the simulation from a different file (new main class), I am unable to exit 2D simulator after one run. The code doesn't reach //tbd1 in the new main class which has some output print statements. Essentially I want to simply exit simulator and not the whole JVM. So far I've tried:
game.stop() & gameenv.finish() at //tbd which does nothing.
System.exit(0) at //tbd which exits game after simulation but also exits jvm and doesnt reach the other main class.
finish() at both //tbd and GameEnvironment class which behaves exactly like point 2.
Additionally, I am unable to run the above (MainGAME in gamedriver class) in a for loop. This throws Game.Exception error. I understand it has something to do with threads but I'm not sure how to run them.
Thank you for your time. Appreciate your help!
I found a method to solve the above problem. I'm posting my workaround as a reference for someone who encounters a similar issue. Please note that there might be other (better) solutions out there.
I disabled all (any) threads implementation in the MainGAME (by simply commenting it out).
I added System.out.println() in MainGAME to print all my outputs at the end of simulation.
My second script uses Runtime processes to execute MainGAME:
public class gamedriver {
public static void main(String[] args) {
try {
String separator = System.getProperty("file.separator");
String classpath = System.getProperty("java.class.path");
String path = System.getProperty("java.home") + separator + "bin" + separator + "java";
String[] command = new String[]{path, "-cp", classpath, gamedriver.GAME.class.getName(), "output.csv"};
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
static class GAME {
public static void main(String args[]) {
PrintStream out = null;
try {
out = new PrintStream(args[1]);
} catch (FileNotFoundException p) {
p.printStackTrace();
}
System.setOut(out);// this catches all output from the game to a csv file (output.csv)
MainGAME temp = new MainGame(some params); // initiate the simulation
temp.run()
out.close();
System.exit(0); // force close the simulator
}
}
}
I parse output.csv to get the solution.
This might be a sloppy way to solve the issue, but it worked for me, especially because none of the other methods provided by GTGE worked.

Can multiple showMessageDialogs break swing?

This is simplified code, which would be called from pressing a button in my main JFrame class. Using this code, and then dismissing one of the resulting dialogs, causes all of my active windows in my Java session to either lock up or just go away.
//Broken code
private void buttonActionPerformed(java.awt.event.ActionEvent evt) {
List<JFrame> frameList = new ArrayList<>();
frameList.add(new TestJFrame());
frameList.add(new TestJFrame());
frameList.forEach(frame -> frame.setVisible(true));
frameList.forEach(frame -> {
SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(frame, "Msg", "Title", 0);
frame.setVisible(false);
frame.dispose();
});
});
}
However, if I were to remove the SwingUtilities.invokeLater() section then it works like I would expect (dialog pops up, close the dialog, window goes away, repeat).
//Working code
private void buttonActionPerformed(java.awt.event.ActionEvent evt) {
List<JFrame> frameList = new ArrayList<>();
frameList.add(new TestJFrame());
frameList.add(new TestJFrame());
frameList.forEach(frame -> frame.setVisible(true));
frameList.forEach(frame -> {
//SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(frame, "Msg", "Title", 0);
frame.setVisible(false);
frame.dispose();
//});
});
}
I'd rather not use the second one because the actual code is being started in a background thread that is notifying a set of listeners, so if I were to use the second one then it would block up the thread and slow down listeners until the user responds (when I could be processing during that time). What about the invokeLater() is breaking me? Is this expected behavior?
NOTE: This is simplified code pulled out of how I'm actually using it, but the core issue still exists (I have multiple JFrames, and if multiple JOptionPane.showMessageDialog()s were put on invokeLater()s for different JFrames then it breaks me. I tested the above code with a new, isolated, JFrame class created in Netbeans and was able to reproduce the error.
EDIT: I can't seem to reproduce the error in Windows, only seems to happen in Linux.
It is most likely invokeLater() which is breaking your code. If you want to thread this action try using a simple thread or
EventQueue.invokeLater(new Runnable()
{
public void run()
{ //Your joptionpane here
}
});`
Instead of invoke later i prefer using a
1) simple thread
2) TimerTask
3) ScheduledExecutorService
Use one of these methods.
This is an example for using timer task
import java.util.Timer;
import java.util.TimerTask;
public class Task2 {
public static void main(String[] args) {
TimerTask task = new TimerTask() {
#Override
public void run() {
// task to run goes here
System.out.println("Hello !!!");
}
};
Timer timer = new Timer();
long delay = 0;
long intevalPeriod = 1 * 1000;
// schedules the task to be run in an interval
timer.scheduleAtFixedRate(task, delay,
intevalPeriod);
} // end of main
}
If you are not satisfied you can use invoke later.
But remember never use invoke and wait its a bad idea
Here is my approach, as I understand the problem is on the locked windows, which are waiting for an event to finish, in swing the events are related with the AWT-EventQueue.
Here is a little explanation about: https://stackoverflow.com/a/22534931/1670134
So, in order to get your window working I used the Future type:
From the java doc:
A Future represents the result of an asynchronous computation. Methods
are provided to check if the computation is complete, to wait for its
completion, and to retrieve the result of the computation. The result
can only be retrieved using method get when the computation has
completed, blocking if necessary until it is ready.
package com.stackoverflow.future;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import com.stackoverflow.frame.MyFrame;
public class MySwingWorker extends SwingWorker<Void, Void>{
#Override
protected Void doInBackground() throws Exception {
final ExecutorService service = Executors.newFixedThreadPool(1);
List<Future<JFrame>> frameList = new ArrayList<Future<JFrame>>();
frameList.add(service.submit(new SwingLoader(new MyFrame())));
frameList.add(service.submit(new SwingLoader(new MyFrame())));
try {
service.shutdown();
service.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException ex) {
ex.printStackTrace();
} finally {
service.shutdownNow();
}
return null;
}
public static void main(String[] args) {
MySwingWorker mySwingWorker = new MySwingWorker();
SwingUtilities.invokeLater(mySwingWorker);
}
}
The loader:
package com.stackoverflow.future;
import java.util.concurrent.Callable;
import javax.swing.JFrame;
public class SwingLoader implements Callable<JFrame>{
private JFrame frame;
public SwingLoader(JFrame frame){
this.frame = frame;
}
#Override
public JFrame call() throws Exception {
frame.setVisible(true);
return frame;
}
}
NOTE: This code is just a proto, in order to provide you with ideas and it must be modified and cleaned in order to achieve the desired results.
Here you are a link with a couple of explanations of each type:
http://winterbe.com/posts/2015/04/07/java8-concurrency-tutorial-thread-executor-examples/

JFileChooser.showSaveDialog() not showing up

I'm using the Eclipse IDE, and I'm trying to call showSaveDialog(null) from another method which is in turn called from main, but nothing happens when I call it.
Main:
public static void main(String[] args) {
Main main = new Main();
main.pt = main.new PlayThread();
main.generateSound(getSamples(2), 500);
main.play(main.sound, 1);
if(new Scanner(System.in).nextLine().equals("save")){
System.out.println(main.save());
}
}
All the code before calling main.save() works fine, and main.save() starts like this:
public boolean save(){
System.out.println("Calling save");
try{
String saveName = getSaveTo("C:/");
System.out.println("I'm here now");
while getSaveTo() looks like:
public String getSaveTo(String def){
JFileChooser chooser = new JFileChooser();
System.out.println("Save called");
//chooser.setCurrentDirectory(new File(def));
int resp = chooser.showSaveDialog(null);
if(resp == JFileChooser.APPROVE_OPTION){
return chooser.getSelectedFile() + ".wav";
}else return null;
}
After it executes the first few functions in main, I type in 'save', and the console prints:
Calling save
Save called
But the dialog never opens, and it never says "I'm here now." Why's that? Also, when I typed
new JFileChooser().showSaveDialog(null)
That shows up fine, but in my save() method, it won't. Any ideas why?
EDIT:
Here's a condensed program which has the same problem:
package com.funguscow;
import java.util.Scanner;
import javax.swing.JFileChooser;
import com.funguscow.Main.PlayThread;
public class Test {
public static void main(String[] args) {
Test main = new Test();
Scanner scan = new Scanner(System.in);
boolean should = scan.nextLine().equals("save");
scan.close();
if(should){
System.out.println(main.save());
}
}
public String getSaveTo(String def){
JFileChooser chooser = new JFileChooser();
System.out.println("Save called");
//chooser.setCurrentDirectory(new File(def));
int resp = chooser.showSaveDialog(null);
System.out.println("Save called");
if(resp == JFileChooser.APPROVE_OPTION){
return chooser.getSelectedFile() + ".wav";
}else return null;
}
public boolean save(){
System.out.println("Calling save");
try{
String saveName = getSaveTo("C:/");
System.out.println("I'm here now");
if(saveName == null)return false;
}catch(Exception e){}
return false;
}
}
You can run this yourself if you'd like to examine it more.
This MCVE may be enough to reproduce the problem, and it seems that the Scanner that is initialized with System.in is interfering with the JFileChooser's ability to display an open file dialog, even when care is taken so that the file chooser is run on the Swing event thread:
import java.util.Scanner;
import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;
public class Test3 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.print("Enter something and press Enter: ");
scan.nextLine();
scan.close();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFileChooser fileChooser = new JFileChooser();
int result = fileChooser.showOpenDialog(null);
}
});
// JFileChooser fileChooser = new JFileChooser();
// int result = fileChooser.showOpenDialog(null);
}
}
On Windows, Scanner interferes w/ JFileChooser -- why?
tl;dr strictly speaking, it's the user that interferes with JFileChooser by using a Scanner expecting input from system console (System.in). Either way, it's just about the windowing focus and Java's Dialog modality.
Explanation
The bug happens because the dialog appears in the background because requiring nextLine() on the Scanner on System.in essentially forces the user to switch the focus to the console. The application loses the focus, so the Dialog appears in the background. The code doesn't "hang" by itself, it just waits until the user selects a file or does any other dialog option - until he does, it does nothing. If there's some kind of OS issue blocking the background window from displaying/working properly (e.g. some "always in foreground" window obstructing it) - well, then your app is hanging around, waiting for input event that's not likely to happen due to the fact nobody can use the input dialog itself.
For anybody affected - the Dialog is there. I've tested this code on Java 8 on Windows XP, Java 8 on Windows 7 and some other configurations - in all of the cases the Dialog was created, possibly hidden in the background of the desktop due to focus loss. It's not shown in the taskbar, but it does show in the Alt+Tab list. Try running it outside of IDE, they tend to do strange things with app focus when console input is required (due to "real" console getting redirected to IDE output window etc). Feeding it data through the < pipe instead of using "real" console will propably prevent this behaviour from happening too.
As far as my imagination goes, workarounds would require either forcing the focus to the app's Frame or creating an on-the-fly always-on-top Frame to keep the focus where it should be (on the Dialog). Since the Dialog of JFileChooser (or any other similar Dialog to speak of) is itself a fire-and-forget object, it's kind of difficult to force it to foreground without specifying a parent. Therefore, I think that the easiest way to go, for parent-less Dialogs is to just use code like:
Exemplary solution
Scanner scan = new Scanner( System.in );
System.out.print( "Enter something and press Enter: " );
scan.nextLine();
scan.close();
SwingUtilities.invokeLater( new Runnable() {
public void run() {
JFrame jf = new JFrame( "Dialog" ); // added
jf.setAlwaysOnTop( true ); // added
JFileChooser fileChooser = new JFileChooser();
int result = fileChooser.showOpenDialog( jf ); // changed
//System.out.print( "result: " + result );
jf.dispose(); // added
}
} );
or, if you prefer to interfere with the Dialog itself, by subclassing it and applying the aforementioned setAlwaysOnTop(true) in the createDialog() call (note that createDialog() has protected access in JFileChooser, making it impossible to change the behaviour without extending the class, similarly to all the Dialog related stuff there), e.g.
int result = new JFileChooser() {
#Override
protected JDialog createDialog( Component parent ) throws HeadlessException {
JDialog jDialog = super.createDialog( parent );
jDialog.setAlwaysOnTop( true );
return jDialog;
}
}.showOpenDialog( null );
or even by creating a regular utility class extending JFileChooser doing just that.
NB switching to/fro EDT does nothing here, as this is not threading related by itself (though the multithreading of UI is the source of the problem, kind of).
I believe the answer by vaxquis to be quite correct in that the chooser is being shown, just behind the current window as it retains focus. It's all to do with Dialog modality and which windows take precedence over others on the various OS's (possibly in combination with some IDE's).
I have found the following workaround - keeping all your other code exactly the same, extend JFileChooser and then use MyFileChooser instead of JFileChooser in the getSaveTo() method. I've tested on my setup (Windows 8, Java 7). YMMV
class MyFileChooser extends JFileChooser {
protected JDialog createDialog(Component parent) throws HeadlessException {
JDialog dialog = super.createDialog(parent);
dialog.setAlwaysOnTop(true);
return dialog;
}
}
This allows you to use the original construction (.openSaveDialog(null)) and has the nice added advantage that you can set up other features of the dialog box in your overridden createDialog() method should you wish.
EDIT
I see vaxquis has appended this method to their answer now too, so that answer is, I would say, canonical.

Java: How to wait for fileChanged to execute?

I'd like to have my thread (the main/EDT) wait until changes to a file occur and then wait. DefaultFileMonitor extends Runnable and hence runs in a thread of its own. Here is a SSCE:
import java.io.File;
import org.apache.commons.vfs.*;
import org.apache.commons.vfs.impl.DefaultFileMonitor;
public class FileChangeListener implements FileListener {
DefaultFileMonitor fm;
public final static File logFile = new File("t.txt");
public void startListening() throws FileSystemException {
final FileSystemManager fsManager = VFS.getManager();
final FileObject listendir = fsManager.toFileObject(logFile);
fm = new DefaultFileMonitor(this);
fm.addFile(listendir);
fm.start();
}
#Override
public void fileCreated(FileChangeEvent fce) throws Exception {
fileChanged(fce);
}
#Override
public void fileDeleted(FileChangeEvent fce) throws Exception {
//hmm..why deleted?
}
#Override
public void fileChanged(FileChangeEvent fce) throws Exception {
System.out.println("fileChanged executed");
}
}
The main:
import java.io.PrintWriter;
public class App {
public static void main(String[] args) {
FileChangeListener fcl = new FileChangeListener();
try {
fcl.startListening();
final PrintWriter printWriter = new PrintWriter(FileChangeListener.logFile);
printWriter.println("Hello Threads!");
printWriter.close();
//EXECUTE THE FOLLOWING ONLY AFTER fileChanged
System.out.println("Mission complete.");
} catch (Exception ex) {
}
}
}
Append the following to App.main(..) after printWriter.close():
synchronized (fcl) {
fcl.wait();
}
//EXECUTE THE FOLLOWING ONLY AFTER fileChanged
System.out.println("Mission complete.");
and append the following to FileChangeListener.fileChanged(..) after System.out.println("fileChanged executed"):
synchronized (this) {
this.notifyAll();
}
You could communicate between teh two using "Conditions" : http://download.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Condition.html
Basically, create a new "shared" Condition (say fileChanged). Now, whenever the file changes (in fileChanged() trigger this condition (fileChanged.signal()). In your main code, wait for this condition to occur (fileChanged.await()).
Hope you get the idea.
For making the condition accessible to multiple code unit, here is what I can think (decreasing order of preference) :
Assuming you are going to need as many conditions as many files you listen to, create a factory method getCondition(String file path/name/attribute) which will return the Condition object based on the file (its path or name or other attributes). Use this factory method to get the condition in all cases. The factory should internally create new Condition() instances for each new file to be listened to AND must throw away older instances as the processing of the files is complete (so probably you should add a destroy/deleteCondition(String file) method as well.)
Store the condition as a public field in the listener class (kind of hack if you have the listener instance available).
Store the condition as a public static field in the listener class (kind of hack if you have only one listener instance throughout).
Why? FileChangeListener is a callback: it is executed when the event occurs. In this specific case you've just closed the file so you alreayd know that the mission is complete on that file, so just proceed to the next step. I don't see why you need a FileChangeListener at all here.

Categories