0

I'm using a JFrame and I wanted to display an image and pause the code until the user presses ANY key. After that key being pressed the image would close and the code would continue running.

What I did:

  • Created a flag

    final boolean[] flag = {true};

  • Added a addKeyListener to the JFrame object that would change the flag

    frame.addKeyListener(new KeyListener() {
    
    @Override
    public void keyTyped(KeyEvent e) {
    }
    
    @Override
    public void keyReleased(KeyEvent e) {
    }
    
    @Override
    public void keyPressed(KeyEvent e) {
        frame.setVisible(false);
        frame.dispose();
        flag[0] = false;
    }
    });
    
  • Wait loop until flagged

    while (flag[0]){
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    

This is working, but I understand that it is a bit resourceful.

Is there any other way of making the wait loop? Is there any listener of the listener?

2nd try, using CountDownLatch:

  • Set the latch

        final CountDownLatch latch = new CountDownLatch(1);
    
  • CountDown for (JFrame frame : framesList) {

        frame.addKeyListener(new KeyListener() {
    
            @Override
            public void keyTyped(KeyEvent e) {
            }
    
            @Override
            public void keyReleased(KeyEvent e) {
            }
    
            @Override
            public void keyPressed(KeyEvent e) {
                    frame.setVisible(false);
                    frame.dispose();
                    latch.countDown();
            }
        });
    
  • Wait

        latch.await();
    
14
  • I don't understand this part from your question: only then I would like to continue the code.. Could you please explain what you mean by this? Is your application terminating because the JFrame is gone? Commented May 25, 2017 at 9:48
  • @OvidiuDolha I added an example Commented May 25, 2017 at 9:51
  • Thanks but I still don't get it... can you post the code where you show /change the image? and where exactly is your while loop placed now? Commented May 25, 2017 at 9:54
  • @OvidiuDolha The full code is here. And I'm calling it from another function with Mat img = Imgcodecs.imread( args[0], Imgcodecs.IMREAD_COLOR ); HighGui.imshow("Colour", img); HighGui.waitKey(0); // Show image in GrayScale img = Imgcodecs.imread( args[0], Imgcodecs.IMREAD_GRAYSCALE ); HighGui.imshow("Gray", img); HighGui.waitKey(0); Commented May 25, 2017 at 10:01
  • 1
    @JoãoCartucho Latch approach looks fine to me, but indeed simple modal dialog would do the same thing as eg JOptionPane.showMessageDialog blocks untill dialog is disposed. If you display the image in dialog, you will achieve both of your goals. Invoking code will continue execution after dialog with image will close. Commented May 25, 2017 at 12:47

1 Answer 1

2

So, you want to display an image and have the execution stop until the window is closed. This just screams modal dialog to me. A modal dialog will stop the code execution from where it is made visible, it will do it in such away so as not to block the Event Dispatching Thread and make your entire problem come to a screaming halt and hang the program. See How to use dialogs for more details...

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Image;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    BufferedImage img = ImageIO.read(...);
                    ImageShower.show(null, img);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        });
    }

    public static class ImageShower extends JPanel {

        private JLabel label = new JLabel();

        public ImageShower() {
            setLayout(new BorderLayout());
            add(label);

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "close");
            am.put("close", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    Window window = SwingUtilities.windowForComponent(ImageShower.this);
                    if (window != null) {
                        window.dispose();
                    }
                }
            });
        }

        public void setImage(Image img) {
            label.setIcon(new ImageIcon(img));
        }

        public static void show(Component owner, Image img) {
            Window parent = null;
            if (owner != null) {
                parent = SwingUtilities.windowForComponent(owner);
            }

            JButton close = new JButton("Close");
            close.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    JButton btn = (JButton) e.getSource();
                    Window window = SwingUtilities.windowForComponent(btn);
                    if (window != null) {
                        window.dispose();
                    }
                }
            });

            JDialog dialog = new JDialog(parent, Dialog.ModalityType.APPLICATION_MODAL);
            ImageShower shower = new ImageShower();
            shower.setImage(img);
            dialog.add(shower);
            dialog.add(close, BorderLayout.SOUTH);
            dialog.getRootPane().setDefaultButton(close);
            dialog.pack();
            dialog.setLocationRelativeTo(owner);
            dialog.setVisible(true);
        }

    }

}

"But wait, may images are large and take time to load and I don't want to freeze the UI while the load"...

Okay, for that, I'd look towards using a SwingWorker, which can load the image in the background but which provides simple methods for ensuring the the image is displayed within the context of the EDT properly...

public class ImageLoadAndShow extends SwingWorker<Void, Image> {

    @Override
    protected Void doInBackground() throws Exception {
        BufferedImage img = ImageIO.read(...);
        publish(img);
        return null;
    }

    @Override
    protected void process(List<Image> chunks) {
        Image img = chunks.get(chunks.size() - 1);
        ImageShower.show(null, img);
    }

}

Not, if the image fails to load, you won't know about it, as the doInBackground method will pass the Exception out of the method. You'd need to use a combination of a PropertyChangeListener and the SwingWorkers get method to trap it, just remember, get is blocking, so calling it inside the context of the EDT will block until the worker completes

"But I need to carry out other operations when the dialog is closed"

There are a few ways you might be able to achieve this, depending on what it is you want to do, for this example, I've stuck with the SwingWorker, because it was easy to copy and paste the basic structure, but you could use a Runnable wrapped in a Thread

public class ImageLoadShowAndWait extends SwingWorker<Void, Void> {

    @Override
    protected Void doInBackground() throws Exception {
        BufferedImage img = ImageIO.read(...);
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                ImageShower.show(null, img);
            }
        });
        return null;
    }

}

Now, if none of that does what you want...then I'd like to know what it is you're actually doing :P, have a look at Foxtrot which provides an API which allows you to execute code asynchronisly within the EDT without blocking it (entirly), but which will stop the code execution at the point it's called until it completes

The thing is that I wanted it to close the JFrame when ANY key is pressed

KeyListener is going to give you issues, maybe not today, maybe not tomorrow, but it will blow up in your face eventually. The example I've provide binds the Escape key to dispose of the window. It also makes the "Close" button the default button, which provides Space and/or Enter keys as well and a nice visual queue to the user.

If you want to use KeyListener, that's up to you, but your core problem doesn't seem to revolve around it, but the ability to display a window and pause the code execution till it's closed

Sign up to request clarification or add additional context in comments.

5 Comments

It worked great! The reason why I'm doing this is that I'm trying to do the OpenCV HighGui module for the Java API, and if I'm able to do it this will help lots of people.
Code Here, is it ok if I call the Class from a specific method instead of the main? I wanted people just to do HighGui2.imshow("title", imgMat);
Yep, call it from wherever you need it, just make sure you call from within the context of the EDT
So can I erase the main method? If I wanted to show more than one image/dialog at a time would you recommend joining them all together in the same JPanel or keeping separate windows?
@JoãoCartucho Well, you don't need my main method, but you're going t need one eventually. If you wanted to show more then one image at a time, I'd be tempted to use a single window

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.