我正在向程序添加一项功能以将一些内容保存到文件中。进度由进度条(在其自己的 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 中执行。我的意思是这两个 for 循环:A)
for (int k=0; k<blastData.size(); k++) {...}
和 B)for (int k=0; k<ora.length; k++) {...}
这看起来是您加载或保存信息的代码。这将立即卡住您的 GUI。 - 同样重要的是,您正在从后台线程中执行 Swing 调用,包括设置进度条的值和设置 JFrame 的可见性,这是您永远不想做的事情,而且这在很大程度上抵消了在第一名。
- 换句话说,您正在以完全相反的方式执行所有 Swing 线程工作——从后台线程调用 Swing 并在事件线程中运行长进程。
- 相反,做相反的事情——在后台线程中完成所有长时间运行的工作,并在 EDT 上进行所有非线程安全的 Swing 调用。
- 一种方法是使用 SwingWorker,在其
doInBackground(...)
中进行加载和保存方法 - 并在取得进展时设置其进度字段..
- 然后您将在 PropertyChangeListener 中监控工作人员的进度字段,这是在 EDT 上完成的,然后使用它来设置进度条的值。
- 或者如果你必须使用自己的后台线程,那么
- 让内部类实现 Runnable,而不是扩展 Thread
- 如果您从后台线程中进行 Swing 调用,则将这些调用包装在一个 Runnable 中,并通过
SwingUtilities.invokeLater(yourRunnable)
将它们排队到 Swing 事件线程中
更多小问题:
- 您不应使用空布局和绝对定位,而应使用布局管理器。而空布局和
setBounds()
对于 Swing 新手来说,这似乎是创建复杂 GUI 的最简单和最好的方法,您创建的 Swing GUI 越多,使用它们时就会遇到越严重的困难。当 GUI 调整大小时,它们不会调整组件的大小,它们是增强或维护的皇家女巫,当放置在滚动 Pane 中时它们会完全失败,当在所有平台或与原始屏幕分辨率不同的屏幕分辨率上查看时,它们看起来很糟糕. - 您的辅助对话窗口应该是 JDialog,并且可能是模态 JDialog,而不是另一个 JFrame。您不是在创建和显示一个新的独立程序,而是在主 GUI 窗口之外显示一个对话窗口。如果您希望主 GUI 窗口在显示对话框时不起作用,那么模态 JDialog 是可行的方法,因为它就像 JOptionPane(模态 JDialog 的一种形式)一样工作,并使调用窗口非- 功能可见。
对于我的一些代码示例:
关于Java - 进度条不显示(线程),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28591205/