【问题标题】:Java - JProgress bar not showing (threaded)Java - JProgress 栏未显示(线程)
【发布时间】:2023-03-10 23:43:02
【问题描述】:

我正在向程序添加一项功能,以将一些内容保存到文件中。进度由进度条(在它自己的 JFrame 中)显示,但进度条仅显示在它读取的最后一个值上。我有一个由主线程更新的全局变量,它表示已完成工作的百分比,另一个线程读取这个全局变量并相应地更新进度条。 现在当它运行时,JFrame 是空的,然后活动完成,然后进度条显示自己的完整数量。我如何让它随着它的进行更新进度(并从一开始就显示 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);
}

编辑

在这里找到解决方案: https://weblogs.java.net/blog/mkarg/archive/2010/01/03/did-you-know-swingworker-can-send-progress-status

【问题讨论】:

  • 答案已更新,以更深入地了解您的代码。如果您有任何问题,请询问。

标签: java multithreading swing jprogressbar


【解决方案1】:

有几个问题:

  • 最重要的是(我最初错过了这一点!),您不是在后台线程中执行长时间运行的代码,而是在 Swing 事件线程(EDT)中执行。我的意思是这两个 for 循环:A)for (int k=0; k&lt;blastData.size(); k++) {...} 和 B)for (int k=0; k&lt;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 调整大小时,它们不会调整您的组件大小,它们是增强或维护的皇家女巫,放置在滚动窗格中时它们完全失败,在与原始不同的所有平台或屏幕分辨率上查看时它们看起来很糟糕.
  • 您的辅助对话框窗口应该是一个 JDialog,并且可能是一个模态 JDialog,而不是另一个 JFrame。您不是在创建和显示一个新的独立程序,而是在主 GUI 窗口之外显示一个对话窗口。如果您希望在显示对话框时主 GUI 窗口不起作用,那么模态 JDialog 是可行的方法,因为它就像 JOptionPane(它是模态 JDialog 的一种形式)一样工作,并使调用窗口不- 功能可见。

对于我的一些代码示例:

【讨论】:

  • 一个非常完整的答案。
  • 您为我指明了正确的方向。会继续研究。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多