秋千:我如何运行AWT线程工作,但窗户被奠定了以后? [英] Swing: How do I run a job from AWT thread, but after a window was layed out?

查看:198
本文介绍了秋千:我如何运行AWT线程工作,但窗户被奠定了以后?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的完整的GUI线程AWT内部运行,因为我开始使用主窗口 SwingUtilities.invokeAndWait(...)

现在我有一个JDialog具有只是显示一个JLabel,这表明,在一定的工作正在进行中,作业结束后关闭对话框。

<强>问题是:不显示的标签。这工作看起来的JDialog充分奠定出之前启动。

当我只是让对话框打开,而不必等待工作和关闭,标签的的显示。

对话框做的最后一件事在它的构造函数是调用setVisible(真)。结果
世事如重新验证()重绘(),...不帮助的。

当我开始为监测工作线程,并等待它使用

即使 someThread.join()没有帮助,因为当前线程(这是在AWT线程),由受阻加入,我猜。

用的JFrame更换的JDialog没有帮助的。

那么,是什么概念错误有什么看法?或者,我可以管理它做某项工作的之后的就保证了一个JDialog(或JFrame的)完全奠定了呢?

的我想要达到简化算法:


  • 创建的JDialog的子类

  • 确保它和它的内容是完全奠定出

  • 启动进程并等待它完成(螺纹与否,并不重要)

  • 关闭对话

我设法写的可重复性测试用例:

修改从答案的问题现在已经解决:
这个用例的确实显示标签,但它未能关闭
经过模拟流程,因为对话的方式的。

 进口java.awt中的*。
进口的javax.swing *。公共类_DialogTest2 {  公共静态无效的主要(字串[] args)
      抛出异常{
    SwingUtilities.invokeAndWait(新的Runnable(){
      最终的JLabel的JLabel =新的JLabel(请稍候...);      @覆盖
      公共无效的run(){
        JFrame的myFrame =新的JFrame(主框架);
        myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        myFrame.setSize(750,500);
        myFrame.setLocationRelativeTo(NULL);
        myFrame.setVisible(真);        的JDialog D =新的JDialog(myFrame,我在等待);
        d.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);        d.add(JLabel的);
        d.setSize(300,200);
        d.setLocationRelativeTo(NULL);
        d.setVisible(真);        SwingUtilities.invokeLater(Runnable的新(){          @覆盖
          公共无效的run(){
            尝试{
              视频下载(3000); //模拟过程
              jLabel.setText(完成);
            }赶上(InterruptedException的前){
            }
          }        });        d.setVisible(假);
        d.dispose();        myFrame.setVisible(假);
        myFrame.dispose();
      }
    });
  }
}


解决方案

试试这个:

 包javaapplication3;进口javax.swing.JDialog中;
进口javax.swing.JFrame中;
进口javax.swing.JLabel中;
进口javax.swing.SwingUtilities中;公共类主要{公共静态无效的主要(字串[] args)
        抛出异常{
    SwingUtilities.invokeAndWait(新的Runnable(){        最终的JLabel的JLabel =新的JLabel(请稍候...);        @覆盖
        公共无效的run(){
            JFrame的myFrame =新的JFrame(主框架);
            myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            myFrame.setSize(750,500);
            myFrame.setLocationRelativeTo(NULL);
            myFrame.setVisible(真);            的JDialog D =新的JDialog(myFrame,我在等待);            d.add(JLabel的);
            d.setSize(300,200);
            d.setLocationRelativeTo(NULL);
            d.setVisible(真);            新主题(新的Runnable接口(){                @覆盖
                公共无效的run(){                公共无效的run(){
                    尝试{
                        视频下载(3000); //模拟过程
                        jLabel.setText(完成); //这里:应在美国东部时间完成!
                    }赶上(InterruptedException的前){
                    }
                }
            })。开始();
        }
    });
}
}

这一件作品,但它是不正确的。我要怎么回事解释。

的main()法主线程启动了。所有Swing相关code应该在美国东部时间线程来完成。这就是为什么您正在使用(正确) SwingUtilities.invokeAndWait(...)。到目前为止好。

不过应该​​在美国东部时间没有长时间运行的任务。由于Swing是单线程的任何长期运行的进程将阻止EDT。所以,你的code Thread.wait(...)不应该在美国东部时间执行。这是我修改。我裹在另一个线程调用。因此,这是惯用的长期运行的任务处理的摇摆。我用Thread类的简洁,但我真的建议与的SwingWorker 线程去。

和非常重要的:我在做有一个错误在preceding例子。请参阅使用这里注释的行?这又是摆一个线程违反规则。内螺纹code上的 的EDT之外,因此它应该永远无法触摸摇摆。所以这code不是与Swing一个线程规则是正确的。这不是冻结GUI是安全的。

如何纠正呢?简单。你应该换你的呼叫在另一个线程,并把它放在EDT队列。所以,正确的code应该是这样的:

  SwingUtilities.invokeLater(Runnable的新(){            公共无效的run(){
                jLabel.setText(完成);
            }
        });

修改:这个问题触及了很多摇摆的相关问题。不能解释他们一下子......但这里是多了一个片段,它做什么你想要的:

 公共静态无效的主要(字串[] args)
        抛出异常{
    SwingUtilities.invokeAndWait(新的Runnable(){        最终的JFrame myFrame =新的JFrame(主框架);
        最终的JLabel的JLabel =新的JLabel(请稍候...);
        最终的JDialog D =新的JDialog(myFrame,我在等待);        @覆盖
        公共无效的run(){
            myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            myFrame.setSize(750,500);
            myFrame.setLocationRelativeTo(NULL);
            myFrame.setVisible(真);            d.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);            d.add(JLabel的);
            d.setSize(300,200);
            d.setLocationRelativeTo(NULL);
            新主题(新的Runnable接口(){                @覆盖
                公共无效的run(){
                    尝试{
                        视频下载(3000); //模拟过程
                        的System.out.println(后);
                        SwingUtilities.invokeLater(Runnable的新(){                            公共无效的run(){
                                d.setVisible(假);
                                d.dispose();                                myFrame.setVisible(假);
                                myFrame.dispose();
                            }
                        });
                    }赶上(InterruptedException的前){
                    }
                }
            })。开始();
            d.setVisible(真);        }
    });
}

要总结一下:


  • 所有的Swing相关code必须在美国东部时间运行

  • 所有长期运行code不应该在美国东部时间运行

  • code可以使用美国东部时间 SwingUtilities类中运行。 ...

    • invokeAndWait() - 正如它的名字说,调用是同步的,

    • 的invokeLater() - 调用code'的某个时候,而是立即返回


  • 如果您是在美国东部时间,想调用另一个线程code,比你可以:

    • 创建一个新的(传递一个Runnable新的主题或覆盖它的的start()方法),并开始它,

    • 创建其中有一些额外的一个新的的SwingWorker 主题。

    • 可能使用的其他任何线程机制(例如执行程序线程)。


典型的GUI场景包括:


  1. 创建GUI组件,

  2. 连接最多属性更改侦听器,

  3. 执行code相关用户操作(即运行属性更改侦听器),

  4. 运行可能费时的任务,

  5. 更新GUI状态,

1,2,3和4上运行的 EDT 的。 4.不应该。有许多方法来写正确的线程code。最麻烦的是用它来了与Java的早期版本类。如果一个人做它天真,资源浪费(线程太多捉迷藏一次)。同时更新GUI很麻烦。使用SwingWorker类是缓解问题的一点。它保证可正常使用,同时启动,运行和更新GUI(每个人都有,你可以重写,并确保它运行在正确的线程专用的方法)。

My complete GUI runs inside the AWT thread, because I start the main window using SwingUtilities.invokeAndWait(...).

Now I have a JDialog which has just to display a JLabel, which indicates that a certain job is in progress, and close that dialog after the job was finished.

The problem is: the label is not displayed. That job seems to be started before JDialog was fully layed-out.

When I just let the dialog open without waiting for a job and closing, the label is displayed.

The last thing the dialog does in its ctor is setVisible(true).
Things such as revalidate(), repaint(), ... don't help either.

Even when I start a thread for the monitored job, and wait for it using someThread.join() it doesn't help, because the current thread (which is the AWT thread) is blocked by join, I guess.

Replacing JDialog with JFrame doesn't help either.

So, is the concept wrong in general? Or can I manage it to do certain job after it is ensured that a JDialog (or JFrame) is fully layed-out?

Simplified algorithm of what I'm trying to achieve:

  • Create a subclass of JDialog
  • Ensure that it and its contents are fully layed-out
  • Start a process and wait for it to finish (threaded or not, doesn't matter)
  • Close the dialog

I managed to write a reproducible test case:

EDIT Problem from an answer is now addressed: This use case does display the label, but it fails to close after the "simulated process", because of dialog's modality.

import java.awt.*;
import javax.swing.*;

public class _DialogTest2 {

  public static void main(String[] args)
      throws Exception {
    SwingUtilities.invokeAndWait(new Runnable() {
      final JLabel jLabel = new JLabel("Please wait...");

      @Override
      public void run() {
        JFrame myFrame = new JFrame("Main frame");
        myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        myFrame.setSize(750, 500);
        myFrame.setLocationRelativeTo(null);
        myFrame.setVisible(true);

        JDialog d = new JDialog(myFrame, "I'm waiting");
        d.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);

        d.add(jLabel);
        d.setSize(300, 200);
        d.setLocationRelativeTo(null);
        d.setVisible(true);

        SwingUtilities.invokeLater(new Runnable() {

          @Override
          public void run() {
            try {
              Thread.sleep(3000); // simulate process
              jLabel.setText("Done");
            } catch (InterruptedException ex) {
            }
          }

        });

        d.setVisible(false);
        d.dispose();

        myFrame.setVisible(false);
        myFrame.dispose();
      }
    });
  }
}

解决方案

Try this:

package javaapplication3;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class Main {

public static void main(String[] args)
        throws Exception {
    SwingUtilities.invokeAndWait(new Runnable() {

        final JLabel jLabel = new JLabel("Please wait...");

        @Override
        public void run() {
            JFrame myFrame = new JFrame("Main frame");
            myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            myFrame.setSize(750, 500);
            myFrame.setLocationRelativeTo(null);
            myFrame.setVisible(true);

            JDialog d = new JDialog(myFrame, "I'm waiting");

            d.add(jLabel);
            d.setSize(300, 200);
            d.setLocationRelativeTo(null);
            d.setVisible(true);

            new Thread(new Runnable() {

                @Override
                public void run() {

                public void run() {
                    try {
                        Thread.sleep(3000); // simulate process
                        jLabel.setText("Done");   // HERE: should be done on EDT!
                    } catch (InterruptedException ex) {
                    }
                }
            }).start();


        }
    });
}
}

This one works, but it's not correct. I'll explain whats going on.

Your main() method starts out in 'main' thread. All Swing related code should be done on EDT thread. And this is why You are using (correctly) SwingUtilities.invokeAndWait(...). So far so good.

But there should be no long running tasks on EDT. Since Swing is single threaded any long running processes will block the EDT. So your code Thread.wait(...) should never be executed on EDT. And this is my modification. I wrapped the invocation in another thread. So this is idiomatic long running task handling for Swing. I used Thread class for brevity, but I'd really recommend going with SwingWorker thread.

And very important: I'm making one error in preceding example. See the line with "HERE" comment? This is another Swing one-thread rule violation. Code inside the thread is running outside EDT, so it should never touch Swing. So this code is not a correct with Swing one-thread rule. It's not safe from freezing GUI.

How to correct this? Simple. You should wrap your call in another thread and put it on EDT queue. So correct code should look like:

    SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                jLabel.setText("Done");
            }
        });

EDIT: This question is touching a lot Swing related issues. Can't explain them all at once... But here is one more snippet, which does what You want:

public static void main(String[] args)
        throws Exception {
    SwingUtilities.invokeAndWait(new Runnable() {

        final JFrame myFrame = new JFrame("Main frame");
        final JLabel jLabel = new JLabel("Please wait...");
        final JDialog d = new JDialog(myFrame, "I'm waiting");

        @Override
        public void run() {
            myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            myFrame.setSize(750, 500);
            myFrame.setLocationRelativeTo(null);
            myFrame.setVisible(true);

            d.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);

            d.add(jLabel);
            d.setSize(300, 200);
            d.setLocationRelativeTo(null);
            new Thread(new Runnable() {

                @Override
                public void run() {
                    try {
                        Thread.sleep(3000); // simulate process
                        System.out.println("After");
                        SwingUtilities.invokeLater(new Runnable() {

                            public void run() {


                                d.setVisible(false);
                                d.dispose();

                                myFrame.setVisible(false);
                                myFrame.dispose();
                            }
                        });
                    } catch (InterruptedException ex) {
                    }
                }
            }).start();
            d.setVisible(true);

        }
    });
}

To sum up:

  • All Swing related code must run on EDT
  • All long running code must not run on EDT
  • Code can be run on EDT using SwingUtilities....
    • invokeAndWait() - as the name says, call is synchronous,
    • invokeLater() - invoke the code 'sometime', but return immediately
  • If you are on EDT and want to invoke code on another thread, than you can:
    • Create a new Thread (pass a Runnable to new Thread or override it's start() method) and start it ,
    • Create a new SwingWorker thread which has some extras.
    • Possibly use any other threading mechanism (for example Executor threads).

The typical GUI scenario involves:

  1. Creating GUI components,
  2. Wiring up property change listeners,
  3. Executing code related to user actions (i.e. running property change listeners),
  4. Running possibly time consuming tasks,
  5. Updating GUI state,

1., 2., 3. and 4. run on EDT. 4. should not. There are many ways to write proper threading code. The most cumbersome is using Thread class which came with early versions of Java. If one does it naively, resources can be wasted (too many threads runnning at once). Also updating GUI is cumbersome. Using SwingWorker is alleviates the problem a little. It's guaranteed to behave properly while starting, running and updating GUI (each has a dedicated method which you can override and be sure it runs on proper thread).

这篇关于秋千:我如何运行AWT线程工作,但窗户被奠定了以后?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆