java 来自 SwingWorker 的 jProgressBar 更新
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10773552/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
jProgressBar update from SwingWorker
提问by HpTerm
I use to monitor a long running task by updating a ProgressBar. The long running task is of course performed in a Swingworker thread.
我过去常常通过更新 ProgressBar 来监视长时间运行的任务。长时间运行的任务当然是在 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 ? 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.
我的问题是:我的第一个代码是否可以接受,还是我应该出于任何原因使用 setProgress?我发现第二个代码更复杂,就我而言,不知道使用第二个代码是否有任何优势或理由。
Any advise ?
有什么建议吗?
EDITThanks for the answer. As a summary. First solution is "wrong" because of the progress bar update is performed outside the EDT. Second solution is "correct" because the progress bar update is performed inside the EDT
编辑感谢您的回答。作为总结。第一个解决方案是“错误的”,因为进度条更新是在 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). My current code is the following.
现在,根据@mKorbel 的“有趣”答案,在我的情况下,我的计算给出了我“插入”的 HTML 文本结果(请参阅此链接)。我当前的代码如下。
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. I mean he use to override table rendering in my case what renderer shall I override (jTextPane?) and how ?
在我的情况下,我如何重用 @mKobel 来做同样的事情。我的意思是,在我的情况下,他用来覆盖表格渲染,我应该覆盖什么渲染器(jTextPane?)以及如何覆盖?
采纳答案by Hakan Serce
In the first code, you are calling the following line in a non-EDT (Event Dispatcher Thread) thread. So it is not thread safe:
在第一个代码中,您在非 EDT(事件调度程序线程)线程中调用以下行。所以它不是线程安全的:
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. 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()
.
有不同的方法可以以 Swing 方式执行此操作。一种正确的方法是您在第二篇文章中所做的。另一种publish()/process()
方法是使用方法,第三种方法是编写自己的线程而不是SwingWorker
使用SwingUtilities.invokeLater()
.
回答by mKorbel
I use to monitor a long running task by updating a ProgressBar. The long running task is of course performed in a Swingworker thread.
我过去常常通过更新 ProgressBar 来监视长时间运行的任务。长时间运行的任务当然是在 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),
但为什么用Wwing GUI复杂SwingWorker
(需要约最深的知识Java Essential Classes
和Generics
太)
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 监听器)开始的情况下,并且没有任何代码行包含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
(简单、容易、非错误和清晰的方式),只有当你对Java
&Swing
非常了解时,你才能思考SwingWorker
回答by trashgod
As shown in this example, your use of the worker's setProgress()
in your second example is correct: any PropertyChangeListener
will be notified asynchronously on the event dispatch thread.
如本示例所示,您setProgress()
在第二个示例中使用工作PropertyChangeListener
线程是正确的:任何将在事件调度线程上异步通知。
回答by Robin
Your second approach is correct and is even documented in the class javadoc of the SwingWorker
class. The 'progress' event is fired on the EDT, so your listener updates the progress bar on the EDT.
This is not the case in your first approach.
您的第二种方法是正确的,甚至记录在该类的 javadocSwingWorker
类中。'progress' 事件在 EDT 上触发,因此您的侦听器会更新 EDT 上的进度条。在您的第一种方法中,情况并非如此。
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 问题的回答中找到