2012-05-27 19 views
12

Io uso per monitorare un'attività di lunga durata aggiornando una barra di avanzamento. L'attività di lunga durata viene ovviamente eseguita in un thread di Swingworker.jProgressBar aggiornamento da SwingWorker

ho usato per programmare cose del genere:

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

ma di recente ho scoperto che avrei potuto farlo utilizzando il "setProgress" e definisce il cambiamento di proprietà e fare cose del genere

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

La mia domanda è: il mio primo codice è accettabile o devo usare il setProgress per anyreason? Trovo il secondo codice più complicato e nel mio caso e non so se c'è un vantaggio o un motivo per usare il secondo.

Qualche consiglio?

MODIFICA Grazie per la risposta. Come sommario La prima soluzione è "errata" perché l'aggiornamento della barra di avanzamento viene eseguito all'esterno dell'EDT. La seconda soluzione è "corretta" perché l'aggiornamento della barra di avanzamento viene eseguito all'interno dell'EDT

Ora, in base alla risposta "interessante" di @mKorbel nel mio caso il mio calcolo restituisce risultati in testo HTML che "inserisco" (vedere this link). Il mio codice attuale è il seguente.

che pubblico (stringa) e il mio codice processo sembra che

@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) { 
      } 
     } 
    } 

Come posso riutilizzare @mKobel a fare lo stesso nel mio caso. Voglio dire che usa per sovrascrivere il rendering della tabella nel mio caso quale renderer devo sovrascrivere (jTextPane?) E come?

+1

Penso che anche il tuo secondo approccio sia sbagliato. Stai utilizzando un listener per impostare il valore, che invocherà il listener, che imposterà il valore, che invocherà l'ascoltatore, ecc. Non sono sicuro che ciò accadrà, ma è sbagliato. In questo modo, non ottieni i vantaggi di SwingWorker, stai ancora impostando il valore della barra di avanzamento nell'EDT. –

+1

['SwingWorker.publish (V ...)'] (http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html#publish%28V...%29) "Questo metodo deve essere utilizzato all'interno del metodo' doInBackground' per fornire risultati intermedi per l'elaborazione nel * Thread di invio eventi * all'interno del metodo di processo. " –

+0

@Andrews Thompson: utilizzo la pubblicazione per "pubblicare" i risultati ma qui http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html per impostare un valore della barra di avanzamento usano setProgress e NON pubblicare, quindi? – HpTerm

risposta

7

Nel primo codice, si chiama la seguente riga in un thread non EDT (Event Dispatcher Thread). Quindi non è thread-safe:

progressBar.setValue(value); 

Ciò può causare un comportamento imprevisto come swing non è stato progettato come una libreria thread-safe.

Esistono diversi metodi per eseguire questa operazione nella modalità Swing. Un modo corretto di ciò è quello che hai fatto nel secondo post. Un altro sarebbe utilizzare i metodi publish()/process() e un terzo metodo sarebbe scrivere il proprio thread anziché SwingWorker e utilizzare SwingUtilities.invokeLater().

+0

Puoi modificare la tua risposta per dire se intendi che il mio secondo codice è quello da usare? perché i commenti dicono che è "sbagliato", ma ho pensato che fosse giusto secondo questo http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html dovrei usare la "pubblica" per aggiornare la barra di avanzamento? Ho pensato a setprogress cosa c'è per questo. – HpTerm

4

Il secondo approccio è corretto ed è persino documentato nella classe javadoc della classe SwingWorker. L'evento 'progresso' viene attivato sull'EDT, quindi il listener aggiorna la barra di avanzamento sull'EDT. Questo non è il caso nel tuo primo approccio.

Un esempio di un altro approccio (usando publish/process come indicato dal visir) può essere trovato in my answer on a previous SO question

+0

Oops, ho trascurato questo prima, analisi corretta. – trashgod

+0

Il collegamento che hai fornito è interessante Mi piace il modo in cui aggiorni la barra di avanzamento nel processo/pubblica e non usando il setProgress. Avremo uno sguardo più da vicino a questo. – HpTerm

+1

L'esempio utile di Robin mostra la comodità di accoppiare direttamente il lavoratore e la barra di avanzamento; il vantaggio dell'accoppiamento approssimativo tramite 'PropertyChangeListener' è che tutti gli ascoltatori sono notificati sull'EDT, non solo la barra di avanzamento. – trashgod

4

che utilizzo per monitorare un'operazione all'esecuzione prolungata aggiornando un ProgressBar. Il compito di lunga durata viene ovviamente eseguito in un thread di Swingworker.

destra è possibile utilizzare SwingWorker in tutti i casi per reindirizzare qualsiasi compito pesante e lunga corsa per il 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; 
    } 
} 

ma perché complicare Wwing GUI utilizzando SwingWorker (più profonda conoscenza necessaria su Java Essential Classes e Generics troppo) ,

Implementazioni di base per Runnable#Thread richieste solo invokeLater per l'output alla GUI Swing e nel caso in cui starte d da EDT (da Swing/AWT Listener), e senza alcuna linea di codice contiene Thread.sleep(int) allora è invokeLater solo CONSIGLIA/richiesta per il codice di produzione

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

mia conclusione per vero compito in esecuzione pesante e lungo avete un'occhiata a Runnable#Thread (semplice, facile, non_buggy e modo chiaro), solo se è la vostra conoscenza circa Java & Swing molto bene allora si può pensare di SwingWorker

+0

questa risposta genera eccezioni nel mio caso vedi [Domanda] (http://stackoverflow.com/questions/13538275/the-property-change-event-of-progress-bar-not-firing)! –

4

Come mostrato in questo example, l'utilizzo del lavoratore del setProgress() nel secondo esempio è corretto: un ny PropertyChangeListener verrà notificato in modo asincrono sul thread di invio dell'evento.

+0

ho sempre dimenticato questo – mKorbel

Problemi correlati