简体   繁体   English

来自SwingWorker的jProgressBar更新

[英]jProgressBar update from SwingWorker

I use to monitor a long running task by updating a ProgressBar. 我用来通过更新ProgressBar来监视长时间运行的任务。 The long running task is of course performed in a Swingworker thread. 长时间运行的任务当然是在Swingworker线程中执行的。

I used to program things like that : 我曾经编程过这样的事情:

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

but recently I discovered that I could do it by using the "setProgress" and defining the property change and do things like that 但最近我发现我可以通过使用“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);      
   }
}

My question is : is my first code acceptable or shall I use the setProgress for anyreason ? 我的问题是:我的第一个代码是可接受的,还是我应该使用setProgress进行任何调整? I find the second code more complicated and in my case and don't know if there is any advantage or reason to use the second one. 我发现第二个代码更复杂,在我的情况下,并不知道是否有任何优势或理由使用第二个。

Any advise ? 有什么建议吗?

EDIT Thanks for the answer. 编辑感谢您的回答。 As a summary. 作为总结。 First solution is "wrong" because of the progress bar update is performed outside the EDT. 第一种解决方案是“错误的”,因为进度条更新是在EDT之外执行的。 Second solution is "correct" because the progress bar update is performed inside the EDT 第二种解决方案是“正确的”,因为进度条更新是在EDT内执行的

Now, according to the "interesting" answer of @mKorbel in my case my calculation give results in HTML text which I "insert" (see this link ). 现在,根据@mKorbel的“有趣”答案,在我的情况下,我的计算结果是HTML文本,我“插入”(见此链接 )。 My current code is the following. 我目前的代码如下。

I publish(string) and my process code looks like that 我发布(字符串),我的流程代码就是这样

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

How can I reuse @mKobel to do the same in my case. 在我的情况下,如何重复使用@mKobel来做同样的事情。 I mean he use to override table rendering in my case what renderer shall I override (jTextPane?) and how ? 我的意思是他在我的情况下使用覆盖表渲染我应该覆盖什么渲染器(jTextPane?)以及如何?

In the first code, you are calling the following line in a non-EDT (Event Dispatcher Thread) thread. 在第一个代码中,您在非EDT(事件调度程序线程)线程中调用以下行。 So it is not thread safe: 所以它不是线程安全的:

progressBar.setValue(value);

This may result in unexpected behaviour as Swing is not designed as a thread-safe library. 这可能会导致意外行为,因为Swing未设计为线程安全库。

There are different methods to perform this in the Swing way. 有不同的方法以Swing方式执行此操作。 One correct way of this is what you have done in the second post. 一个正确的方法就是你在第二篇文章中所做的。 Another would be to use publish()/process() methods, and a third method would be writing your own thread instead of SwingWorker and using SwingUtilities.invokeLater() . 另一种方法是使用publish()/process()方法,第三种方法是编写自己的线程而不是SwingWorker并使用SwingUtilities.invokeLater()

I use to monitor a long running task by updating a ProgressBar. 我用来通过更新ProgressBar来监视长时间运行的任务。 The long running task is of course performed in a Swingworker thread. 长时间运行的任务当然是在Swingworker线程中执行的。

right you can use SwingWorker in all cases for redirecting any heavy and long running task to the Background 您可以在所有情况下使用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;
    }
}

but why complicating Wwing GUI by using SwingWorker (required deepest knowledge about Java Essential Classes and Generics too), 但是为什么使用SwingWorker使Wwing GUI复杂化(需要最深入的Java Essential ClassesGenerics知识),

Basic implementations for Runnable#Thread required only invokeLater for output to the Swing GUI, and in the case that started from EDT (from Swing/AWT Listener), and without any code line contains Thread.sleep(int) then is invokeLater only adviced / required for production code Runnable#Thread基本实现只需要invokeLater输出到Swing GUI,并且在从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();
    }
}

my conclusion for real heavy and long running task you have look at Runnable#Thread (simple, easy, non_buggy and clear way), only if is your knowledge about Java & Swing very well then you can thinking about SwingWorker 我的结论是真正重型和长期运行的任务,你看看Runnable#Thread #Thread(简单,简单,非笨拙和清晰的方式),只有你对JavaSwing了解非常好,那么你才能想到SwingWorker

Your second approach is correct and is even documented in the class javadoc of the SwingWorker class. 您的第二种方法是正确的,甚至记录在SwingWorker类的类javadoc中。 The 'progress' event is fired on the EDT, so your listener updates the progress bar on the EDT. “进度”事件在EDT上被触发,因此您的侦听器会更新EDT上的进度条。 This is not the case in your first approach. 在您的第一种方法中情况并非如此。

An example of another approach (using publish/process as indicated by vizier) can be found in my answer on a previous SO question 另一种方法的例子(使用publish/process如vizier所示)可以在我之前的SO问题的答案中找到

如本所示,在第二个示例中使用worker的setProgress()是正确的:任何PropertyChangeListener都将在事件派发线程上异步通知。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM