Question

Basically, I have 2 classes: 1 for the GUI where the JProgressBar is added to the JPanel, and the other is the class that has the process in which the JProgressBar should update.

My problem is the progress bar does not update periodically and it only updates after the processing is totally done. I've tried using SwingUtilities.invokeLater(), but somehow it turns out the processing task is done later, and all the done message have been printed first before the processing is even executed, so I am trying invokeAndWait().

I've searched around for answers, but most of them just give example of JProgressBar where the GUI and the updating is done in the same class, whereas for my case, the 2 are separated. I also considered SwingWorker, but not sure where and how should I implement it, as it will change the structure of my program.

Below is my code. Sorry for it being messy and the naming not very coherent, I am trying lots of stuff to make the progress bar update, and need some time to reorganize later (but essentially the code is working fine).

The GUI class (see the method getYesButton() specifically):

public class TextureRevertPanel extends JPanel {

private TextureEditor te;
private TextureEditorGUI parent;
private JButton yesButton;
private JButton noButton;
private String imgtype;
private String path;
private JProgressBar pbar;
static final int MY_MINIMUM = 0;
static final int MY_MAXIMUM = 100;

public TextureRevertPanel(TextureEditor te, TextureEditorGUI parent, String imgtype, String path) {
    super();
    this.parent = parent;
    setPreferredSize(new Dimension(800, 400));

    setLayout(new GridBagLayout());

    GridBagConstraints c = new GridBagConstraints();
    c.insets = new Insets(5, 5, 5, 5);
    c.anchor = GridBagConstraints.WEST;

    c.gridx = 0;
    c.gridy = 0;
    c.gridwidth = 2;
    add(new JLabel("Energy saving mode is turned ON, do you want to turn it off?"), c);

    c.gridx = 0;
    c.gridy = 1;
    c.gridwidth = 2;
    JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
    bottomPanel.add(getYesButton());
    bottomPanel.add(getNoButton());
    add(bottomPanel, c);

    c.gridx = 0;
    c.gridy = 4;
    c.gridwidth = 1;
    add(new JLabel("Progress"), c);

    c.gridx = 1;
    c.gridy = 4;
    c.gridwidth = 1;
    pbar = new JProgressBar();
    pbar.setMinimum(MY_MINIMUM);
    pbar.setMaximum(MY_MAXIMUM);
    // add to JPanel
    add(pbar, c);

    this.te = te;
    this.path = path;
    this.imgtype = imgtype;

}

private JButton getYesButton() {
    if (yesButton == null) {
        yesButton = new JButton("Yes");
        yesButton.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseReleased(MouseEvent e) {
                if (MouseEvent.BUTTON1 == e.getButton()) {
                    boolean b = te.revertTextures("Y", path, imgtype, pbar);
                    if (b) {
                        /*JOptionPane.showMessageDialog(
                                parent.getMainFrame(),
                                "Textures in " + path + " have been reverted back",
                                "Notification", JOptionPane.INFORMATION_MESSAGE);*/
                        //parent.showMainPane();
                    }
                }
            }
        });
    }
    return yesButton;
}

private JButton getNoButton() {
    if (noButton == null) {
        noButton = new JButton("No");
        noButton.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseReleased(MouseEvent e) {
                if (MouseEvent.BUTTON1 == e.getButton()) {
                    parent.showMainPane();
                }
            }
        });
    }
    return noButton;
}
}

By pressing Yes, the class will invoke the method revertTextures() of TextureEditor class. The JProgressBar pbar is passed to the method as a parameter.

Then, the revertTextures method in TextureEditor:

public boolean revertTextures(String input, String texturepath, String imgtype,     JProgressBar pbar) {
    System.out.println("Energy saving mode is on, do you want to turn it off?(Y/N)");
    if (input.equalsIgnoreCase("Y")) {
        System.out.println("Turning off energy saving mode...");
        //copy all the png in the backup folder to texture folder
final String tpath = texturepath;
final String imagetype = imgtype;
this.pbar = pbar;
final Runnable doHelloWorld = new Runnable() {

            public void run() {
                System.out.println("Hello World on " + Thread.currentThread());
                restorebackup(tpath, imagetype);
            }
        };

        Thread appThread = new Thread() {

            @Override
            public void run() {
                try {
                    SwingUtilities.invokeAndWait(doHelloWorld);
                    String text = "OFF";
                    try {
                        File file = new File(tpath + "backup\\indicator.txt");
                        BufferedWriter output = new BufferedWriter(new  FileWriter(file));
                        output.write(text);
                        output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Energy saving mode has been turned off.");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("Finished on " + Thread.currentThread());
            }
        };
        appThread.start();

Then inside the thread, the real processing method restorebackup() is called. This is where the JProgressBar should be updated:

public void restorebackup(String tpath, String imgtype) {
    File backuppath = new File(tpath + "backup\\");
    final File[] files = backuppath.listFiles();
    final String imagetype = "." + imgtype;
    final String texpath = tpath;
    final String imagtype = imgtype;

    for (int i = 0; i < files.length; i++) {
        final int ii = i;
        System.out.println(files[i].getName());
        File texturepath;
        BufferedImage image;
        try {
            System.out.println(files[ii]);
            if (files[ii].getName().contains(imagetype)) {
                texturepath = new File(texpath + files[ii].getName());
                image = ImageIO.read(files[ii]);
                ImageIO.write(image, imagtype, texturepath);
                double proportion = (ii / (double) (files.length - 1)); //count out the indicator file
                System.out.println((int) (proportion * 100) + "%");
                pbar.setValue((int) (proportion * 100));
            }
        } catch (IOException e) {
            System.out.println("Could not open texture files in backup folder");
        }
    }
    System.out.println("Done");
}
Was it helpful?

Solution 2

OK, finally I decided to just use a SwingWorker class inside my TextureEditor class. This solved my problem. However, I need to copy over the restorebackup() operations to doInBackGround().

private static class MySwingWorker extends SwingWorker<String, Double> {

    private final JProgressBar fProgressBar;
    private final String tpath;
    private final String imagetype;
    private final TextureEditorGUI parent;

    private MySwingWorker(JProgressBar aProgressBar, String tpath, String imagetype, TextureEditorGUI parent) {
        fProgressBar = aProgressBar;
        this.tpath = tpath;
        this.imagetype = imagetype;
        this.parent = parent;
    }

    @Override
    protected String doInBackground() throws Exception {
        File backuppath = new File(tpath + "backup\\");
        File texturepath;
        final File[] files = backuppath.listFiles();
        BufferedImage image;
        final String texpath = tpath;
        for (int i = 0; i < files.length; i++) {
            final int ii = i;
            try {
                System.out.println(files[i].getName());
                System.out.println(files[ii]);
                if (files[ii].getName().contains("." + imagetype)) {
                    texturepath = new File(texpath + files[ii].getName());
                    image = ImageIO.read(files[ii]);
                    ImageIO.write(image, imagetype, texturepath);
                    double proportion = (ii / (double) (files.length - 1)); //count out the indicator file
                    publish(proportion);
                    System.out.println((int) (proportion * 100) + "%");
                }
            } catch (IOException e) {
                System.out.println("Could not open texture files in backup folder");
            }
        }
        return "Finished";
    }

    @Override
    protected void process(List<Double> aDoubles) {
        //update the percentage of the progress bar that is done
        int amount = fProgressBar.getMaximum() - fProgressBar.getMinimum();
        fProgressBar.setValue((int) (fProgressBar.getMinimum() + (amount * aDoubles.get(aDoubles.size() - 1))));
    }

    @Override
    protected void done() {
        try {
            JOptionPane.showMessageDialog(
                    parent.getMainFrame(),
                    "Textures in " + tpath + " have been reverted back",
                    "Notification", JOptionPane.INFORMATION_MESSAGE);
            parent.showMainPane();
        } catch (Exception ignore) {
        }
    }
}

However, I have another method in my TextureEditor class that requires progress bar updating as well. Does this mean I need to create another SwingWorker class just for that other method, or a conditioning inside the SwingWorker will do (i.e. pass parameter to SwingWorker, and it will execute different operation for different method, by conditional)?

OTHER TIPS

You're not calling restorebackup() from the background thread. You're calling it from the event dispatch thread, since its call is wrapped inside a call to SwingUtilities.invokeAndWait(). So all the work in this method is done in the UI thread, freezing it and preventing it to update the UI until the method completes.

You must do all this job in the background thread, except the updates to the progress bar, which must be wrapped into a SwingUtilities.invokeLater() call.

Or better, you should read the documentation of SwingWorker and use it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top