javaswingswingworkerjprogressbarswingutilities

Java Swing ProgressBar update while computing


There are some example for similar question (Progress Bar Java) and (Java uploading percentage progressbar thread) but didn't understand how to make it work for my code.

My main has 2 panels one over the other (one of which is a tabbedPane with 2 tabs)

public class MainIRH {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }
                MainWindow window = new MainWindow();
                JPanel mainPanel = new JPanel(new BorderLayout(0, 20));
                mainPanel.setBorder(new EmptyBorder(20, 20, 20, 20));

                JTabbedPane tabbedPane = new JTabbedPane();
                tabbedPane.setBorder(BorderFactory.createLineBorder(Color.BLACK));

                ControlPanel controlPanel = new ControlPanel();

                tabbedPane.setBorder(new EmptyBorder(10, 10, 10, 10));
                controlPanel.setBorder(new EmptyBorder(0, 20, 20, 20));

                ECPanel ecPanel = new ECPanel();
                SMRPanel smrPanel = new SMRPanel();

                tabbedPane.addTab("Environnement Canada", null, ecPanel, "Convertir des données des stations d\'Environnement Canada");
                tabbedPane.addTab("Station Météo Routière", null, smrPanel, "Convertir des données des stations météos routières");

                mainPanel.add(tabbedPane, BorderLayout.NORTH);
                mainPanel.add(controlPanel, BorderLayout.CENTER);
                window.add(mainPanel);

                new ControllerIRH(ecPanel, smrPanel, controlPanel);

                window.setVisible(true);
            }
        });
    }
}

The important bit is this object : SMRPanel smrPanel = new SMRPanel();

Inside this object I have a JButton convertArchiveButton that I add a ActionListener from a Controller class

public void addConvertArchiveButton(ActionListener ConvertArchiveButton) {
    convertArchiveButton.addActionListener(ConvertArchiveButton);
}

Here's the the implementation of the Listener in the Controller

private class ConvertArchiveButtonListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent event) {
             converter.convert();
    }
}

converter is the class that does the lengthy operation

File[] listOfFiles = folder.listFiles();

for (int file = 0; file < listOfFiles.length; file++) {

     int progress = Math.round(file * 100 / listOfFiles.length);

     //tried a lot of things here...

    //...computing

}

I example tried to add some new Runnable with both EventQueue and SwingUtilities but didn't work. A SwingWorker class would be useful? Don't quite understand the use.

Basically, int progress would be passed to the JProgressBar in the SMRPanel.

EDIT WITH ControlAltDel answer :

Still doesn't work, converter.readFile(file); doesn't get called.

private class ConvertArchiveButtonListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent event) {
        File[] listOfFiles = folder.listFiles();

        System.out.println(listOfFiles.length);

        for (int i = 0; i < listOfFiles.length; i++) {
            final File file = listOfFiles[i];
            final int idx = (i * 100) / listOfFiles.length;
            Thread t = new Thread(new Runnable() {
                public void run() {
                    converter.readFile(file);

                    System.out.println(idx);

                    SwingUtilities.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            smrPanel.setProgressBarValue(idx);
                        }
                    });
                }
            });

            try {
                t.start();
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        converter.convert(year);

    }
}

Solution

  • Your problem is that you are running your loop in the EDT thread. Fix by running this in another thread, and then setting the updated progress value in the EDT when you finish processing a file

    private class ConvertArchiveButtonListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent event) {
                new Thread(new Runnable() {
                public void run() {
                   for (int file = 0; file < listOfFiles.length; file++) {
                      final File f = listOfFiles[i];
                      final idx = file;
                      Thread t = new Thread(new Runnable() {
                        converter.convert(f);
                        swingUtilities.invokeLater(new Runnable() {
                          myProgressBar.setValue(idx);
                        });
                       t.start();
                       t.join();
                      });
                     }
                 }).start();
        }
    }