Подтвердить что ты не робот

Обновление jProgressBar от SwingWorker

Я использую для отслеживания долговременной задачи путем обновления 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 для anyreason? Я считаю, что второй код более сложный и в моем случае и не знаю, есть ли какое-либо преимущество или причина для использования второго.

Любые советы?

ИЗМЕНИТЬ Спасибо за ответ. Как резюме. Первое решение является "неправильным" из-за того, что обновление индикатора выполнения выполняется вне EDT. Второе решение является "правильным", потому что обновление индикатора выполнения выполняется внутри EDT

Теперь, согласно "интересному" ответу @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?) И как?

4b9b3361

Ответ 1

В первом коде вы вызываете следующую строку в потоке non-EDT (поток диспетчера событий). Таким образом, он не является потокобезопасным:

progressBar.setValue(value);

Это может привести к неожиданному поведению, поскольку Swing не предназначен для потоковой библиотеки.

Существуют разные способы выполнения этого в режиме Swing. Один правильный способ - это то, что вы сделали во втором посте. Другим будет использование методов publish()/process(), а третий метод будет писать собственный поток вместо SwingWorker и использовать SwingUtilities.invokeLater().

Ответ 2

Ваш второй подход правильный и даже документирован в классе javadoc класса SwingWorker. Событие "прогресс" запускается на EDT, поэтому ваш слушатель обновляет индикатор выполнения на EDT. Это не относится к вашему первому подходу.

Пример другого подхода (используя publish/process, как указано визирем) можно найти в моем ответе на предыдущий вопрос SO

Ответ 3

Я использую для отслеживания долговременной задачи путем обновления 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 с помощью SwingWorker (требуется глубокое знание о Java Essential Classes и Generics тоже),

В базовых реализациях для Runnable#Thread требуется только invokeLater для вывода в GUI Swing, и в случае, который начался с EDT (из Swing/AWT Listener), и без какой-либо строки кода содержит 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 и Swing очень хорошо, тогда вы можете думать о SwingWorker

Ответ 4

Как показано в примере , использование вами рабочего setProgress() в вашем втором примере правильно: любой PropertyChangeListener будет асинхронно уведомляться о потоке отправки событий.