0

I created one external class with Swing worker that runs the progress bar. This is the code,

public class ExtProgressMonitor extends JFrame {

private static final long serialVersionUID = 1L;
private static final String s = "Database Statistics Report is exectuing in the Background";
private JProgressBar progressBar = new JProgressBar(0, 100);
private JLabel label = new JLabel(s, JLabel.CENTER);

public ExtProgressMonitor() {
    this.setLayout(new GridLayout(0, 1));
    this.setTitle("Database Utility Execution");
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.add(progressBar);
    this.add(label);
    this.setSize(100, 100);
    this.setLocationRelativeTo(null);
    this.setVisible(true);
    pack();
}

public void runCalc() {
    progressBar.setIndeterminate(false);
    progressBar.setStringPainted(false);
    TwoWorker task = new TwoWorker();
    task.addPropertyChangeListener(new PropertyChangeListener() {

        @Override
        public void propertyChange(PropertyChangeEvent e) {
            if ("progress".equals(e.getPropertyName())) {
                progressBar.setIndeterminate(false);
                progressBar.setValue((Integer) e.getNewValue());
            }
        }
    });
    task.execute();
}

private class TwoWorker extends SwingWorker<Integer, Integer> {

    private static final int N = 500;
    private final DecimalFormat df = new DecimalFormat(s);
    Integer x = 1;

    @Override
    protected Integer doInBackground() throws Exception {
        if (!javax.swing.SwingUtilities.isEventDispatchThread()) {
            System.out.println("javax.swing.SwingUtilities.isEventDispatchThread() + returned false.");
        }
        for (int i = 1; i <= N; i++) {
            x = x - (((x * x - 2) / (2 * x)));
            setProgress(i * (100 / N));
            publish(Integer.valueOf(x));
            Thread.sleep(1000); // simulate latency
        }
        return Integer.valueOf(x);
    }

    @Override
    protected void process(List<Integer> chunks) {
        for (Integer percent : chunks ) {
           progressBar.setValue(progressBar.getValue() + percent);
        }
    }
}

The above code works when I call it in main class like below .

public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            ExtProgress t = new ExtProgress();
            t.runCalc();
        }
    });
}

However When I try to call the same in my Action button that fetches a lot of row from database, takes around 15-20 minutes. progressbar gets launched, once the db process starts, the progressbar is frozen, while the db statistics is fetched.once the long process is over, the progress bar continues to run again.

private void jButton1ActionPerformed(java.awt.event.ActionEvent e) throws Exception {                                         
         
    ExtProgressMonitor t = new ExtProgressMonitor();
    t.runCalc();
    CheckStorage.DBVaultCheck(Host, port, instance, workbook, Schema_Password, schema);
    //.. Rest of the process, check storage comes from another class. 
    });

Can you please help me fix this issue?

Abra
  • 19,142
  • 7
  • 29
  • 41
  • If I add a `JButton` to your GUI and an `ActionListener` that calls method `runCalc()`, the progress bar is not frozen. – Abra Aug 28 '20 at 16:33
  • @Abra could you please post how you accessed it ? – Parthasarathy J Aug 28 '20 at 16:54
  • It seems that `CheckStorage.DBVaultCheck` is a long process which is invoked on the EDT and blocks it. It is only a guess because no MRE is provided. As asked in the previous similar [question](https://stackoverflow.com/questions/63612798/update-jprogressbar-from-a-swingworker/63615279?noredirect=1#comment112526088_63615279) you posted please post [mre]. – c0der Aug 29 '20 at 04:04

2 Answers2

0

I just added three lines (and commented out two others) to the code you posted in your question. The lines I added are indicated in the below code with a comment preceding them.

import java.awt.EventQueue;
import java.awt.GridLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DecimalFormat;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

public class ExtProgressMonitor extends JFrame {

    private static final long serialVersionUID = 1L;
    private static final String s = "Database Statistics Report is exectuing in the Background";
    private JProgressBar progressBar = new JProgressBar(0, 100);
    private JLabel label = new JLabel(s, JLabel.CENTER);

    public ExtProgressMonitor() {
        this.setLayout(new GridLayout(0, 1, 10, 10));
        this.setTitle("Database Utility Execution");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(progressBar);
        this.add(label);

        /*Added next three lines.*/
        JButton b = new JButton("GO");
        b.addActionListener(e -> runCalc());
        add(b);

        pack();
//        this.setSize(100, 100);
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }

    public void runCalc() {
        progressBar.setIndeterminate(false);
        progressBar.setStringPainted(false);
        TwoWorker task = new TwoWorker();
        task.addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent e) {
                if ("progress".equals(e.getPropertyName())) {
                    progressBar.setIndeterminate(false);
                    progressBar.setValue((Integer) e.getNewValue());
                }
            }
        });
        task.execute();
    }

    private class TwoWorker extends SwingWorker<Integer, Integer> {
        private static final int N = 500;
        private final DecimalFormat df = new DecimalFormat(s);
        Integer x = 1;

        @Override
        protected Integer doInBackground() throws Exception {
            if (!javax.swing.SwingUtilities.isEventDispatchThread()) {
                System.out.println(
                        "javax.swing.SwingUtilities.isEventDispatchThread() + returned false.");
            }
            for (int i = 1; i <= N; i++) {
                x = x - (((x * x - 2) / (2 * x)));
                setProgress(i * (100 / N));
                publish(Integer.valueOf(x));
                Thread.sleep(1000); // simulate latency
            }
            return Integer.valueOf(x);
        }

        @Override
        protected void process(List<Integer> chunks) {
            for (Integer percent : chunks) {
                progressBar.setValue(progressBar.getValue() + percent);
            }
        }
    }
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                ExtProgressMonitor t = new ExtProgressMonitor();
//                t.runCalc();
            }
        });
    }
}

When I click on the GO button, the progress bar starts to fill.

Abra
  • 19,142
  • 7
  • 29
  • 41
0

An MRE representing your application could look like the following:

import java.awt.GridLayout;
import java.text.DecimalFormat;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

public class SwingMain {

    SwingMain() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.add(new TestPanel());
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        new SwingMain();
    }
}

class TestPanel extends JPanel {

    public TestPanel() {

        JButton btn = new JButton("Run long process");
        btn.addActionListener(evt -> runLongProcess());
        add(btn);
    }

    private void runLongProcess() {
        new ExtProgressMonitor().runCalc();
        CheckStorage.DBVaultCheck();
    }
}

//For a second frame it is recommended to use JDialog instead of JFRame
class ExtProgressMonitor extends JFrame {

    private static final long serialVersionUID = 1L;
    private static final String s = "Database Statistics Report is exectuing in the Background";
    private final JProgressBar progressBar = new JProgressBar(0, 100);
    private final JLabel label = new JLabel(s, JLabel.CENTER);

    public ExtProgressMonitor() {
        this.setLayout(new GridLayout(0, 1));
        this.setTitle("Database Utility Execution");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(progressBar);
        this.add(label);
        this.setSize(100, 100);
        this.setLocationRelativeTo(null);
        this.setVisible(true);
        pack();
    }

    public void runCalc() {
        progressBar.setIndeterminate(false);
        progressBar.setStringPainted(false);
        new TwoWorker().execute();
    }

    private class TwoWorker extends SwingWorker<Integer, Integer> {

        private static final int N = 500;
        private final DecimalFormat df = new DecimalFormat(s);
        Integer x = 1;

        @Override
        protected Integer doInBackground() throws Exception {
            for (int i = 1; i <= N; i++) {
                x = x - (x * x - 2) / (2 * x);
                publish(Integer.valueOf(x));
                Thread.sleep(1000); // simulate latency
            }
            return Integer.valueOf(x);
        }

        @Override
        protected void process(List<Integer> chunks) {
            for (Integer percent : chunks ) {
                progressBar.setValue(progressBar.getValue() + percent);
            }
        }
    }
}

class CheckStorage{

    private static final int  LIMIT = 1000;
    public static void DBVaultCheck() {
        int counter = 0;
        while(counter ++ < LIMIT)   { //simulate long process
            try {
                Thread.sleep(1000);
                System.out.println(counter);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
}

The problem with this code is that CheckStorage.DBVaultCheck() starts a long process on the EDT.
Swing is a single Thread library. All painting tasks are executed in the Event Dispatcher Thread (EDT). Running long processes (such as sleep) on the EDT makes keeps this thread busy, so it does not do other things like updating the gui. The gui becomes unresponsive (freezes).
Assuming CheckStorage.DBVaultCheck() does not update the gui, all you have to do is run the long process on a different thread by changing a single line in the code:

    private void runLongProcess() {
        new ExtProgressMonitor().runCalc();
        new Thread(()->CheckStorage.DBVaultCheck()).start();
    }

In case CheckStorage.DBVaultCheck() does update the gui, you'l have to take measures to make sure that those updates occur on the EDT. Swing gui updates should only be done by the EDT.

c0der
  • 18,467
  • 6
  • 33
  • 65
  • hI @c0der, Thanks a lot for taking time and giving me a nice working example. I was able to fix it. However I have another query. Without a class called CheckStorage, if I want to put the method DBVaultCheck inside runLongProcess() with this concept of Swing worker. i.e private void runLongProcess() { new ExtProgressMonitor().runCalc(); new Thread(()->DBVaultCheck(); } I tried but only ExtProgressMonitor is working. Thank again! – Parthasarathy J Aug 29 '20 at 10:54
  • I am glad it helped. For more questions please make a new post. – c0der Aug 29 '20 at 11:19