Search code examples
javamultithreadingswingjprogressbar

How to do an updateable JProgressBar in an JInternalFrame inside a thread?


This is what i have so far, It's probably totally wrong...

The problem seems to be updating the percentage inside the loop and then calling it outside in the SwingUtilities.invokeLater program.

Also the JInternalFrame sits behind the rest of the program, Perhaps a JDialog would be better used.

Will be here for anymore code needed, Also willing to post entire project on github if you need more information.(If that is allowed)

This is my first project with code so any help would be appreciated!

package twoDMapEditor;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;

import javax.swing.BorderFactory;
import javax.swing.JInternalFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;

public class SaveActionListener implements ActionListener {
    public void actionPerformed(ActionEvent me) {
        final JProgressBar progressBar = new JProgressBar();
        progressBar.setValue(0);
        progressBar.setStringPainted(true);
        progressBar.setBorder(BorderFactory.createTitledBorder("Saving..."));
        final JInternalFrame loadingDialog = new JInternalFrame();
        loadingDialog.add(progressBar);
        loadingDialog.pack();
        loadingDialog.setVisible(true);
        TwoDMapEditor.frame.add(loadingDialog);
        TwoDMapEditor.frame.revalidate();
        TwoDMapEditor.frame.repaint();

        new Thread(new Runnable() {
              public void run() {
                  final int percentage = 0;
                  int i = 0, j;
                  while (i < TwoDMapEditor.size[0]) {
                      j = 0;
                        while(j < TwoDMapEditor.size[1]){
                            if (!TwoDMapEditor.mySql.updateMapPiece(i, j)) {
                                System.out.println("Something went wrong");
                            }
                            //percentage = ((i * TwoDMapEditor.size[1]) + j+1) * 100 / (TwoDMapEditor.size[0] * TwoDMapEditor.size[1]);
                            j++;
                        }
                        i++;
                  }

                  try {
                    SwingUtilities.invokeAndWait(new Runnable() {
                        public void run() {
                            progressBar.setValue(percentage);
                        }
                      });
                } catch (InvocationTargetException | InterruptedException e1) {
                    e1.printStackTrace();
                }

                  try {
                    java.lang.Thread.sleep(100);
                  }
                  catch(Exception e) { }
              }
        }).start();
    }
}

edit: got this now:

package twoDMapEditor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.BorderFactory;
import javax.swing.JInternalFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

public class SaveActionListener implements ActionListener, PropertyChangeListener {
JProgressBar progressBar;
ProgressBar progressBarClass;
JInternalFrame loadingDialog;

SaveActionListener(){
    progressBar = new JProgressBar();
    progressBar.setValue(0);
    progressBar.setStringPainted(true);
    progressBar.setBorder(BorderFactory.createTitledBorder("Saving..."));
    loadingDialog = new JInternalFrame();
    loadingDialog.add(progressBar);
    loadingDialog.pack();
    loadingDialog.setVisible(true);
    TwoDMapEditor.frame.add(loadingDialog);
    TwoDMapEditor.frame.revalidate();
    TwoDMapEditor.frame.repaint();          
}

class ProgressBar extends SwingWorker<Void, Void>{
    public int percentage = 1;
    protected Void doInBackground(){
        int i = 0, j;
          while (i < TwoDMapEditor.size[0]) {
              j = 0;
                while(j < TwoDMapEditor.size[1]){
                    if (!TwoDMapEditor.mySql.updateMapPiece(i, j)) {
                        System.out.println("Something went wrong");
                    }
                    percentage = ((i * TwoDMapEditor.size[1]) + j+1) * 100 / (TwoDMapEditor.size[0] * TwoDMapEditor.size[1]);
                    progressBar.setValue(percentage);
                    System.out.println(percentage);
                    j++;
                }
                i++;
          }

        return null;
    }

    public void done(){
        loadingDialog.dispose();
    }
}

public void actionPerformed(ActionEvent me) {
    progressBarClass = new ProgressBar();
    progressBarClass.addPropertyChangeListener(this);
    progressBarClass.execute(); 
}

public void propertyChange(PropertyChangeEvent pce) {
    System.out.println(TwoDMapEditor.saveActionListener.progressBarClass.percentage);
    progressBar.setValue(TwoDMapEditor.saveActionListener.progressBarClass.percentage);
}

}


Solution

  • Without a completely runnable example, it's kind of hard to know where problems might be.

    You should defiantly be updating the progress within the loop. If you could guarantee that every TwoDMapEditor.size[1] was the same value, it would be easier.

    You basically need to know two things, the total number of possible iterations and the number of iterations you've completed, but I don't see any information in your snippet which might provide that information.

    Anyway, I would also suggest using a SwingWorker, as it provides the ability to notify the UI of a progress state change quite easily, for example...

    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.GridBagLayout;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    import java.util.Random;
    import javax.swing.JDesktopPane;
    import javax.swing.JFrame;
    import javax.swing.JInternalFrame;
    import javax.swing.JPanel;
    import javax.swing.JProgressBar;
    import javax.swing.SwingWorker;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class TestProgress {
    
        public static void main(String[] args) {
            new TestProgress();
        }
    
        public TestProgress() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    JDesktopPane dp = new JDesktopPane() {
    
                        @Override
                        public Dimension getPreferredSize() {
                            return new Dimension(400, 400);
                        }
    
                    };
                    JInternalFrame inf = new JInternalFrame("Progressing", true, false, false, false);
                    inf.add(new TestPane());
                    inf.pack();
                    dp.add(inf);
                    inf.setVisible(true);
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(dp);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private JProgressBar pb;
    
            public TestPane() {
                setLayout(new GridBagLayout());
                pb = new JProgressBar();
                add(pb);
    
                LongRunningProcessWorker worker = new LongRunningProcessWorker();
                worker.addPropertyChangeListener(new PropertyChangeListener() {
                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {
                        String name = evt.getPropertyName();
                        SwingWorker worker = (SwingWorker) evt.getSource();
                        switch (name) {
                            case "progress":
                                System.out.println(worker.getProgress());
                                pb.setValue(worker.getProgress());
                                break;
                            case "state":
                                switch (worker.getState()) {
                                    case DONE:
                                        // Close the window or something
                                        break;
                                }
                                break;
                        }
                    }
                });
                worker.execute();
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
        }
    
        public class LongRunningProcessWorker extends SwingWorker {
    
            @Override
            protected Object doInBackground() throws Exception {
    
                Random rnd = new Random();
                int outterMax = rnd.nextInt(150) + 50;
                int innerMax = rnd.nextInt(150) + 50;
    
                System.out.println(outterMax + "/" + innerMax);
    
                for (int outter = 0; outter < outterMax; outter++) {
                    for (int inner = 0; inner < innerMax; inner++) {
                        float progress = (float)((outter * innerMax) + inner) / (float)(innerMax + outterMax);
                        Thread.sleep(10); // Simulate some processing
                    }
                }
                return null;
            }
    
        }
    
    }
    

    Take a look at Concurrency in Swing and Worker Threads and SwingWorker for more details