简体   繁体   English

如何在非事件派发线程中提示确认对话框

[英]How to prompt a confirmation dialog box in the middle of non event dispatching thread

I have the following fun which will be executed by non event dispatching thread. 我有以下fun ,将由非事件派发线程执行。 In the middle of thread, I want a 在线程中间,我想要一个

  1. A confirmation box pop up. 弹出确认框。 Thread suspend its execution. 线程暂停其执行。
  2. User makes a choice. 用户做出选择。
  3. Thread will get the choice and continue execution. 线程将获得选择并继续执行。

However, I find out it is not easy to do it in thread safety way, as dialog box should be shown by event dispatching thread. 但是,我发现以线程安全方式执行它并不容易,因为对话框应该由事件调度线程显示。 I try 我试试

public int fun()
{
    // The following code will be executed by non event dispatching thread.
    final int choice;
    SwingUtilities.invokeAndWait(new Runnable() {

        @Override
        public void run() {
            // Error.
            choice = JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
        }            
    });
    return choice;
}

Of course this won't work as choice is final, and I cannot assign the returned value from dialog to it. 当然这不会起作用,因为choice是最终的,我无法将对话框的返回值分配给它。

What is the correct way to achieve the above 3 objectives? 实现上述3个目标的正确方法是什么?

Have you tried: 你有没有尝试过:

public int fun()
{
    // The following code will be executed by non event dispatching thread.
    final int[] choice = new int[1];
    SwingUtilities.invokeAndWait(new Runnable() {
        @Override
        public void run() {
            // Error.
            choice[0] = JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
        }            
    });
    return choice[0];
}
public int fun() throws InterruptedException, InvocationTargetException {
    // The following code will be executed by non event dispatching thread.
    ChoiceRunnable runabble = new ChoiceRunnable();
    SwingUtilities.invokeAndWait(runabble);

    return runabble.choice;
  }

  class ChoiceRunnable implements Runnable {
    private int choice;

    public void run() {
      choice = JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
    }
  }

Contrary on the popular belief you dont need to dispatch to AWT (EventQueue) thread to show the dialog. 与流行的看法相反,您不需要调度到AWT(EventQueue)线程来显示对话框。 So just show it. 所以只是展示它。

When you do JOptionPane,showMessge() your thread (Thread.currentThread()) is going to do wait(), and the dialog will pop up. 当您执行JOptionPane时,showMessge()您的线程(Thread.currentThread())将执行wait(),并弹出对话框。 Use the result after showMessage and you're good to go. 在showMessage之后使用结果,你很高兴。

Thus: 从而:
choice = JOptionPane.showConfirmDialog(this, message, title, JOptionPane.YES_NO_OPTION);

I am a bit tired of people saying things are one way or another but without actually knowing what they are talking about. 我有些厌倦了人们说事情是这样或那样的,但实际上并不知道他们在谈论什么。 I came here wondering which thread JOptionPane should be run on but I get conflicting answers with no real evidence to support either point. 我来到这里想知道应该运行哪个线程JOptionPane但是我得到了相互矛盾的答案,没有真正的证据支持任何一点。 Well, I researched it myself and I am well satisfied with this answer so I will share. 好吧,我自己研究过,我很满意这个答案所以我会分享。

A call to one of JOptionPane's showXXXDialog() is BLOCKING until the user selects ok/cancel/etc. 对一个JOptionPane的showXXXDialog()的调用是阻塞,直到用户选择ok / cancel / etc. In general you do not put such slow blocking insturctions on the Event Dispatch Thread (EDT) as a rule because every single other GUI component will freeze. 通常,您不会在事件调度线程(EDT)上放置这种缓慢阻塞的指令,因为每个其他GUI组件都会冻结。 So, a gut instinct to not put it on the EDT is good, but it is also wrong. 所以,直觉不要把它放在EDT上是好的,但这也是错误的。 The reason is as stated by some others, the method creates GUI components and this should always be done on the EDT. 原因如其他一些人所述,该方法创建GUI组件,这应始终在EDT上完成。 But what about the blocking? 但阻止怎么样? You will notice that even if you do run it on the EDT, it works fine. 您会注意到,即使您在EDT上运行它,它也能正常工作。 The reason is found inside the source code. 原因在源代码中找到。 The JOptionPane class creates a Dialog object and then calls show() followed by dispose(), the first of which is what blocks the thread. JOptionPane类创建一个Dialog对象,然后调用show(),然后调用dispose(),第一个是阻塞线程。 If you read the comments (or javadoc), you will see that it says this about the method: 如果您阅读了注释(或javadoc),您会看到它说明了该方法:

If the dialog is modal and is not already visible, this call will not return until the dialog is hidden by calling hide or dispose. 如果对话框是模态的并且尚未显示,则在通过调用hide或dispose隐藏对话框之前,此调用将不会返回。 It is permissible to show modal dialogs from the event dispatching thread because the toolkit will ensure that another event pump runs while the one which invoked this method is blocked. 允许从事件调度线程显示模态对话框,因为工具箱将确保在调用此方法的事件被阻止时运行另一个事件泵。

So, it is perfectly safe to run JOptionPane on the EDT despite it blocking. 因此,尽管它阻塞了,但在EDT上运行JOptionPane是完全安全的。 Obviously, it is safe to call Dialog's show() method off the EDT but the same is not true for JOptionPane because its methods are creating GUI components, adding listeners, accessing other containers when modal and blocking input to them, etc. You do not want all of this done off the EDT because it is not thread-safe and there could be problems. 显然,从EDT调用Dialog的show()方法是安全的,但是对于JOptionPane也是如此,因为它的方法是创建GUI组件,添加监听器,在模态时访问其他容器并阻止对它们的输入等。你不是希望所有这些都在EDT之外完成,因为它不是线程安全的,并且可能存在问题。 Admittedly, I have never seen problems when using JOptionPane off the EDT and so the chances seem low, but they are most certainly possible. 不可否认,在EDT上使用JOptionPane时我从来没有遇到过问题,因此机会似乎很低,但它们肯定是可能的。 Passing in a null for the container of the dialog and only giving immutable objects (like Strings) as arguments to the fields will significantly reduce (maybe even eliminate as far as I know) the chance of something bad happening because all relevant GUI components are made and accessed within the same thread while they are not visible. 为对话框的容器传入一个null并且只给出不可变对象(比如字符串)作为字段的参数将显着减少(甚至可能消除据我所知)发生错误的可能性,因为所有相关的GUI组件都是并且在它们不可见时在同一个线程中访问。 But, you should just be safe and put it on the EDT. 但是,你应该安全并把它放在EDT上。 It is not that difficult to call SwingUtilities.invokeAndWait(). 调用SwingUtilities.invokeAndWait()并不困难。

May be I do not understand the question, but I do not get the answers either... if you want the calling thread to block on the call to fun(), why display the JOptionPane in a new (parallel) Thread? 可能是我不明白这个问题,但是我也没有得到答案......如果你想让调用线程阻塞对fun()的调用,为什么要在一个新的(并行)线程中显示JOptionPane? Shouldn't this be sufficient? 这不应该足够吗?

public int fun() {
    return JOptionPane.showConfirmDialog(null, message, title, JOptionPane.YES_NO_OPTION);
} 

PS How do you define a non event dispatching thread? PS如何定义非事件调度线程?

Launch the JOptionPane from the EDT, and pass the value back with a FutureTask. 从EDT启动JOptionPane,并使用FutureTask传回值。

public int fun()
{
    FutureTask<Integer> dialogTask = new FutureTask<Integer>(new Callable<Integer>() 
    {
        @Override public Integer call() 
        {
            return JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
        }
    });
    SwingUtilities.invokeLater(dialogTask);
    int choice  = dialogTask.get();
}

Credit to jtahlborn How to pass results from EDT back to a different thread? 感谢jtahlborn 如何将EDT的结果传递回另一个线程?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM