Вопрос по swing, java – Обновление jProgressBar от SwingWorker

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

Любой совет?

EDIT Спасибо за ответ. Как резюме. Первое решение является "неправильным" из-за индикатора выполнения обновление выполняется вне 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?) И как?

Я думаю, что ваш второй подход также неверен. Вы используете слушателя для установки значения, которое будет вызывать слушателя, которое будет устанавливать значение, которое будет вызывать слушателя и т. Д. И т. Д. Я не уверен, действительно ли это произойдет, но это не так. Таким образом, вы не получаете преимущества SwingWorker, вы все равно устанавливаете значение индикатора выполнения в EDT. Martijn Courteaux
SwingWorker.publish(V...) & quot; Этот метод должен использоваться изнутриdoInBackground метод для предоставления промежуточных результатов для обработки наEvent Dispatch Thread внутри метода процесса. & quot; Andrew Thompson
@MartijnCourteaux: я не уверен, чтобы понять. Я использовал этоdocs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html написать слушателю. Можете ли вы предоставить мне код, чтобы объяснить, что вы говорите. HpTerm
@ Andrews Thompson: я использую публикацию для & quot; публикации & quot; результаты, но здесьdocs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html чтобы установить значение индикатора выполнения, они используют setProgress, а НЕ публикуют, так? HpTerm
@trashgod: О да, я вижу. Я не заметил, что слушатель был добавлен в SwingWorker вместо ProgressBar. Спасибо! Узнал что-то: D Martijn Courteaux

Ваш Ответ

4   ответа
5

ng running task is of course performed in a Swingworker thread.

правильно, вы можете использовать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 Classes а такжеGenerics тоже),

Основные реализации дляRunnable#Thread требуется толькоinvokeLater для вывода в графический интерфейс Swing, а также в том случае, если он запущен из 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 & Амп;Swing очень хорошо, тогда вы можете думать оSwingWorker

Error: User Rate Limit ExceededQuestion!
7

отличном от EDT (Thread Dispatcher Thread). Так что это не потокобезопасно:

progressBar.setValue(value);

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

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

Error: User Rate Limit Exceededdocs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.htmlError: User Rate Limit Exceeded HpTerm
4

Ваш второй подход верен и даже задокументирован в классе JavadocSwingWorker учебный класс. «Прогресс» событие происходит на EDT, поэтому ваш слушатель обновляет индикатор выполнения на EDT. Это не так в вашем первом подходе.

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

Error: User Rate Limit Exceeded HpTerm
Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededPropertyChangeListenerError: User Rate Limit ExceededallError: User Rate Limit Exceeded
4

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

Error: User Rate Limit Exceeded

Похожие вопросы