1

I am trying to write a progress bar for an application that is downloading information before the GUI runs. Because this is such a long process to download and organize the information, I am wanting to inform the user of the progress. I decided on using a progress bar late in the game and, as such, a majority of the code is written and I'm trying to incorporate the progress bar into the code without a drastic re-working of the code. The following is the code for the progress bar. Currently, the progress bar comes up AFTER everything runs and the GUI pops up.

static class PopulatingCardsWorker extends SwingWorker<Void, Integer> {

    JProgressBar jpb;
    int max;
    JLabel label;

    public PopulatingCardsWorker(JProgressBar jpb, int maximum, JLabel label) {
        this.jpb = jpb;
        this.max = maximum;
        this.label = label;
    }

    @Override
    protected void process(List<Integer> chunks) {
        int i = chunks.get(chunks.size()-1);
        jpb.setValue(i); // The last value in this array is all we care about.
        System.out.println(i);
        label.setText("Loading " + i + " of " + max);
    }

    @Override
    protected Void doInBackground() throws Exception {
        for(int i = 0; i < max; i++) {
            Thread.sleep(10); // Illustrating long-running code.
            publish(i);
        }
        return null;
    }

    @Override
    protected void done() {
        try {
            get();
            JOptionPane.showMessageDialog(jpb.getParent(), "Success", "Success", JOptionPane.INFORMATION_MESSAGE);
        } catch (ExecutionException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

private static void go(int max) {
    JFrame frame = new JFrame();
    JPanel panel = new JPanel();
    JLabel label = new JLabel("Loading...");
    JProgressBar jpb = new JProgressBar();
    jpb.setIndeterminate(false);
    jpb.setMaximum(max);
    panel.add(label);
    panel.add(jpb);
    frame.add(panel);
    frame.pack();
    frame.setSize(200,90);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    new PopulatingCardsWorker(jpb, max, label).execute();
}

The program initially calls the GUI application and then runs the database acquisition code as shown below.

public static void main(String args[]) {
    /* Create and display the form */
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new MagicTheGatheringUI().setVisible(true);
        }
    });
}

public MagicTheGatheringUI() {
    int i, size;

    colorRefinementFilter = "selected";
    try {
        my_list=new CardDatabase();
        my_list.sortByName(my_list.all_cards);
        my_list.populateSubArrays();
        size = my_list.all_cards.size();

        for(i = 0; i < size; i++)
        {
            namesList.addElement(my_list.all_cards.get(i).name);
        }
    } catch(Exception e) {
        System.out.println(e);
        System.exit(0);
    }
    initComponents();
}

The swing worker is created during the creation of "my_list=new CardDatabase();". In that class, I have the swing worker and the process the swing worker is supposed to monitor.

I currently call swing worker in a method called "populate_cards()" and I use the following code to create the swing worker. The swing worker is supposed to monitor what's going on in the populate_cards() method. All of the data in the swing worker methods are just temporary until I better understand how to make it work the way I want it to.

SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            go(1000);
        }
    });

I believe the issue is that I'm calling the progress bar inside of the "invokeLater" method for the actual GUI.

I have looked at the following questions to try and solve my problem.

How to add a progress bar?

Can a progress bar be used in a class outside main?

and I have also looked at tutorials.

Brian
  • 13
  • 3
  • 1
    Based on your code, I can't see anywhere that you're calling `PopulatingCardsWorker.go` – MadProgrammer Aug 06 '18 at 04:04
  • I'd also highlight the fact that `SwingWorker` supports a `progress` property which can be coupled with a `PropertyChangeListener` - [demonstrated here](https://stackoverflow.com/questions/24835638/issues-with-swingworker-and-jprogressbar/24835935#24835935) – MadProgrammer Aug 06 '18 at 04:08
  • I apologize. I reread the post and made more edits to provide more of the code. Currently, the code is several thousand lines long so I'm trying to limit what is shown to only relevant information. Again, I apologize for neglecting that last part you requested. – Brian Aug 06 '18 at 04:12
  • How is `populate_cards` called? You might consider providing a [runnable example](https://stackoverflow.com/help/mcve) which demonstrates the work flow and your current issue – MadProgrammer Aug 06 '18 at 04:18
  • Ummmm I'm not sure how feasible providing a runnable example would be in terms of this program. It's rather large and would take an immense amount of time reducing the code to the bare bones that details out my problem. To answer your question, populate_cards is a method inside of CardDatabase. So, the GUI is invoked and then before the components are initialized, the database is initialized. When initializing the database, a swing worker is called to monitor the progress of the database being initialized. – Brian Aug 06 '18 at 04:26
  • Sooo, you need to "wait" till the `SwingWorker` is `done` before moving on ... I'd consider a `CountDownLatch` or some other `Semaphore` - just don't "wait" on these on the context of the EDT – MadProgrammer Aug 06 '18 at 04:27

1 Answers1

0

Your code is ok. The reason why the progressBar (and the gui itself) pops after the code ends is that by default in java, the code and the gui runs on the same Thread. Because the code has higher priority then the gui, all the code executes first and after that the gui updates.

You should do that in the "go" method:

new Thread(){
    public void run(){
       new PopulatingCardsWorker(jpb, max, label).execute();
    }
} start();

see that article for more information about threads

Im answering from my phone, so sorry about the bad writing.

Code and GUi threads

and from Wikipedia

last one

SagiZiv
  • 932
  • 1
  • 16
  • 38
  • I used your suggestion and got a few errors. I looked up the Java tutorial on threading and got to this point. new Thread(new Runnable() { @Override public void run(){ new PopulatingCardsWorker(jpb, max, label).execute(); } }).start(); But now I'm getting the error that "jpb, max, and label are local variables being accessed from within inner class; needs to be declared final". I've seen that error before and I couldn't find a way to fix that either. Thoughts? – Brian Aug 06 '18 at 16:13
  • I actually just fixed those errors, ran the code and had the same issue =/ The Progress bar pops up after the code it's supposed to monitor has finished and then begins running. Is it possibly that I'm calling this runnable inside of another runnable as mentioned in the OP? – Brian Aug 06 '18 at 16:30
  • Hi @Brian, sorry for that mistake, I forgot the dot before the start. about the other error you got, you can put all the method (from the JFrame) inside the run in the thread. did you managed to solve it¿ – SagiZiv Aug 07 '18 at 03:38