JFileChooser.showSaveDialog() not showing up
Asked Answered
P

2

7

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);
   }
}
Pushkin answered 25/1, 2015 at 21:42 Comment(15)
Can you condense your problem into a single small compilable and runnable program? Please help us to test your issue ourselves on our own machines to better be able to help you.Selima
Also, have you isolated your error? In other words, when you use a debugger, where do you see that the error coming from?Selima
It just seems to kind of stop at chooser.showSaveDialog(null);Pushkin
I hope you don't mind but I've tried to minimize your code further to see where the problem is, and I've posted my minimal attempt as an edit to your question.Selima
@HovercraftFullOfEels your example work fine. Though the dialog stays on the background.Carvajal
@HovercraftFullOfEels So all you changed was that you put the GUI functions in a new Thread?Pushkin
@Aqua: It doesn't work for me. My program just freezes. I'm running on Windows 7 using Eclipse Luna 4.4.0, Java compiler compliance level 1.7.Selima
@HovercraftFullOfEels same config here with Luna 4.4.1 :) The dialog is not modal and stays behind all the windows. But if I comment out the Scanner then the dialog appears correctly. So your guess is correct.Carvajal
Well I've at least gotten mine to work by doing away with Scanner and changing the confirmation to something using a JOptionPane instead. Guess I got really lucky, too, as it actually saves a playable audio file like I intended it to.Pushkin
OP, my code is a simplified version that sort of resembles your code in that a Scanner is used with System.in, then closed, then a JFileChooser is created and displayed, but the latter is set up to run on the Swing Event Dispatch Thread.Selima
I tried running your last example on MacOS and it seems to work fine, although scan.close worries me, as you shouldn't be closing the System.in stream, but it worked with and without it. What OS are you running it on? Have y tried to run it out side of the EDT?Corrie
Sorry that should be "outside the IDE"Corrie
Also, your second example works fine as well for meCorrie
}catch(Exception e){} Never swallow exceptions. What happens when you add e.printStackTrace(); inside your catch block?Selfsatisfaction
@Pushkin is the information provided by me & the others helpful enough for you to select an accepted answer?Arne
A
5

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).

Arne answered 28/1, 2015 at 19:43 Comment(2)
I don't recommend using setAlwaysOnTop, it's pretty invasive to the desktop because it obstructs all other applications on the system.Eirene
@Eirene I agree that it is invasive, but the thing is: if you can't force a blocking window to be always-on-top, it's quite possible you won't see it at all, and have a seemingly "hanging" app :D - the whole point here is to obstruct other applications on the system so that the user knows that his action is requiredLeeway
P
2

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.

Photoperiod answered 28/1, 2015 at 21:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.