12

ProgressBar を更新して、長時間実行されるタスクを監視するために使用します。もちろん、長時間実行されるタスクは Swingworker スレッドで実行されます。

私はそのようなことをプログラムしていました:

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 を使用する必要がありますか? 私の場合、2番目のコードはより複雑で、2番目のコードを使用する利点や理由があるかどうかわかりません。

アドバイスはありますか?

編集 回答ありがとうございます。まとめとして。プログレスバーの更新はEDTの外部で実行されるため、最初の解決策は「間違っています」。プログレスバーの更新はEDT内で実行されるため、2番目の解決策は「正しい」です

さて、私の場合の @mKorbel の「興味深い」回答によると、私の計算結果は、「挿入」した HTML テキストになります (このリンクを参照)。私の現在のコードは次のとおりです。

私は(文字列)を発行し、私のプロセスコードはそのようになります

@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?)、どのようにテーブルレンダリングをオーバーライドするために彼が使用することを意味しますか?

4

4 に答える 4

7

最初のコードでは、非 EDT (イベント ディスパッチャー スレッド) スレッドで次の行を呼び出しています。したがって、スレッドセーフではありません。

progressBar.setValue(value);

Swing はスレッドセーフなライブラリとして設計されていないため、予期しない動作が発生する可能性があります。

Swing 方式でこれを実行するには、さまざまな方法があります。これの 1 つの正しい方法は、2 番目の投稿で行ったことです。もう 1 つはメソッドを使用するpublish()/process()方法で、3 番目の方法は の代わりに独自のスレッドを作成SwingWorkerして使用することSwingUtilities.invokeLater()です。

于 2012-05-27T11:19:34.200 に答える
5

ProgressBar を更新して、長時間実行されるタスクを監視するために使用します。もちろん、長時間実行されるタスクは 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;
    }
}

しかし、Wwing GUI を使用して複雑にする理由(およびSwingWorkerについての深い知識が必要)、Java Essential ClassesGenerics

基本的な実装は、Swing GUI への出力にRunnable#Threadのみ必要でinvokeLaterあり、EDT (Swing/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(シンプル、簡単、バグのない、明確な方法)、あなたの知識が非常によくある場合にのみ、あなたは考えることができJavaますSwingSwingWorker

于 2012-05-27T13:19:05.187 に答える
4

あなたの2番目のアプローチは正しく、クラスのクラスjavadocにも文書化されていSwingWorkerます。「progress」イベントは EDT で発生するため、リスナーは EDT の進行状況バーを更新します。これは、最初のアプローチには当てはまりません。

別のアプローチの例 ( publish/processvizier で示されているように使用) は、以前の SO の質問に対する私の回答にあります。

于 2012-05-27T11:59:07.957 に答える
4

このに示すように、2 番目の例でのワーカーの使用setProgress()は正しいです。すべてPropertyChangeListenerのイベントは、イベント ディスパッチ スレッドで非同期に通知されます。

于 2012-05-27T13:50:46.920 に答える