Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java wait for component to be painted

I'm trying to create a program in Java that would display set of images one after another adjusting the size of the frame for each one. I'm extending JPanel to display an image like this:

public class ImagePanel extends JPanel{

String filename;
Image image;
boolean loaded = false;

ImagePanel(){}

ImagePanel(String filename){
    loadImage(filename);
}

public void paintComponent(Graphics g){
    super.paintComponent(g);
    if(image != null && loaded){
        g.drawImage(image, 0, 0, this);
    }else{
        g.drawString("Image read error", 10, getHeight() - 10);
    }
}

public void loadImage(String filename){
    loaded = false;         
    ImageIcon icon = new ImageIcon(filename);
    image = icon.getImage();
    int w = image.getWidth(this);
    int h = image.getHeight(this);
    if(w != -1 && w != 0 && h != -1 && h != 0){
        setPreferredSize(new Dimension(w, h));
        loaded = true;
    }else{
        setPreferredSize(new Dimension(300, 300));
    }
}

}

Then in event thread I'm doing main work:

        SwingUtilities.invokeLater(new Runnable(){

        @Override
        public void run(){
            createGUI();
        }
    });

In createGUI() I'm going through the set of images:

        ImagePanel imgPan = new ImagePanel();
    add(imgPan);

    for(File file : files){
        if(file.isFile()){
            System.out.println(file.getAbsolutePath());

            imgPan.loadImage(file.getAbsolutePath());
            pack();

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } 
        }
    }

The problem is that my program does the resizing properly, so images are loaded correctly but it doesn't display anything. If I display only 1 image it works also it works for the last image. I think the problem is that Thread.sleep() is called before image painting is finished.

How can I wait for my ImagePanel to finish painting and start waiting after that? Or is there another way to solve the problem?

Thanks! Leonty

like image 424
Leonti Avatar asked Dec 09 '10 19:12

Leonti


People also ask

What is the use of paint component in Java?

By now you know that the paintComponent method is where all of your painting code should be placed. It is true that this method will be invoked when it is time to paint, but painting actually begins higher up the class hierarchy, with the paint method (defined by java.awt.Component .)

Where does the'painting'code belong in a JComponent?

Painting code belongs in the paintComponent method of any component descended from JComponent. Problem: The stuff I paint doesn't show up. Check whether your component is showing up at all. Solving Common Component Problems should help you with this. Check whether repaint is invoked on your component whenever its appearance needs to be updated.

When to use the paint method?

It is true that this method will be invoked when it is time to paint, but painting actually begins higher up the class heirarchy, with the paint method (defined by java.awt.Component.) This method will be executed by the painting subsystem whenever you component needs to be rendered. Its signature is:

How do I paint a specific area of a component?

If you can paint part of your component, use the getClip or getClipBounds method of Graphics to determine which area you need to paint. The less you paint, the faster it will be. If only part of your component needs to be updated, make paint requests using a version of repaint that specifies the painting region.


2 Answers

All of your code is executing on the Event Dispatch Thread. This effectively causes all user interaction to sleep, since the Event Dispatch Thread is responsible for all user interaction - both input (events) and output (painting).

You need to get your waiting to happen outside of the EDT. You need to know how to execute events on and off the EDT. You do this by creating a new Runnable and then either calling new Thread(runnable) to execute it off the EDT or SwingUtilities.invokeLater(runnable) to make it execute on the EDT. All interaction with Swing components need to take place on the EDT, since Swing objects are not thread safe. All sleeping, waiting, file access, network access, database access, or anything else that may block for indeterminate periods of time must take place off the EDT.

There are many questions on Stack Overflow having to do with the Event Dispatch Thread. I recommend you review these to find more information and code samples to do what you are looking to do in different ways.

like image 134
Erick Robertson Avatar answered Oct 21 '22 10:10

Erick Robertson


Instead of making the thread sleep use a timer and kick off the next image as an event.

like image 21
jzd Avatar answered Oct 21 '22 09:10

jzd