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();
}
}
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());
}
}
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
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?