2012-05-27 94 views
12

我用於通過更新ProgressBar來監視長時間運行的任務。 長時間運行的任務當然是在Swingworker線程中執行的。來自SwingWorker的jProgressBar更新

我用來編程這樣的事情:

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

但最近我發現,我可以通過使用「setProgress」和定義屬性的變化做,做這樣的事情

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

我的問題是:我的第一個代碼是可以接受的,還是應該使用setProgress作爲任何原因? 我發現第二個代碼更復雜,在我的情況下,不知道是否有任何優勢或原因使用第二個。

有沒有建議嗎?

編輯 感謝您的回答。作爲總結。 由於進度條更新在EDT之外執行,所以第一個解決方案是「錯誤的」。 第二種解決方案是「正確的」,因爲進度條更新裏面的EDT

執行現在,根據@mKorbel在我的情況下,「有趣」的答案我計算給在HTML文本結果我「插入」(見this link)。我目前的代碼如下。

我出版(串)和我的過程代碼看起來像

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

如何重新@mKobel做同樣的在我的案件。我的意思是他使用重寫表格渲染在我的情況下,我應該重寫什麼渲染器(jTextPane?)以及如何?

+1

我認爲你的第二種方法也是錯誤的。你正在使用一個監聽器來設置值,它將調用監聽器,它將設置值,這將調用監聽器等等。我不確定這是否會發生,但是它感覺不對。這樣,您沒有得到SwingWorker的好處,您仍然在設置EDT中進度條的值。 –

+1

['SwingWorker.publish(V ...)'](http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html#publish%28V...%29) 「這種方法是在'doInBackground'方法內部使用的,以便在處理方法內的* Event Dispatch Thread *上處理中間結果。」 –

+0

@Andrews Thompson:我使用發佈來「發佈」結果,但在這裏http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html設置他們使用setProgress的progressbar值和不發佈,所以? – HpTerm

回答

7

在第一個代碼中,您在非EDT(Event Dispatcher線程)線程中調用以下行。所以它不是線程安全的:

progressBar.setValue(value); 

這可能會導致意外的行爲,Swing不是設計成線程安全的庫。

以Swing方式執行此操作有多種方法。一個正確的方法就是你在第二篇文章中所做的。另一種方法是使用publish()/process()方法,第三種方法是編寫自己的線程而不是SwingWorker並使用SwingUtilities.invokeLater()

+0

你可以編輯你的答案,說如果你的意思是我的第二個代碼是使用的?因爲評論說它是「錯誤的」,但我認爲根據這個http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html,我是否應該使用「發佈」來更新進度欄?我認爲setprogress這是什麼。 – HpTerm

4

你的第二種方法是正確的,甚至記錄在SwingWorker類的javadoc類中。 「進度」事件在EDT上觸發,因此您的監聽器會更新EDT上的進度條。 在第一種方法中情況並非如此。

的另一種方法(使用publish/process由大臣所指示的)的例子可以在my answer on a previous SO question

+0

哎呀,我早前忽略了這個正確的分析。 – trashgod

+0

您提供的鏈接很有趣我喜歡在流程/發佈中更新進度欄的方式,而不是使用setProgress。仔細看看這個。 – HpTerm

+1

羅賓的有用示例顯示了直接耦合工作人員和進度欄的便利性;通過PropertyChangeListener鬆散耦合的優點是_all_監聽器在EDT上通知,而不僅僅是進度條。 – trashgod

4

我使用通過更新進度監視長期運行的任務中找到。長時間運行的任務當然是在Swingworker線程中執行的。您可以使用在所有情況下SwingWorker重定向任何重物和長時間運行的任務交給了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; 
    } 
} 

但爲什麼用SwingWorker(約Java Essential ClassesGenerics需要最深的知識太)Wwing GUI複雜

權,

Runnable#Thread的基本實現只需要invokeLater輸出到Swing GUI,並且在starte d從EDT(從鞦韆/ AWT監聽器),且無任何代碼行中包含Thread.sleep(int)則是invokeLater只有諫/生產所需的代碼

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

我的結論對你看看Runnable#Thread實重和長時間運行的任務(簡單,方便,non_buggy和清晰的方式),只有當你對Java & Swing知識非常好,那麼你可以考慮SwingWorker

+0

這個答案在我的情況下生成異常看到[問題](http://stackoverflow.com/questions/13538275/the-property-change-event-of-progress-bar-not-firing)! –

4

在本example所示,你的工人的setProgress()的使用在你的第二個例子是正確的:一個將在事件調度線程上異步通知。

+0

對,我總是忘記這個 – mKorbel