Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 667
Bence Kaulics Avatar asked Jan 09 '23 11:01

Bence Kaulics


2 Answers

The setProgress() API notes: "For performance purposes all these invocations are coalesced into one invocation with the last invocation argument only." Adding Thread.sleep(1) simply defers the coalescence; invoking println() introduces a comparable delay. Take heart that your file system is so fast; I would be reluctant to introduce an artificial delay. As a concrete example that illustrates the effect, I added intermediate reporting to this complete example, as shown below.

private static class LogWorker extends SwingWorker<TableModel, String> {
    private long fileLength;
    private long bytesRead;
    ...
    this.fileLength = file.length();
    ...
    while ((s = br.readLine()) != null) {
        publish(s);
        bytesRead += s.length();
        int progress = (int)(100 * bytesRead / fileLength);
        // System.out.println(progress);
        setProgress(progress);
    }
    ...
}

lw.addPropertyChangeListener((PropertyChangeEvent e) -> {
    if ("progress".equals(e.getPropertyName())) {
        jpb.setValue((Integer)e.getNewValue());
    }
    if ("state".equals(e.getPropertyName())) {
        SwingWorker.StateValue s = (SwingWorker.StateValue) e.getNewValue();
        if (s.equals(SwingWorker.StateValue.DONE)) {
            jpb.setValue(100);
        }
    }
});
like image 73
trashgod Avatar answered Jan 25 '23 01:01

trashgod


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.

like image 36
Bence Kaulics Avatar answered Jan 24 '23 23:01

Bence Kaulics