如何从线程更新Java GUI?

3
 private void StartActionPerformed(java.awt.event.ActionEvent evt) {

        Queue queue=new Queue();
        int target=Integer.parseInt(Target.getText());
        String path=Path.getText();
        final Producer p=new Producer(queue, target);
        Consumer c=new Consumer(queue);
        p.start();
        c.start();

        while(p.finish !=true)
        {
          Runnable r = new Runnable() {
            public void run() {
              ProgressPrecent.setValue(Producer.ProgressPercent);
            }
          };
          if(EventQueue.isDispatchThread()) {
            r.run();
          }
          else {
            EventQueue.invokeLater(r);
          }
       }
 }

我有两个类,它们共享一个队列。其中一个是生产者,生产到达目标后,另一个消费这些元素。这两个类都继承了Thread类。我想向用户显示进度百分比,但是这会冻结我的GUI,我该怎么办?


尝试使用 SwingUtilities.invokeLater(new Runnable()) - devsnd
我已经做了,但它还是不起作用? - Alyafey
如果你需要帮助,你必须更具体地说明问题。你可能还想改进你的问题,以解释你的代码。阅读常见问题解答将有所帮助。 - devsnd
2
@sarcan:并不是每个人都以英语为母语。你不应该因为语言而歧视某个人,因为这并不是实际问题所在。关键在于解释已经尝试过什么等等... - devsnd
我之前在C#中遇到了这个问题,但是已经解决了。你可以在这里查看问题:https://dev59.com/82rWa4cB1Zd3GeqP8jyU - Alyafey
这个问题的答案中引用了几个有用的例子。 - trashgod
2个回答

6
我认为你需要将整个while循环放入一个线程中。否则,该循环将阻塞ActionEvent并导致UI冻结。
可以尝试像这样实现:
new Thread(){
    public void run(){
        while(!p.finish){
           SwingUtilities.invokeLater(new Runnable(){
             public void run(){
               ProgressPrecent.setValue(Producer.ProgressPercent);
             }
           }); 
           try{
              Thread.sleep(100);
           }catch(...){}
        }
    }
}.start();

1
你将如何同步访问 ProgressPrecent - trashgod
刚刚试图指出循环将导致初始事件阻塞。我更新了我的答案。 - morja
如果没有其他了解,调用ProgressPrecent.setValue将是危险的。所有UI更新必须在事件分派线程中执行。虽然从理论上讲你的答案是正确的,但它会产生误导,并且会引起“为什么我得到绘画瑕疵”的问题,随后是“但我被告知要这样做”的回答... - MadProgrammer

6
  • Worker Threads 默认情况下不会调用 EventDispatchThread,如果你在 Swing 并发 中遇到问题,需要解决。

  • 所有对 Swing GUI 的更新都必须在 EDT 上进行。

  • Runnable 可能是正确的方法,但是 ProgressPrecent.setValue(Producer.ProgressPercent); 必须被包装在 invokeLater 中。

code

SwingUtilities.invokeLater(new Runnable(){
    public void run(){
        ProgressPrecent.setValue(Producer.ProgressPercent);
    }
}); 
  • 删除对EDT的测试,

代码行

if(EventQueue.isDispatchThread()) {
   r.run();
}

Workers Thread默认情况下不会调用EDT,因此无论是从EDT启动还是测试isDispatchThread()都没有意义。

  • 永远不要在Swing Listeners中使用Thread.sleep(int),因为这会导致Swing GUI冻结。

  • 我认为您也可以使用SwingWorker来完成这项工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接