java - SwingWorker no actualiza JProgressBar sin Thread.sleep() en el panel de diálogo personalizado
thread-sleep (2)
Tengo una clase
SwingWorker
que carga un archivo de texto y lo corta en trozos para su posterior procesamiento.
Esta es la clase
SwingWorker
:
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;
}
Esto funciona bien cuando
Thread.sleep(1);
No está comentado.
Sin embargo, cuando comento el
Thread.sleep(1);
la clase no actualiza la barra de progreso.
Llamo a mi clase a través de un botón, aquí está el
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();
}
}
});
Esto debería funcionar bien sin dormir, entonces, ¿qué es lo que estoy haciendo mal aquí?
ACTUALIZAR:
Resultó que mi
DialogPanel
no es el mejor y causa este comportamiento.
Aquí está la clase
DialogPanel
:
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);
}
}
Probablemente sea un desastre para los ojos profesionales. ¿Cómo puedo mostrar la barra de progreso en un cuadro de diálogo que tendrá un botón de confirmación para eliminar el cuadro de diálogo cuando el proceso haya finalizado?
Solución:
He cambiado de mi clase
DialogPanel
a
ProgressMonitor
y ahora todo está bien.
Gracias por su tiempo y consejos.
La API
setProgress()
señala: "Para fines de rendimiento, todas estas invocaciones se fusionan en una invocación con el último argumento de invocación solamente".
Agregar
Thread.sleep(1)
simplemente difiere la fusión;
Invocar
println()
introduce un retraso comparable.
Anímate a que tu sistema de archivos sea tan rápido;
Sería reacio a introducir un retraso artificial.
Como ejemplo concreto que ilustra el efecto, agregué informes intermedios a este
example
completo, como se muestra a continuación.
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);
}
}
});
Quería seguir el
SwingWorker
mi
SwingWorker
con una
JProgressBar
dentro de un
JDialog
.
Sin embargo, mi clase
SwingWorker
no pudo manejar mi clase personalizada
DialogPanel
.
Para lograr el mismo resultado, usar la clase
ProgressMonitor
predeterminada era la mejor opción.
He pasado el
ProgressMonitor
al
SwingWorker
través de su constructor:
private final File f;
private final ProgressMonitor pm
public FileLoadWorker(File f, ProgressMonitor pm)
{
this.f = f;
this.pm = pm;
}
y cambió los siguientes métodos como este:
@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));
}
La
propertyChangeListener
la tarea cambió así:
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();
Gracias a
por la respuesta y los comentarios sobre la propiedad del
state
.