Bence Kaulics Bence Kaulics - 2 months ago 12
Java Question

SwingWorker does not update JProgressBar without Thread.sleep() in custom dialog panel

I have a

SwingWorker
class which loads a text file and slices it to chunks for further processing.

This is the
SwingWorker
class:

public class ConverterWorker extends SwingWorker<String, String>
{
private final File f;
private final JLabel label;

public ConverterWorker(File f, JLabel label)
{
this.f = f;
this.label = label;
}

@Override
protected String doInBackground() throws Exception
{
NMTMain.convertableData = getDataSets(f);

if(!NMTMain.convertableData.isEmpty())
{
return "Done";
}
else
{
publish("Failed to load the file!");
return "Failed";
}
}

@Override
public void done()
{
try
{
label.setText(get());
}
catch (Exception e)
{
e.printStackTrace(System.err);
System.out.println("error");
}
}

@Override
protected void process(List<String> chunks)
{
label.setText(chunks.get(chunks.size() - 1));
}

public ArrayList<ArrayList<Convertable>> getDataSets(File f)
{
ArrayList<ArrayList<Convertable>> dataSets = new ArrayList<ArrayList<Convertable>>();

publish("Loading file...");
setProgress(0);

String[] data = loadFile(f);

for(int i = 0; i< NMTMain.nodes.size(); i++)
{
dataSets.add(splitByNode(data, NMTMain.nodes.get(i).getName()));
}

setProgress(100);

return dataSets;
}

private ArrayList<Convertable> splitByNode(String[] data, String name)
{
ArrayList<Convertable> temp = new ArrayList<Convertable>();

for(int i = 0; i < data.length; i++)
{
if(data[i].contains(name))
{
temp.add(new Convertable(data[i]));
}
}

Collections.sort(temp);

return temp;
}

private String[] loadFile(File f)
{
String data = "";
String[] nodes;

long fileLength = f.length();
int bytesRead = -1;
int totalBytesRead = 0;

try
{
if(f.exists())
{
Scanner scan = new Scanner(f);

while(scan.hasNextLine())
{
String line = scan.nextLine();
data = data + line + "\n";
bytesRead = line.getBytes().length;
totalBytesRead += bytesRead;
int progress = (int) Math.round(((double) totalBytesRead / (double) fileLength) * 100d);

/* try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/

//publish("loading... " + String.valueOf(progress));
setProgress(progress);
}

scan.close();
}

}
catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}

nodes = data.split("\n\"\n");

return nodes;
}


This works fine when the
Thread.sleep(1);
is uncommented. However, when I comment the
Thread.sleep(1);
the class does not update the progressbar.

I call my class thorugh a button, here is the
ActionListener
:

loadInput.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent arg0)
{
int returnval=NMTMain.fileChooser.showOpenDialog(NMTMain.MainFrame);

if(returnval == 0)
{
File f=NMTMain.fileChooser.getSelectedFile();

final ConverterWorker worker = new ConverterWorker(f, dialogPanel.getLabel());

worker.addPropertyChangeListener(new PropertyChangeListener()
{
@Override
public void propertyChange(final PropertyChangeEvent evt)
{
if("progress".equalsIgnoreCase(evt.getPropertyName()))
{
dialogPanel.showProgressDialog("Conversion");
dialogPanel.setProgressBarValue((int) evt.getNewValue());
}

if(worker.isDone())
{
dialogPanel.showConfirmDialog("Conversion", "OK");
}
}
});
worker.execute();

}
}
});


This should work fine without the sleep, so what is that I am doing wrong here?

UPDATE:

It turned out that my
DialogPanel
is not the best, and cause this behaviour.

Here is the
DialogPanel
class:

public class DialogPanel extends JDialog
{

private JLabel label;
private JPanel panel;
private JButton button;
private JProgressBar progressBar;

public DialogPanel()
{
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5,10,5,10);

panel = new JPanel();
panel.setLayout(new GridBagLayout());

progressBar = new JProgressBar(0,100);
progressBar.setVisible(false);
progressBar.setStringPainted(true);

setLabel(new JLabel("def text", SwingConstants.CENTER));

button = new JButton("OK");
button.setVisible(false);

button.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent arg0)
{
// TODO Auto-generated method stub
dispose();
}
});

gbc.gridx = 0;
gbc.gridy = 0;
panel.add(getLabel(), gbc);
gbc.gridy = 1;
panel.add(progressBar, gbc);
panel.add(button, gbc);

this.setContentPane(panel);
this.setLocationRelativeTo(null);
this.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
this.setResizable(false);
}

public void setProgressBarValue(int value)
{
progressBar.setValue(value);
}

public void setProgressBarVisibility(boolean value)
{
progressBar.setVisible(value);
}

public void setText(String text)
{
getLabel().setText(text);
}

public void showProgressDialog(String title)
{
progressBar.setVisible(true);
button.setVisible(false);
this.setTitle(title);
this.pack();

if(!this.isVisible())
{
this.setVisible(true);
}
}

public void showConfirmDialog(String title, String buttontext)
{
progressBar.setVisible(false);
button.setVisible(true);
this.setTitle(title);
button.setText(buttontext);
this.pack();

if(!this.isVisible())
{
this.setVisible(true);
}
}

public JLabel getLabel()
{
return label;
}

public void setLabel(JLabel label)
{
this.label = label;
}

public JProgressBar getProgressBar()
{
return progressBar;
}

@Override
public Dimension getPreferredSize()
{
return new Dimension(200, 100);
}
}


It is probably a mess for professional eyes. How can I show the progressbar in a dialog which will have a confirm button to dispose the dialog when the process is done?

Solution:

I have changed from my
DialogPanel
class to ProgressMonitor and now everything is fine.
Thank you for your time and advices.

Answer

I wanted to track my SwingWorker's progress with a JProgressBar within a JDialog. However my SwingWorker class could not handle my custom DialogPanel class. To achieve the same result, using the default ProgressMonitor class was the best option.

I have passed the ProgressMonitor to the SwingWorker through its constructor:

private final File f;
private final ProgressMonitor pm

public FileLoadWorker(File f, ProgressMonitor pm)
{
    this.f = f;
    this.pm = pm;
}

and changed the following methods like this:

@Override
public void done() 
{
    try 
    {
        pm.setNote(get());
    } 
    catch (Exception e)
    {
        e.printStackTrace(System.err);
        System.out.println("error");
    }
}

@Override
protected void process(List<String> chunks)
{
   pm.setNote(chunks.get(chunks.size() - 1));
}

The task's propertyChangeListener changed like this:

final FileLoadWorker worker = new FileLoadWorker(f, pm);

worker.addPropertyChangeListener(new PropertyChangeListener()
{
    @Override
    public void propertyChange(final PropertyChangeEvent evt)
    {
        if("progress".equalsIgnoreCase(evt.getPropertyName())) 
        {
            pm.setProgress((int) evt.getNewValue());
        }

        if("state".equals(evt.getPropertyName())) 
        {
            SwingWorker.StateValue s = (SwingWorker.StateValue) evt.getNewValue();
            if(s.equals(SwingWorker.StateValue.DONE))
            {
                pm.setProgress(100);
                pm.close();
                Toolkit.getDefaultToolkit().beep();
            }
        }

        if(pm.isCanceled())
        {
            pm.close();
            worker.cancel(true);
        }
    }
});
worker.execute();

Thanks to trashgod for the answer and comments about the state property.