2010-10-17 1 views
6
// This is supposed to show a modal dialog and then hide it again. In practice, 
// this works about 75% of the time, and the other 25% of the time, the dialog 
// stays visible. 
// This is on Ubuntu 10.10, running: 
// OpenJDK Runtime Environment (IcedTea6 1.9) (6b20-1.9-0ubuntu1) 

// This always prints 
// setVisible(true) about to happen 
// setVisible(false) about to happen 
// setVisible(false) has just happened 
// even when the dialog stays visible. 

package modalproblemdemo; 

import java.awt.Frame; 
import javax.swing.JDialog; 
import javax.swing.SwingUtilities; 

public class Main { 
    public static void main(String[] args) { 
     final Dialogs d = new Dialogs(); 
     new Thread() { 
      @Override 
      public void run() { 
       d.show(); 
       d.hide(); 
      } 
     }.start(); 
    } 

    static class Dialogs { 
     final JDialog dialog; 

     public Dialogs() { 
      dialog = new JDialog((Frame) null, "Hello World", /*modal*/ true); 
      dialog.setSize(400, 200); 
     } 

     public void show() { 
      SwingUtilities.invokeLater(new Runnable() { public void run() { 
       dialog.setLocationRelativeTo(null); 
       System.out.println("setVisible(true) about to happen"); 
       dialog.setVisible(true); 
      }}); 
     } 

     public void hide() { 
      SwingUtilities.invokeLater(new Runnable() { public void run() { 
       System.out.println("setVisible(false) about to happen"); 
       dialog.setVisible(false); 
       System.out.println("setVisible(false) has just happened"); 
      }}); 
     } 
    } 
} 
+0

Можете ли вы сделать диалог не модальным? Тогда вы можете закрыть его, не вникая в Threads. –

+0

См. Также: http://stackoverflow.com/questions/3886264/non-blocking-modal-swing-progress-dialog – Zarkonnen

ответ

2

Итак, выясняется, что то, что происходит, когда вы показываете()/setVisible (true), модальное диалоговое окно заключается в том, что во время вызова show/setVisible выполняется второй цикл отправки событий. Что имеет смысл, когда вы об этом знаете. Имея это в виду, я закончил с этим кодом:

public void runBlockingTask(final String taskName, final BlockingTask bt) { 
    SwingUtilities.invokeLater(new Runnable() { public void run() { 
     new Thread("Worker Thread: " + taskName) { 
      @Override 
      public void run() { 
       bt.run(); 
       progressDialog.setVisible(false); 
      } 
     }.start(); 
    }}); 
    // NB This causes the event dispatch loop to be run inside this call, 
    // which is why we need to put everything after setVisible into an 
    // invokeLater. 
    progressDialog.setVisible(true); 
} 
+0

+1 для ответа на свой вопрос два года спустя. Есть ли значок для посвящения? –

4

Это явно какой-то вид гонки. Я не думаю, что это так же просто, как ответ Эрика Робертсона. Код Dialog show() довольно сложный, он содержит специальную логику для вызова из потока отправки событий, а также отправляет события в очередь событий. Возможно, порядок, в котором размещены события, каким-то образом затронуты задержками потоков.

Возможно, вам необходимо SwingUtilities.invokeAndWait(), таким образом вы гарантируете, что setVisible(true) закончил выполнение, прежде чем позвонить по телефону setVisible(false). Как указано в пропущенном заголовке, invokeAndWait будет блокироваться до тех пор, пока диалог не будет закрыт.

И зачем вам это нужно?

EDIT: Это мой сценарий того, что происходит:

  1. вы звоните d.show(), что сообщения setVisible(true) событие
  2. нить ставится так спать планировщиком и пинки EDT в и начинает выполнять первое событие
  3. EDT выталкивается до завершения первой задачи и отправляет фактическое событие, которое показывает диалог
  4. Ваш поток выполняет d.hide(), который отправляет событие setVisible (false). Поток закончен и EDT пинается в
  5. EDT закончил первую задачу, помещает свое отображаемое событие в очередь событий
  6. Он переходит к следующему событию, а вуаля - событие setVisible (false)!
  7. Это испортило все состояние диалога, и оно остается видимым и невосприимчивым.

EDIT2: Похож на то, что ProgressMonitor обладает функциональностью, которую вы пытаетесь реализовать.

+0

InvokeAndWait в show() заставляет модальное диалоговое окно блокировать ожидающее закрытие. Я думаю, это то, чего Zarkonnen пытается избежать со всеми дополнительными потоками. –

+0

@Skip Head: да, ты прав. –

+0

Мне нужно это, как «подождать, обработать» модальный диалог, чтобы сообщить пользователю, что программа занята работой. Он должен быть модальным, чтобы пользователь не нажимал кнопки, пока программа занята («потому что тогда и EDT, и рабочий поток будут манипулировать состоянием). – Zarkonnen

1

Вы можете попробовать вместо dispose() диалог, а не скрывать его, но это потребует от вас перестроить его, если вы хотите снова его показать.

0

немного времени сна (100мс) между SetVisible (истинной) и SetVisible (ложь) решает эту проблему в некоторых случаях. см. также https://bugs.openjdk.java.net/browse/JDK-5109571 И когда вы пытаетесь использовать dispose вместо setVisible (false), никакого состояния гонки не возникает