1

I have tried following tutorials but I keep getting lost somewhere.

What I need is a class that creates and displays a progress bar (JProgressBar) that I can set the value of as I iterate over data loaded from a file and place into the database memory. My problems come that every example I have found has some kind of counter that fills the progress bar and executes from a "main" function. Every time I alter that tutorial to be a class that I can call at will and display the bar, I do not get the bar showing (IE the frame comes up but the bar does not even look like it is added to the frame until after the iteration is done).

I have tried using SwingUtilities.invokeLater and SwingWorker (latest attempt at class below) all having the same issue. To make matters worse, I can do a dbug.myMessage (basically sends to System.out) and see a message that shows that the bar is changing in memory just not showing. I am obviously missing something probably simple but I can't think of what it is.

One other thing, if I leave the tutorial as is Java Tutorials Code Sample – ProgressBarDemo2.java and just change the main to a createAndShow method, it works but of course it does not do what I need it to do.

I did post another question about this but have altered the class so much I thought it best to post a new question.

So, here is my altered code that does not seem to work:

public class MyProgressBar extends JPanel implements PropertyChangeListener, 
                                                     MyData, 
                                                     Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -1632492668549544408L;

    private MyDebug         dbug               = new MyDebug( MyData.MYDEBUGCHECK.MYPROGRESSBAR.getOn() );

    public static final int MAX                = 100;
    public static final int WIDTH              = 400;
    public static final int HEIGHT             = 75;

    private JProgressBar    myBar              = new JProgressBar( SwingConstants.HORIZONTAL, 0, MAX );
    private JFrame          myFrame            = new JFrame();

    public  Task            task;


    class Task extends SwingWorker<Void, Void> {

        public int myValue = 0;

        @Override
        public Void doInBackground() {
            //Initialize progress property.
            setProgress(0);
            while (myValue < 100) {
                //Make random progress.
                //myValue += random.nextInt(10);
                setProgress( Math.min( myValue, 100 ) );
                dbug.myMessage( "MYPROGRESSBAR", "doInBackground", "Value is %3.2f %d", myBar.getPercentComplete(), myValue );
                myBar.repaint();
            }
            return null;
        }

        public void done() {
        }

        public void mySetValue( int percent ) {
            myValue = (int)( MAX * ( (double)percent / 100.0 ) );
            dbug.myMessage( "MYPROGRESSBAR", "mySetValue", "Value is %3.2f %d percent was %d", myBar.getPercentComplete(), myValue, percent );
        }

    }



    public MyProgressBar() {
        add(myBar);

        int x = ( MyData.SCREEN.width  / 2 ) - ( WIDTH  / 2);
        int y = ( MyData.SCREEN.height / 2 ) - ( HEIGHT / 2);

        this.setBounds( x, y, WIDTH, HEIGHT );

        myFrame.setBounds( x, y, WIDTH, HEIGHT );
        myFrame.setUndecorated(true);
        myFrame.getContentPane().setSize( new Dimension( WIDTH, HEIGHT ) );
        myFrame.setMinimumSize( new Dimension( WIDTH, HEIGHT ) );
        myFrame.setPreferredSize( new Dimension( WIDTH, HEIGHT ) );
        myFrame.setSize( new Dimension( WIDTH, HEIGHT ) );
        myFrame.setVisible(false);
        myFrame.getContentPane().setLayout(null);
        myFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);

        myBar.setStringPainted( true );
        myBar.setBorderPainted( true );
        myBar.setValue( 0 );
        myBar.setBounds( 0, 0, WIDTH, HEIGHT );
        myBar.addPropertyChangeListener( this );

        myFrame.add( myBar );

        //Create and set up the content pane.
        //JComponent newContentPane = new MyProgressBar();
        JComponent newContentPane = myBar;
        newContentPane.setOpaque(true);                     //content panes must be opaque

        myFrame.setContentPane(newContentPane);
        myFrame.pack();

    }

    public void createAndShow () {

        //Display the window.
        myFrame.setVisible(true);
        myFrame.repaint();

    }

    public void hideAndClear () {
        //myFrame.setVisible(false);
    }


    @Override
    public void propertyChange(PropertyChangeEvent args) {
        dbug.myMessage( "MYPROGRESSBAR", "propertyChange", "Value is %s", args.getPropertyName() );
        if ( "progress" == args.getPropertyName() ) {
            int progress = (Integer) args.getNewValue();
            //myBar.setValue(progress);
        }
    }

    public void start () {
        //Instances of javax.swing.SwingWorker are not reusuable, so
        //we create new instances as needed.
        task = new Task();
        task.addPropertyChangeListener(this);
        task.execute();
    }

}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Patrick Aquilone
  • 584
  • 2
  • 11
  • 28
  • For better help sooner, post an [SSCCE](http://sscce.org/) (like in the tutorial code). – Andrew Thompson May 04 '12 at 14:19
  • I can't do self contained because the issue is that when called from another class this does not work. When I used the tutorial (which has the main class) it works because it is not trying to change the progress outside of the class. – Patrick Aquilone May 04 '12 at 14:33
  • I could probably trim this. But self contained would defeat the question. – Patrick Aquilone May 04 '12 at 14:33
  • As a note, I took out the while loop in the doInBackground because I read that would hold up the update. Now, it does appear immediately with 0%. I get my print outs that show the status is update but the visible bar is not. The property change only gets fired after we hit 100%. – Patrick Aquilone May 04 '12 at 14:39
  • Sorry, I did see the link. When I read about it being self contained, I stopped because I need it to work with other classes and not by itself. In fact, when I change the createAndShow to a main and then add a loop to change to value it works. It does not work when instead of a loop it the value needs to change based on another classes conditions. – Patrick Aquilone May 04 '12 at 14:41
  • Since 2 classes are (apparently) required to demonstrate the problem, make them both default level access (I.E. not `public`), put them in the same source file, and name it after the class that has the `main()`. – Andrew Thompson May 04 '12 at 14:45
  • 1
    Ahhhhh, I see what you are saying. – Patrick Aquilone May 04 '12 at 15:36
  • Ok, so I made the class with two classes in it and it works. So somehow something I am doing when it is all part of the larger application is causing it to not update. In addition, I noticed that a JScrollPane I have does not update until all the vectors have been drawn that were loaded from the XML file into the database. So, I guess now the question is what could be holding up graphics updates including repaint until done or released. I would post the code but not being sure where the actually hiccup is I don't know what to post. – Patrick Aquilone May 04 '12 at 16:21

3 Answers3

3

Instead of calling setProgress in the doInBackGround method, you should call the publish method available in the SwingWorker.

The publish method will pass these values to the process method which is called on the Event Dispatch Thread, and it is in this method you should update the progress bar.

In another question I already posted an example which does exactly that

Community
  • 1
  • 1
Robin
  • 36,233
  • 5
  • 47
  • 99
  • Ok, I have a problem with the @Override of process. It keeps telling me to remove the override. Is that ok? – Patrick Aquilone May 04 '12 at 15:36
  • @JesterHawk That means that you are not actually overriding anything. So double-check the signature of your method (or ask your IDE to override the method which will generate a skeleton method with the correct signature) – Robin May 04 '12 at 16:17
  • That is the odd thing. If I remove the process function it does not complain that it is missing anything. I expected it too but it did not – Patrick Aquilone May 04 '12 at 16:22
  • *"If I remove the process function it does not complain"* If `process(List)` was declared `protected abstract void process(List chunks)` that **would** be a problem. – Andrew Thompson May 04 '12 at 16:25
  • @JesterHawk sounds like you need to do some reading on what it means to override a method. Of course it does not complain if you do not override a non-abstract method. Otherwise you would have to override each method, which would be only nice if my salary would depend on the lines of code I can write ... – Robin May 04 '12 at 16:28
  • Perhaps. But I was just copying and pasting your code in that Eclipse was complaining about. I will freely admit that I am learning and don't know as much as I would like. Please help me learn. Thanks! – Patrick Aquilone May 05 '12 at 18:04
2

Maybe the following example will be of use to you. It pretends to load a large XML file and adds a Panel for each "important data segment" (which is really just a string in this example.) The GUI is updated through a PropertyChangeEvent in the SwingWorker.

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;

public class WorkerExample
{

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                WorkerGui gui = new WorkerGui();
                JFrame frame = gui.createFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}



class WorkerGui
{
    public static final String ADD_NEW_ROW_PROPERTY = "ADD_NEW_ROW";
    private static final Dimension SUB_PANEL_SIZE = new Dimension(350, 50);
    private JButton button;
    private JComponent labelComponent;
    private JScrollPane scroller;
    int labelCount = 0;

    private List<String> loadXml() {
        try {
            // pretend to load a large XML file...
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException ex) {
            Logger.getLogger(WorkerGui.class.getName()).log(Level.SEVERE, null, ex);
        }

        List<String> dataList = new ArrayList<String>();

        for (int i = 1; i <= 20; i++) {
            dataList.add("Important data " + i);
        }

        return dataList;
    }

    ProgressMonitor monitor = null;


    class XmlWorker extends SwingWorker<Void, Void> {

        @Override protected Void doInBackground() throws Exception {
            monitor.setProgress(2);
            List<String> rows = loadXml();

            final int denom = rows.size();

            int i = 1;
            Random random = new Random();
            for (String string : rows) {
                if (monitor.isCanceled()) {
                    break;
                }
                Thread.sleep(random.nextInt(2000));
                firePropertyChange(ADD_NEW_ROW_PROPERTY, null, string);
                setProgress((int)(100 * (i / (double)denom)));
                i++;
            }
            return null;
        }

        @Override protected void done() {
            // restore the button
            button.setEnabled(true);
            button.requestFocus();
        }
    }


    final PropertyChangeListener listener = new PropertyChangeListener() {
        @Override public void propertyChange(PropertyChangeEvent evt) {
            if ("progress".equals(evt.getPropertyName())) {
                monitor.setProgress((Integer)evt.getNewValue());
            } else if (ADD_NEW_ROW_PROPERTY.equals(evt.getPropertyName())) {
                addRow((String)evt.getNewValue());
            }
        }
    };

    private void doWork() {
        monitor = new ProgressMonitor(labelComponent.getTopLevelAncestor(), "Loading XML", "", 0, 100);
        monitor.setProgress(0);
        monitor.setMillisToPopup(0);
        monitor.setMillisToDecideToPopup(0);
        XmlWorker worker = new XmlWorker();
        worker.addPropertyChangeListener(listener);
        worker.execute();
    }

    private JComponent setupGui() {
        labelComponent = Box.createVerticalBox();
        scroller = new JScrollPane(labelComponent,
                                    JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 
                                    JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        JPanel buttonPanel = new JPanel();
        button = new JButton("Do it!");
        buttonPanel.add(button);
        button.addActionListener(new ActionListener() {
            @Override public void actionPerformed(ActionEvent e) {
                button.setEnabled(false);
                clear();
                doWork();
            }
        });

        JPanel mainPanel = new JPanel(new BorderLayout());
        mainPanel.add(scroller, BorderLayout.CENTER);
        mainPanel.add(buttonPanel, BorderLayout.SOUTH);
        return mainPanel;
    }

    public JFrame createFrame() {
        JFrame frame = new JFrame("Hello!");
        frame.setSize(400, 400);
        JComponent component = setupGui();
        frame.add(component);
        return frame;
    }

    public void clear() {
        labelComponent.removeAll();
        labelCount = 0;
        scroller.validate();
        scroller.getTopLevelAncestor().repaint();
    }

    /**
     * Creates a JLabel out of the data and adds it to the main panel.
     * Must be called on Event Dispatch Thread!!
     * @param data  the data to add, could be any class.
     */
    public void addRow(String data) {
        if (labelCount > 0) {
            labelComponent.add(Box.createVerticalStrut(10));
        }
        labelCount++;

        final JLabel label = new JLabel(data);
        JPanel panel = new JPanel();
        panel.add(label);

        panel.setPreferredSize(SUB_PANEL_SIZE);
        panel.setMaximumSize(SUB_PANEL_SIZE);
        panel.setBorder(BorderFactory.createLineBorder(Color.BLUE));

        labelComponent.add(panel);
        scroller.validate();
        panel.scrollRectToVisible(panel.getBounds());
    }
}
black panda
  • 2,842
  • 1
  • 20
  • 28
  • 1
    I followed your model and got it to work. AWESOME!! Thanks! – Patrick Aquilone May 07 '12 at 12:46
  • What happens when the "CANCEL" button is hit? Actually, is there anyway to remove that button on the Progress Monitor window? Thanks. – Patrick Aquilone May 07 '12 at 12:49
  • if the "CANCEL" button is hit, the ProgressMonitor cancels. The code I posted checks if the monitor is canceled and breaks out of the loop. You will have to create your own dialog from JProgressBar if you want specialization. – black panda May 07 '12 at 17:34
  • Taking your example, I was able to use the JProgressBar and make a custom progress window. THANKS! – Patrick Aquilone May 08 '12 at 11:43
0

I did get this to work and am posting in case anyone happens to search on this tread and needs the working code.

package com.swa.fin.esaa.sgb.myclasses.myzzarchives;

import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingConstants;
import javax.swing.SwingWorker;

public class MyProgressBarTest {

    public class MyProgressBar extends JPanel implements PropertyChangeListener, 
                                                         Serializable {

        private static final long serialVersionUID = -1632492668549544408L;

        public static final int MAX                = 100;
        public static final int WIDTH              = 400;
        public static final int HEIGHT             = 75;

        private JProgressBar    myBar              = new JProgressBar( SwingConstants.HORIZONTAL, 0, MAX );
        private JFrame          myFrame            = new JFrame();

        public  Task            task;


        class Task extends SwingWorker<Void, Integer> {

            public int myValue = 0;

            @Override
            public Void doInBackground() {
                //Initialize progress property.
                setProgress(0);
                myBar.setValue( myValue );
                myBar.repaint();
                return null;
            }

            public void done() {
            }

            public void mySetValue( int percent ) {
                myValue = (int)( MAX * ( (double)percent / 100.0 ) );
                setProgress( Math.min( myValue, 100 ) );
                myBar.repaint();
            }

        }

        public MyProgressBar() {
            add(myBar);

            myFrame.setUndecorated(false);
            myFrame.getContentPane().setSize( new Dimension( WIDTH, HEIGHT ) );
            myFrame.setMinimumSize( new Dimension( WIDTH, HEIGHT ) );
            myFrame.setPreferredSize( new Dimension( WIDTH, HEIGHT ) );
            myFrame.setSize( new Dimension( WIDTH, HEIGHT ) );
            myFrame.setVisible(false);
            myFrame.getContentPane().setLayout(null);
            myFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);

            myBar.setStringPainted( true );
            myBar.setBorderPainted( true );
            myBar.setValue( 0 );
            myBar.setBounds( 0, 0, WIDTH, HEIGHT );
            myBar.addPropertyChangeListener( this );

            myFrame.add( myBar );

            //Create and set up the content pane.
            //JComponent newContentPane = new MyProgressBar();
            JComponent newContentPane = myBar;
            newContentPane.setOpaque(true);                     //content panes must be opaque

            myFrame.setContentPane(newContentPane);
            myFrame.pack();

        }

        public void createAndShow () {

            //Display the window.
            myFrame.setVisible(true);
            myFrame.repaint();

        }

        public void hideAndClear () {
            //myFrame.setVisible(false);
        }


        @Override
        public void propertyChange(PropertyChangeEvent args) {
            if ( "progress" == args.getPropertyName() ) {
                int progress = (Integer) args.getNewValue();
                myBar.setValue(progress);
            }
        }

        public void mySetValue( int percent ) {
            if ( task != null ) {
                task.mySetValue( percent );
            }
            myBar.repaint();
        }

        public void start () {
            //Instances of javax.swing.SwingWorker are not reusuable, so
            //we create new instances as needed.
            task = new Task();
            task.addPropertyChangeListener(this);
            task.execute();
        }
    }




    public class Tester {

        public Tester() {
        }
        public void testit () {
            MyProgressBar bar = new MyProgressBar();
            bar.createAndShow();
            bar.start();
            bar.mySetValue(  0 );
            try { Thread.sleep( 100 );  } catch (InterruptedException e) { e.printStackTrace(); } 
            bar.mySetValue(  10 );
            try { Thread.sleep( 100 );  } catch (InterruptedException e) { e.printStackTrace(); } 
            bar.mySetValue(  20 );
            try { Thread.sleep( 100 );  } catch (InterruptedException e) { e.printStackTrace(); }
            for ( int count = 20; count <= 100; count++ ) {
                bar.mySetValue(  count );
                try { Thread.sleep( 100 );  } catch (InterruptedException e) { e.printStackTrace(); }
            }
        }
    }


    public static void main ( String[] args ) {
        MyProgressBarTest my = new MyProgressBarTest();
        MyProgressBarTest.Tester test = my.new Tester();

        test.testit();

    }


}

But if I take this and call it from the class the reads the XML file, unmarshals it and then loops through the loaded ArrayList and created JLabels that are placed on a JPanel it does not update any of the graphics (any of them at all) until the class has completely finished what it is doing despite repaints and stuff.

Any thoughts on that?

Patrick Aquilone
  • 584
  • 2
  • 11
  • 28