9

Is there a way how to use a dialog in Swing which prohibits any gui activity under it but at the same time DOES NOT stop execution on the thread where it was set to visible?

mKorbel
  • 109,525
  • 20
  • 134
  • 319
ps-aux
  • 11,627
  • 25
  • 81
  • 128
  • 1
    No. A modal dialog takes control over the Event Queue and filters the dispatched events. What is it you are trying to achieve. A modal dialog should only be used within the context of the EDT and never called from any other thread, so it should only be blocking the EDT – MadProgrammer Jun 03 '14 at 11:03
  • It is trivial to get an animation going while a blocking modal dialog is open above it, and it would seem that their would be little else that requires activity while a dialog is open, so I fail to see the point of your inquiry. Of course, if an action in the dialog affects the blocked GUI, that change will go ahead without problem. – Andrew Thompson Jun 03 '14 at 11:09
  • [ModalityTypes](http://docs.oracle.com/javase/tutorial/uiswing/misc/modality.html) – mKorbel Jun 03 '14 at 11:22

5 Answers5

7

Yes it can be done .

dlg.setModal(false);

or

dlg.setModalityType(Dialog.ModalityType.MODELESS);

where dlg is instance of your JDialog .

Mr Coder
  • 8,169
  • 5
  • 45
  • 74
2

The basic idea of a JDialog IS to block the underlying thread until the user reacts to it. If you need to run something on the UI thread which should not be interrupted, consider using an additional worker thread for it. This way, the UI will be blocked by the JDialog, but the underlying process won't.

Zahlii
  • 818
  • 1
  • 7
  • 17
2

Yes, there is a little trick to make it work. We simply deactivate modality and manually disable the JFrame we want to make unclickable.

private final static JDialog dialog; static {
        JOptionPane pane = new JOptionPane();
        pane.setOptions(new Object[]{}); // Removes all buttons
        dialog = pane.createDialog(frame, ""); // Create dialog with pane
        dialog.setModal(false); // IMPORTANT! Now the thread isn't blocked
        dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
}

Now you can use it like this:

dialog.setVisible(true);
frame.setEnabled(false);
// Logic
dialog.setVisible(false);
frame.setEnabled(true);
trallnag
  • 2,041
  • 1
  • 17
  • 33
  • 1
    Where have you been so far, my hero? I lost my whole day with multi-threads, transparent jframes and glasspanels and you solved it from 2 lines of code – Botea Florin Aug 08 '20 at 18:29
1

Technically, no. Like MadProgrammer wrote in a comment, you are never expected to access any Swing component off-EDT, JDialogs included, therefore the situation you hinted at in the question can never happen (there can never be any thread other than EDT that sets a dialog visible).

You could make it seem like it is, though. That's what SwingUtilities.invokeLater(Runnable) is for (doc).

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class BlockingDialogDemo extends JFrame {

    private Timer timer;
    private JDialog blocker;

    public BlockingDialogDemo() {
        setTitle("Blocking Dialog");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(300, 200);
        setLocationRelativeTo(null);

        blocker = new JDialog(this, true);
        blocker.setLayout(new FlowLayout());
        blocker.setUndecorated(true);
        blocker.getRootPane().setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.black));
        blocker.add(new JLabel("I'm blocking EDT!"));    
        JProgressBar progress = new JProgressBar();
        progress.setIndeterminate(true);
        blocker.add(progress);
        blocker.pack();

        timer = new Timer(3000, new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                doSomeWork();
            }
        });
        timer.setRepeats(false);
        timer.start();
    }

    private void doSomeWork() {
        // this executes on-EDT
        Runnable runnable = new Runnable() {

            public void run() {
                // this executes off-EDT - never ever access Swing components here
                showBlocker();
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException ex) {
                    System.out.println("Ummm.. I was sleeping here!");
                } finally {                
                    hideBlocker();
                }
            }
        };
        new Thread(runnable).start();
    }

    private void showBlocker() {
        // this executes off-EDT                
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                // this executes on-EDT
                blocker.setLocationRelativeTo(BlockingDialogDemo.this);
                blocker.setVisible(true);
            }
        });
    }

    private void hideBlocker() {
        // this executes off-EDT
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                // this executes on-EDT
                blocker.setVisible(false);
                timer.restart();
            }
        });
    }

    public static void main(String[] args) {
        // this is called off-EDT
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                // this is called on-EDT
                new BlockingDialogDemo().setVisible(true);
            }
        });
    }

}
predi
  • 5,528
  • 32
  • 60
-1

This works for me... sometimes:

public class NonBlockingModalDialogDemo extends JFrame{

    JButton btnDoIt;

    public NonBlockingModalDialogDemo() {
        setTitle("NonBlockingModalDialog Demo");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(300,300);
        setLayout(new FlowLayout());

        btnDoIt = new JButton("Non-Blocking Notify");
        btnDoIt.addActionListener( new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent arg0) {
                JDialog asyncDialog = createNonBlockingModalDialog("Please wait while we do some work", "Please wait");

                doWork(50);
                //Once your done, just dispose the dialog to allow access to GUI
                asyncDialog.dispose();
            }

        });

        this.add(btnDoIt);
    }

    private JDialog createNonBlockingModalDialog(String message, String title)
    {
        final JDialog dialog = new JDialog();
        dialog.setLayout(new FlowLayout());
        dialog.add(new JLabel(message));
        dialog.setTitle(title);
        dialog.setModal(true);
        dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
        dialog.setAlwaysOnTop(true);
        dialog.pack();

        Runnable dialogDisplayThread = new Runnable() {
            public void run() {
                dialog.setVisible(true);
            }};

        new Thread(dialogDisplayThread).start();

        //Need to wait until dialog is fully visible and then paint it
        //or else it doesn't show up right
        while(!dialog.isVisible()){/*Busy wait*/}
        dialog.paint(dialog.getGraphics());

        return dialog;
    }

    private void doWork(int amount) {
        for(int i = 0; i < amount; i++)
        {
            System.out.println("Doing work step number " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {}
        }

        System.out.println("done");
    }

    public static void main(String[] args) {
        new NonBlockingModalDialogDemo().setVisible(true);
    }
}

I don't really like that it has a busy wait in it to check if the Dialog is visible yet, but so far I haven't found a way around it. At any rate, the busy wait should not take very long at all, so it really shouldn't matter.

Edit: I did something very similar to this and for some reason, on some machines, sometimes, it just blocks forever without even showing the dialog.

I haven't figured out the root cause, but this leads me to conclude that all the people who say "never modify the GUI outside of the Event Dispatch Thread" may be on to something.

Perhaps rather than trying to continue the work you need to do on the EDT, maybe you should try something like this: https://stackoverflow.com/a/4413563/2423283 Which uses SwingWorker to spawn a new thread and then allows you to update the GUI components when you are done.

Community
  • 1
  • 1
NateW
  • 908
  • 1
  • 8
  • 28