Search code examples
javaswingsleepjdialogjmenubar

Sleep causing frame with invisible content


I have a frame with menubar menu, when I'm selecting a menu item I want the program to show a message and then after few seconds(by sleep function) automatically close this message.

But instead of this I'm getting empty message (jdialog with invisible content):

enter image description here

If remove closing message, it content will appear after sleeping time.

What I need to change to get proper result?

I want to get this:

enter image description here

Full working code:

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.util.concurrent.TimeUnit;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;

public class MessageSleepTest extends JDialog {

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable(){
            public void run(){
                new Frame("frame with menubar");
            }
        });
    }
}

class Message extends JDialog {

    public Message() {
        this.setLayout(new GridLayout(0, 1));
        this.add(new JLabel("Displaying this message for 3 seconds and then closing it...", JLabel.CENTER));

        this.setAlwaysOnTop(true);
        this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }
}

class Frame extends JFrame{

    public Frame(String title){

        super(title);
        setJMenuBar(new MenuBar());
        setPreferredSize(new Dimension(500, 300));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setLocationRelativeTo(null); // center the frame
        setVisible(true);

    }

}

class MenuBar extends JMenuBar implements ActionListener{
    public static JMenuItem itmOpen;

    public MenuBar() {
        JMenu menuFile = new JMenu("File");

        itmOpen = new JMenuItem("Open...");
        itmOpen.addActionListener(this);

        add(menuFile);
        menuFile.add(itmOpen);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        JMenuItem source = (JMenuItem)e.getSource();

        if(source == itmOpen){

            JDialog message = new Message();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            WindowEvent windowClosing = new WindowEvent(message, WindowEvent.WINDOW_CLOSING);
            message.dispatchEvent(windowClosing);

        }
    }
}

Solution

  • You have a threading problem:
    The sleep is performed on AWT's Event Dispatch Thread, which does all the event handling stuff in AWT and Swing. Therefore, when you click on the menu item, it creates the objects for the Message, but gets stuck with the setVisible() method, which in general sends an event to the JDialog to lay out and show itself. This message gets on the queue of the EDT, and after your event handler (the actionPerformed method) finishes, it gets processed. However the sleep is between the two.

    So try something like this:

    @Override
    public void actionPerformed(ActionEvent e) {
       JMenuItem source = (JMenuItem) e.getSource();
       if (source == itmOpen) {
          final JDialog message = new Message();
          new Thread( new Runnable() {
             @Override
             public void run() {
                try {
                   TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException ex) {
                   // Do nothing with it
                }
                WindowEvent windowClosing = new WindowEvent(message, WindowEvent.WINDOW_CLOSING);
                message.dispatchEvent(windowClosing);
             }
          }).start();
       }
    }