【问题标题】:JProgressBar update and thread schedulingJProgressBar 更新和线程调度
【发布时间】:2020-03-05 16:20:23
【问题描述】:

你好堆栈交换器,

Java Swing 中的进度条有问题。我认为我的困惑源于我对线程和 Swing 事件队列的了解不足(我不太了解 java 线程,以及 AWTEventQueue 上发生的确切情况,尽管我通常了解多线程是什么)。

上下文是按下JButton 以开始长时间计算。在计算开始之前,我在JFrame 中创建了一个进度条,我原以为会绘制它,但事实并非如此。框架出现,但它只是灰色的。在本例中,按钮上写有“clickMe”。

在“clickMe”动作监听器中,我首先在“运行”的子任务中创建并显示JFrame(我不清楚何时安排TBH)。然后,我调用doTask(),它与动作侦听器在同一个线程中运行(我认为是AWTEventThread??)。 doTask() 运行,将数字打印到控制台。与doTask() 输出混合的是进度条的迭代计数(从动作侦听器启动时开始makeProgressBar())。

因此,从输出看,进度条和AWTEventThread 都在运行,但JProgressBar GUI 中设置的值从未更新。

如何更改我的代码以更新 GUI?我尝试了解JProgressBar 教程并在网上搜索,但我认为我的问题更多是对 Java 任务的概念性理解。

这是我的代码:

package problemclass;

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;

public class ProblemClass 
{

    void progressBarButtonClick()
    {
        JFrame buttonInAFrame = new JFrame();
        JPanel buttonInAFramePanel = new JPanel();
        JButton clickMe = new JButton("Click me!");

        buttonInAFramePanel.add(clickMe);       


        clickMe.addActionListener(new ActionListener() {
            @Override
                    public void actionPerformed(ActionEvent e) 
                {
                JFrame progBarFrame = makeProgressBar();                
                doTask();
                progBarFrame.dispose();
                    }
        });


        buttonInAFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        buttonInAFrame.add(buttonInAFramePanel);
        buttonInAFrame.pack();
        buttonInAFrame.setVisible(true);
    }

    private void doTask()
    {

        for(int i = 0; i < 20000; i++)
        {
            if (i % 100 == 0) 
            {
                System.out.println("TASK iteration " + i);

                try 
                {
                    Thread.sleep(1000);
                } 
                catch (InterruptedException e) {}
            }
        }
    }

    private JFrame makeProgressBar()
    {
        JFrame progBarFrame = new JFrame();
        JPanel progBarPanel = new JPanel();

        JProgressBar progressBar = new JProgressBar();
        progBarPanel.add(progressBar);      
        progressBar.setValue(0);
            progressBar.setStringPainted(true);
            progressBar.setIndeterminate(true);


        new Thread(new Runnable() 
        {
            public void run() 
            {
                for (int i = 0; i <= 100; i++) 
                {

                    final int j = i; 
                    System.out.println("Progress Iteration " + j);

                    SwingUtilities.invokeLater(new Runnable() 
                    {
                        public void run() 
                        {
                           progressBar.setValue(j);
                        }
                    });

                    try 
                    {
                        java.lang.Thread.sleep(100);
                    }
                    catch(Exception e) { }
                }
            }
        }).start();

        progBarFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        progBarFrame.add(progBarPanel);
        progBarFrame.pack();
        progBarFrame.setVisible(true);

        return progBarFrame;
    }
    public static void main(String[] args)
    {
        EventQueue.invokeLater(() ->
        {       
            new ProblemClass().progressBarButtonClick();
        });
    }
}

【问题讨论】:

    标签: java multithreading swing jprogressbar


    【解决方案1】:
    JFrame progBarFrame = makeProgressBar();                
    doTask();
    

    不确定你到底想做什么。

    上面的代码有两个循环:

    1. 在 makePrgressBar() 方法中,您启动一​​个 Thread 并调用 SwingUtilities.invokeLater(...) 来更新进度条,这是正确的。

    2. 但是在 doTack() 中你开始另一个循环。这次您不启动线程,因此代码在 EDT 上调用,并且由于您使用 Thread.sleep,因此 EDT 将休眠,并且 GUI 不会重新绘制自身,直到整个循环完成。

    我建议你去掉 doTask() 方法,因为我不知道为什么你需要两个循环的代码块。或者如果你真的需要它,那么你还需要使用 Thread 和 invokeLater(...)。

    【讨论】:

      【解决方案2】:

      和你一样,我最近在进度条和线程方面做了一些工作,然后发疯了,直到我意识到它是如此简单。简而言之,这是我点击按钮时的代码:

       // Create 2 threads. One handles your GUI. Other does the task
              Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                // code goes here.
                //In here I choose to hide the button, display the progress bar
                            }
                });  
              t1.start();
      
             Thread t2 = new Thread(new Runnable() {
              @Override
              public void run() {
                  // code goes here.
                  //In here I get the task done, then hide the progress bar
      
                   }
                 });  
              t2.start();
      
      

      每次都像魅力一样工作。希望对您有所帮助!

      【讨论】:

      • 啊啊啊啊。现在我明白了......同时运行两个任务,这一切都有效。
      • 我更复杂的是,在 doTask() 完成后,我不得不继续使用“GUI”的东西,我通过创建另一个使用 SwingUtilities.invokeLater() 调度的线程来解决这个问题doTask() run() 方法到最后。感谢您的提示
      • 当然,很高兴为您提供帮助!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-07
      • 1970-01-01
      • 1970-01-01
      • 2023-03-19
      相关资源
      最近更新 更多