Java - 进度条不显示(线程化)
Java - JProgress bar not showing (threaded)
我正在向程序添加一项功能以将一些内容保存到文件中。进度由进度条(在其自己的 JFrame 中)显示,但进度条仅显示在它读取的最后一个值上。我有一个由主线程更新的全局,它表示已完成工作的百分比,另一个线程读取这个全局并相应地更新进度条。
现在当它运行时,JFrame 是空的,然后 activity 完成,然后进度条显示完整的数量。我如何让它随着进度更新(并从一开始就显示 JProgressbar)?这是我的代码:
public class GenomeAnnotator{
private JProgressBar csvProgressBar;
private JFrame csvSaveLoadFrame; //for the progress bar
private Container csvCon;
private double csvPercentSaved; //% of work completed
public JFrame m_frame; //main program frame
....
public static void main(String[] args){
...
showGUI();
...
}
public void showGUI(){
...
JMenu file = new JMenu("File");
JMenu exptann = new JMenu("Export annotation..);
JMenuItem exptcsv = newJMenuItem("CSV format");
exptcsv.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
..determine output file + checks...
System.out.println("Writing to .csv file......");
csvSaveLoadFrame = new JFrame("Saving to csv file..");
csvProgressBar =new JProgressBar(0,100);
csvSaveLoadFrame.setSize(300,100);
csvCon = csvSaveLoadFrame.getContentPane();
csvCon.setLayout(null);
csvProgressBar.setBounds(10,10,280,20);
csvCon.add(csvProgressBar);
csvSaveLoadFrame.setResizable(false);
csvSaveLoadFrame.setVisible(true);
ORF [] ora= orfPanel.getAcceptedOrfs();
int val;
double toload = blastData.size() + ora.length; //how much work
double loaded=0.0; //how much work completed
/*Thread that will read % value from global and update prog. bar*/
Thread progressBarMover = new Thread() {
@Override
public void run() {
int previous=0;
while(csvPercentSaved<100){
csvProgressBar.setValue((int)csvPercentSaved);
//tried putting a sleep() in here when testing
//values from global is read successfully
}
}
System.out.println("Thread done!");
csvPercentSaved = 0; //reset value when done
csvSaveLoadFrame.setVisible(false);
}
};
progressBarMover.start();
for (int k=0; k<blastData.size(); k++) {
..do output work...
loaded+=1; //update % values
csvPercentSaved = (loaded/toload)*100;
val = (int)csvPercentSaved;
System.out.println("main complete "+val+"%");
}
for (int k=0; k<ora.length; k++) {
...do more ouput work...
loaded+=1;
csvPercentSaved = (loaded/toload)*100; //update % value
val = (int)csvPercentSaved;
System.out.println("main complete "+val+"%");
}
System.out.println("Output file finished!");
csvPercentSaved = 100;
}
});
exptann.add(exptcsv);
file.add(exptann);
}
编辑
有几个问题:
- 最重要的(我最初错过了这一点!),您不是在后台线程中而是在 Swing 事件线程 EDT 中执行长 运行ning 代码。我的意思是这两个 for 循环:A)
for (int k=0; k<blastData.size(); k++) {...}
和 B) for (int k=0; k<ora.length; k++) {...}
看起来是您加载或保存信息的代码。这将立即冻结您的 GUI。
- 同样重要的是,您正在从后台线程中执行 Swing 调用,包括设置进度条的值和设置 JFrame 的可见性,这是您永远不想做的事情,而且这在很大程度上抵消了使用后台的好处线程放在首位。
- 换句话说,您正在完全反向执行所有 Swing 线程工作——从后台线程进行 Swing 调用,然后运行在事件线程中进行长处理。
- 相反,做相反的事情——在后台线程中完成所有长时间的 运行ning 工作,并在 EDT 上进行所有非线程安全的 Swing 调用。
- 一种方法是使用 SwingWorker,在其
doInBackground(...)
方法中进行加载和保存
- 并在取得进展时设置其进度字段..
- 然后您将在 PropertyChangeListener 中监视工作人员的进度字段,这是在 EDT 上完成的,然后使用它来设置进度条的值。
- 或者如果你必须使用自己的后台线程,那么
- 让内部 class 实现 Runnable,而不是扩展 Thread
- 如果您从后台线程中进行 Swing 调用,则将这些调用包装在 Runnable 中并通过
SwingUtilities.invokeLater(yourRunnable)
将它们排队到 Swing 事件线程中
更多小问题:
- 您不应使用空布局和绝对定位,而应使用布局管理器。虽然空布局和
setBounds()
对于 Swing 新手来说似乎是创建复杂 GUI 的最简单和最好的方法,但您创建的 Swing GUI 越多,您在使用它们时就会 运行 遇到更严重的困难。它们不会在 GUI 调整大小时调整组件的大小,它们是皇家 来增强或维护的,当放置在滚动窗格中时它们会完全失败,当在所有平台或不同的屏幕分辨率上查看时它们看起来很糟糕从原来的。
- 您的辅助对话框 window 应该是 JDialog,并且可能是模态 JDialog,而不是另一个 JFrame。您不是在创建和显示一个新的独立程序,而是在主 GUI window 之外显示一个对话框 window。如果您希望在显示对话框时主 GUI window 不起作用,那么模态 JDialog 是可行的方法,因为它的工作方式就像 JOptionPane(模态 JDialog 的一种形式)一样,并且使调用 window 在可见时不起作用。
对于我的一些代码示例:
- How do I make my SwingWorker example work properly?
- For a lot more of my examples
我正在向程序添加一项功能以将一些内容保存到文件中。进度由进度条(在其自己的 JFrame 中)显示,但进度条仅显示在它读取的最后一个值上。我有一个由主线程更新的全局,它表示已完成工作的百分比,另一个线程读取这个全局并相应地更新进度条。 现在当它运行时,JFrame 是空的,然后 activity 完成,然后进度条显示完整的数量。我如何让它随着进度更新(并从一开始就显示 JProgressbar)?这是我的代码:
public class GenomeAnnotator{
private JProgressBar csvProgressBar;
private JFrame csvSaveLoadFrame; //for the progress bar
private Container csvCon;
private double csvPercentSaved; //% of work completed
public JFrame m_frame; //main program frame
....
public static void main(String[] args){
...
showGUI();
...
}
public void showGUI(){
...
JMenu file = new JMenu("File");
JMenu exptann = new JMenu("Export annotation..);
JMenuItem exptcsv = newJMenuItem("CSV format");
exptcsv.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
..determine output file + checks...
System.out.println("Writing to .csv file......");
csvSaveLoadFrame = new JFrame("Saving to csv file..");
csvProgressBar =new JProgressBar(0,100);
csvSaveLoadFrame.setSize(300,100);
csvCon = csvSaveLoadFrame.getContentPane();
csvCon.setLayout(null);
csvProgressBar.setBounds(10,10,280,20);
csvCon.add(csvProgressBar);
csvSaveLoadFrame.setResizable(false);
csvSaveLoadFrame.setVisible(true);
ORF [] ora= orfPanel.getAcceptedOrfs();
int val;
double toload = blastData.size() + ora.length; //how much work
double loaded=0.0; //how much work completed
/*Thread that will read % value from global and update prog. bar*/
Thread progressBarMover = new Thread() {
@Override
public void run() {
int previous=0;
while(csvPercentSaved<100){
csvProgressBar.setValue((int)csvPercentSaved);
//tried putting a sleep() in here when testing
//values from global is read successfully
}
}
System.out.println("Thread done!");
csvPercentSaved = 0; //reset value when done
csvSaveLoadFrame.setVisible(false);
}
};
progressBarMover.start();
for (int k=0; k<blastData.size(); k++) {
..do output work...
loaded+=1; //update % values
csvPercentSaved = (loaded/toload)*100;
val = (int)csvPercentSaved;
System.out.println("main complete "+val+"%");
}
for (int k=0; k<ora.length; k++) {
...do more ouput work...
loaded+=1;
csvPercentSaved = (loaded/toload)*100; //update % value
val = (int)csvPercentSaved;
System.out.println("main complete "+val+"%");
}
System.out.println("Output file finished!");
csvPercentSaved = 100;
}
});
exptann.add(exptcsv);
file.add(exptann);
}
编辑
有几个问题:
- 最重要的(我最初错过了这一点!),您不是在后台线程中而是在 Swing 事件线程 EDT 中执行长 运行ning 代码。我的意思是这两个 for 循环:A)
for (int k=0; k<blastData.size(); k++) {...}
和 B)for (int k=0; k<ora.length; k++) {...}
看起来是您加载或保存信息的代码。这将立即冻结您的 GUI。 - 同样重要的是,您正在从后台线程中执行 Swing 调用,包括设置进度条的值和设置 JFrame 的可见性,这是您永远不想做的事情,而且这在很大程度上抵消了使用后台的好处线程放在首位。
- 换句话说,您正在完全反向执行所有 Swing 线程工作——从后台线程进行 Swing 调用,然后运行在事件线程中进行长处理。
- 相反,做相反的事情——在后台线程中完成所有长时间的 运行ning 工作,并在 EDT 上进行所有非线程安全的 Swing 调用。
- 一种方法是使用 SwingWorker,在其
doInBackground(...)
方法中进行加载和保存 - 并在取得进展时设置其进度字段..
- 然后您将在 PropertyChangeListener 中监视工作人员的进度字段,这是在 EDT 上完成的,然后使用它来设置进度条的值。
- 或者如果你必须使用自己的后台线程,那么
- 让内部 class 实现 Runnable,而不是扩展 Thread
- 如果您从后台线程中进行 Swing 调用,则将这些调用包装在 Runnable 中并通过
SwingUtilities.invokeLater(yourRunnable)
将它们排队到 Swing 事件线程中
更多小问题:
- 您不应使用空布局和绝对定位,而应使用布局管理器。虽然空布局和
setBounds()
对于 Swing 新手来说似乎是创建复杂 GUI 的最简单和最好的方法,但您创建的 Swing GUI 越多,您在使用它们时就会 运行 遇到更严重的困难。它们不会在 GUI 调整大小时调整组件的大小,它们是皇家 来增强或维护的,当放置在滚动窗格中时它们会完全失败,当在所有平台或不同的屏幕分辨率上查看时它们看起来很糟糕从原来的。 - 您的辅助对话框 window 应该是 JDialog,并且可能是模态 JDialog,而不是另一个 JFrame。您不是在创建和显示一个新的独立程序,而是在主 GUI window 之外显示一个对话框 window。如果您希望在显示对话框时主 GUI window 不起作用,那么模态 JDialog 是可行的方法,因为它的工作方式就像 JOptionPane(模态 JDialog 的一种形式)一样,并且使调用 window 在可见时不起作用。
对于我的一些代码示例:
- How do I make my SwingWorker example work properly?
- For a lot more of my examples