1

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?

user3728545
  • 71
  • 1
  • 1
  • 5
  • 2
    Sounds like you need to do some threading. Loop up swing worker. (i believe that is what it is called) – pattmorter Jul 30 '14 at 14:39
  • Use [SwingUtilities.invokeLater()](http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html#invokeLater%28java.lang.Runnable%29) or [EventQueue.invokeLater()](http://docs.oracle.com/javase/7/docs/api/java/awt/EventQueue.html#invokeLater%28java.lang.Runnable%29) to make sure that [EDT](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html) is initialized properly but never call any heavy loaded method inside it. – Braj Jul 30 '14 at 14:40
  • I am familiar with this and was going to give it a try, but I didn't think it would solve the problem of it doing this long set up of the frame. Wouldn't threading just mean the GUI wouldn't lock up? I would still have the problem of the page being visible while it is loading (and causing issues with the system>) – user3728545 Jul 30 '14 at 14:47

2 Answers2

4

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();
        });
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
1

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.

mbw
  • 366
  • 3
  • 12
  • I will give it a shot, I have some experience with swing worker so it should be straight forward. On another note, I am creating my colored boxes by using a gridlayout with textboxes that I simply set to a black background. Is there a better way to get simple colored boxes? I tried labels but the system can't handle that many(30,000ish). – user3728545 Jul 30 '14 at 17:44