Stopping a window from displaying till it is fully drawn?

I am working on a Java program that takes in a large amount of files (3000 max) with an associated array of 1/0's. Currently I have a visualization of the array where there is a grid where each box is filled black for 1 or white for 0. When drawn it runs well but takes around a minute to fully load (and potentially locks the computer up in the meantime.) Is there a way I can: 1, not display the window till it is done

(i.e JFrame create,

//draw window

frame.setVisible(true))

and 2, track the progress of the process so that I can use a progress bar with it?

edit: Can I run a thread to draw it and then simply make a while loop to only display it once the thread is completed?


In the example below, a SwingWorker sets pixels in a BufferedImage based on the data read from a random file. Note that Thread.sleep() is used to simulate latency; it is otherwise not required. You can add a JProgressBar as shown here.

Is there a better way to get simple colored boxes?

Yes. In the example below, each pixel represents one cell. For larger boxes, return a multiple of the image size, e.g.

@Override
public Dimension getPreferredSize() {
    return new Dimension(2 * N, 2 * N);
}

image

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;

/**
 * @see https://stackoverflow.com/a/25043676/230513
 */
public class WorkerTest {

    private static final int N = 256;
    private final BooleanPanel panel = new BooleanPanel();

    private class BooleanPanel extends JPanel {

        private BufferedImage image;

        public void setImage(BufferedImage bi) {
            this.image = bi;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.drawImage(image, 0, 0, getWidth(), getHeight(), null);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(N, N);
        }
    }

    private class BufferedImageWorker extends SwingWorker<BufferedImage, BufferedImage> {

        @Override
        protected BufferedImage doInBackground() throws Exception {
            BufferedImage image = new BufferedImage(N, N, BufferedImage.TYPE_INT_ARGB);
            try (DataInputStream dis = new DataInputStream(
                    new BufferedInputStream(new FileInputStream("/dev/random")))) {
                for (int row = 0; row < N; row++) {
                    for (int col = 0; col < N; col++) {
                        image.setRGB(col, row, dis.readByte() < 0 ? 0xffffffff : 0xff000000);
                    }
                    Thread.sleep(40); // ~25 Hz
                    publish(image);
                }
                return image;
            }
        }

        @Override
        protected void process(List<BufferedImage> list) {
            for (BufferedImage bi : list) {
                panel.setImage(bi);
                panel.repaint();
            }
        }
    }

    private void display() {
        JFrame f = new JFrame("WorkerTest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(panel);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
        new BufferedImageWorker().execute();
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            new WorkerTest().display();
        });
    }
}

I would definitely use a SwingWorker in this case. Basically, maybe something along these lines (I'm not sure what type of object your 'visualization' is, so for simplicity, I'll just say it's an Image). You can add this at the bottom of your class. You'll obviously have to edit it to make it work for you.

protected class DrawGridTask extends SwingWorker<Image, Object> {
    ObjectToPutImageOn imageObject;

    public DrawGridTask(ObjectToPutImageOn obj) {
        this.imageObject = obj;
    }
    protected Image doInBackground() {
        // generate your Image or graphic or whatever here
        return Image;
    }
    protected void done() {
        imageObject.drawThisCompletedImage(get());
    }
}

To call this method, you would run (new DrawGridTask(objectToPutImageOn)).execute();

All the code in doInBackground() will run on it's own worker thread. Done() runs on the event dispatch thread, and gets the reference doInBackground() returns when it calls get().

There is more information here, including how to do progress updates at: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html

Since I mentioned Images, if you do work with them, you might also want to take a look at the MediaTracker class, this can be very useful for blocking until an image is ready.