java swing progress-bar swingworker

java - Actualización de jProgressBar desde SwingWorker



progress-bar (4)

Utilizo para monitorear una tarea larga ejecutando una ProgressBar. La tarea de larga ejecución se realiza en un hilo de Swingworker.

bien, puede usar SwingWorker en todos los casos para redirigir cualquier tarea pesada y larga al Background

import java.awt.*; import java.util.*; import javax.swing.*; import javax.swing.table.*; public class TableCellProgressBar { private String[] columnNames = {"String", "ProgressBar"}; private Object[][] data = {{"dummy", 100}}; private DefaultTableModel model = new DefaultTableModel(data, columnNames) { private static final long serialVersionUID = 1L; @Override public Class<?> getColumnClass(int column) { return getValueAt(0, column).getClass(); } @Override public boolean isCellEditable(int row, int col) { return false; } }; private JTable table = new JTable(model); public JComponent makeUI() { TableColumn column = table.getColumnModel().getColumn(1); column.setCellRenderer(new ProgressRenderer()); EventQueue.invokeLater(new Runnable() { @Override public void run() { startTask("test"); startTask("error test"); startTask("test"); } }); JPanel p = new JPanel(new BorderLayout()); p.add(new JScrollPane(table)); return p; } //http://java-swing-tips.blogspot.com/2008/03/jprogressbar-in-jtable-cell.html private void startTask(String str) { final int key = model.getRowCount(); SwingWorker<Integer, Integer> worker = new SwingWorker<Integer, Integer>() { private int sleepDummy = new Random().nextInt(100) + 1; private int lengthOfTask = 120; @Override protected Integer doInBackground() { int current = 0; while (current < lengthOfTask && !isCancelled()) { if (!table.isDisplayable()) { break; } if (key == 2 && current > 60) { //Error Test cancel(true); publish(-1); return -1; } current++; try { Thread.sleep(sleepDummy); } catch (InterruptedException ie) { break; } publish(100 * current / lengthOfTask); } return sleepDummy * lengthOfTask; } @Override protected void process(java.util.List<Integer> c) { model.setValueAt(c.get(c.size() - 1), key, 1); } @Override protected void done() { String text; int i = -1; if (isCancelled()) { text = "Cancelled"; } else { try { i = get(); text = (i >= 0) ? "Done" : "Disposed"; } catch (Exception ignore) { ignore.printStackTrace(); text = ignore.getMessage(); } } System.out.println(key + ":" + text + "(" + i + "ms)"); } }; model.addRow(new Object[]{str, 0}); worker.execute(); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { createAndShowGUI(); } }); } public static void createAndShowGUI() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.getContentPane().add(new TableCellProgressBar().makeUI()); frame.setSize(320, 240); frame.setLocationRelativeTo(null); frame.setVisible(true); } } class ProgressRenderer extends DefaultTableCellRenderer { private final JProgressBar b = new JProgressBar(0, 100); public ProgressRenderer() { super(); setOpaque(true); b.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Integer i = (Integer) value; String text = "Completed"; if (i < 0) { text = "Error"; } else if (i < 100) { b.setValue(i); return b; } super.getTableCellRendererComponent(table, text, isSelected, hasFocus, row, column); return this; } }

pero ¿por qué complicar la GUI de Wwing mediante el uso de SwingWorker (también se requería el conocimiento más profundo sobre Java Essential Classes and Generics ),

Las implementaciones básicas para Runnable#Thread solo requieren invokeLater para la salida a la GUI de Swing, y en el caso que se inició desde EDT (de Swing / AWT Listener), y sin ninguna línea de código contiene Thread.sleep(int) entonces invokeLater solo se invokeLater / requerido para el código de producción

import java.awt.Component; import java.util.Random; import javax.swing.JFrame; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; public class TableWithProgressBars { public static class ProgressRenderer extends JProgressBar implements TableCellRenderer { private static final long serialVersionUID = 1L; public ProgressRenderer(int min, int max) { super(min, max); this.setStringPainted(true); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { this.setValue((Integer) value); return this; } } private static final int maximum = 100; public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new TableWithProgressBars().createGUI(); } }); } public void createGUI() { final JFrame frame = new JFrame("Progressing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Integer[] oneRow = {0, 0, 0, 0}; String[] headers = {"One", "Two", "Three", "Four"}; Integer[][] data = {oneRow, oneRow, oneRow, oneRow, oneRow,}; final DefaultTableModel model = new DefaultTableModel(data, headers); final JTable table = new JTable(model); table.setDefaultRenderer(Object.class, new ProgressRenderer(0, maximum)); table.setPreferredScrollableViewportSize(table.getPreferredSize()); frame.add(new JScrollPane(table)); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); new Thread(new Runnable() { @Override public void run() { Object waiter = new Object(); synchronized (waiter) { int rows = model.getRowCount(); int columns = model.getColumnCount(); Random random = new Random(System.currentTimeMillis()); boolean done = false; while (!done) { int row = random.nextInt(rows); int column = random.nextInt(columns); Integer value = (Integer) model.getValueAt(row, column); value++; if (value <= maximum) { model.setValueAt(value, row, column); try { waiter.wait(15); } catch (InterruptedException e) { e.printStackTrace(); } } done = true; for (row = 0; row < rows; row++) { for (column = 0; column < columns; column++) { if (!model.getValueAt(row, column).equals(maximum)) { done = false; break; } } if (!done) { break; } } } frame.setTitle("All work done"); } } }).start(); } }

mi conclusión para tareas realmente pesadas y de larga ejecución es que has visto Runnable#Thread (simple, fácil, no burocrático y claro), solo si tu conocimiento sobre Java & Swing muy bueno, entonces puedes pensar en SwingWorker

Utilizo para monitorear una tarea larga ejecutando una ProgressBar. La tarea de larga ejecución se realiza en un hilo de Swingworker.

Solía ​​programar cosas como esa:

public class MySwingWorkerClass extends SwingWorker<Void, Void> { private JProgressBar progressBar; public MySwingWorker(JProgressBar aProgressBar) { this.progressBar = aProgressBar; progressBar.setVisible(true); progressBar.setStringPainted(true); progressBar.setValue(0); } @Override public Void doInBackground() { //long running task loop { calculation(); progressBar.setValue(value); } return null; } @Override public void done() { progressBar.setValue(100); progressBar.setStringPainted(false); progressBar.setVisible(false); } }

pero recientemente descubrí que podía hacerlo usando el "setProgress" y definiendo el cambio de propiedad y haciendo cosas como esa

public class MySwingWorkerClass extends SwingWorker<Void, Void> { private JProgressBar progressBar; public MySwingWorker(JProgressBar aProgressBar) { addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if ("progress".equals(evt.getPropertyName())) { progressBar.setValue((Integer) evt.getNewValue()); } } }); progressBar.setVisible(true); progressBar.setStringPainted(true); progressBar.setValue(0); setProgress(0); } @Override public Void doInBackground() { //long running task loop { calculation(); setProgress(value); } return null; } @Override public void done() { setProgress(100); progressBar.setValue(100); progressBar.setStringPainted(false); progressBar.setVisible(false); } }

Mi pregunta es: ¿es aceptable mi primer código o debo usar el setProgress para cualquier razón? Encuentro el segundo código más complicado y en mi caso, y no sé si hay alguna ventaja o razón para usar el segundo.

¿Algún consejo?

EDITAR Gracias por la respuesta. Como un resumen. La primera solución es "incorrecta" debido a que la actualización de la barra de progreso se realiza fuera del EDT. La segunda solución es "correcta" porque la actualización de la barra de progreso se realiza dentro del EDT

Ahora, de acuerdo con la respuesta "interesante" de @mKorbel, en mi caso mi cálculo da resultados en el texto HTML que "inserto" (ver este enlace ). Mi código actual es el siguiente.

Publico (cadena) y mi código de proceso se ve así

@Override protected void process(List<String> strings) { for (String s : strings) { try { htmlDoc.insertBeforeEnd(htmlDoc.getElement(htmlDoc.getDefaultRootElement(), StyleConstants.NameAttribute, HTML.Tag.TABLE), s); } catch (BadLocationException ex) { } catch (IOException ex) { } } }

¿Cómo puedo volver a usar @mKobel para hacer lo mismo en mi caso? Quiero decir, él utiliza para anular la representación de la tabla en mi caso, ¿qué procesador debo anular (jTextPane?) Y cómo?


En el primer código, está llamando a la siguiente línea en un subproceso no EDT (subproceso del distribuidor de eventos). Por lo tanto, no es seguro para subprocesos

progressBar.setValue(value);

Esto puede provocar un comportamiento inesperado ya que Swing no está diseñado como una biblioteca segura para subprocesos.

Hay diferentes métodos para realizar esto en el modo Swing. Una forma correcta de esto es lo que has hecho en la segunda publicación. Otra sería utilizar los métodos publish()/process() , y un tercer método sería escribir su propio hilo en lugar de SwingWorker y usar SwingUtilities.invokeLater() .


Como se muestra en este ejemplo , su uso del setProgress() del trabajador en su segundo ejemplo es correcto: cualquier PropertyChangeListener se notificará de manera asincrónica en la cadena de distribución del evento.


Su segundo enfoque es correcto e incluso está documentado en la clase javadoc de la clase SwingWorker . El evento ''progreso'' se dispara en el EDT, por lo que su oyente actualiza la barra de progreso en el EDT. Este no es el caso en su primer acercamiento.

Un ejemplo de otro enfoque (utilizando publish/process según lo indicado por el visir) se puede encontrar en mi respuesta en una pregunta SO anterior.