2010-10-17 11 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

Czy możesz uczynić dialog nie modalnym? Następnie możesz zamknąć go bez zakłócania wątków. –

+0

Zobacz także: http://stackoverflow.com/questions/3886264/nonblocking-modal-swing-progress-dialog – Zarkonnen

Odpowiedz

2

Okazuje się, że to, co się dzieje, gdy pokazujemy()/setVisible (true) modalne okno dialogowe, że druga pętla ekspedycji zdarzeń jest uruchamiana w ramach wywołania show/setVisible. To ma sens, gdy tylko się o tym dowiesz. Mając to na uwadze, skończyło się z tym kodem:

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 za udzielenie odpowiedzi na własne pytanie dwa lata później. Czy istnieje odznaka do poświęcenia? –

4

Jest to wyraźnie jakiś rodzaj wyścigu. Nie sądzę, żeby była tak prosta jak odpowiedź Ericka Robertsona. Dialogowy kod show() jest dość skomplikowany, zawiera specjalną logikę do wywołania z wątku wysyłki zdarzeń, a także powoduje umieszczanie zdarzeń w kolejce zdarzeń. Być może kolejność, w której publikowane są zdarzenia, zależy od opóźnień wątków.

Być może potrzebne jest SwingUtilities.invokeAndWait(), w ten sposób można zagwarantować, że setVisible(true) zakończyło wykonywanie przed wywołaniem setVisible(false). Jak zauważyła Skip Head, funkcja invokeAndWait będzie blokować, dopóki okno dialogowe nie zostanie zamknięte.

A dlaczego tak czy Ty potrzebujesz?

EDIT: To jest mój scenariusz tego, co się dzieje:

  1. zadzwonić d.show(), który księguje setVisible(true) wydarzenie
  2. wątek jest umieścić więc spać przez scheduler i kopnięcia w EDT i rozpoczyna wykonywanie pierwsze wydarzenie
  3. EDT zostaje wyrzucony przed zakończeniem pierwszego zadania i opublikował faktyczne zdarzenie wyświetlające dialog, w którym wątek wykonuje d.hide(), która wysyła zdarzenie setVisible (false). Wątek jest zakończony i kopie EDT w
  4. EDT zakończyło pierwsze zadanie, umieszcza swoje zdarzenie pokazujące w kolejce zdarzeń
  5. Przechodzi do następnego zdarzenia, i, voila, jest to zdarzenie setVisible (false)!
  6. Przekazuje cały stan okna dialogowego i pozostaje widoczny i nie odpowiada.

EDIT2: Wygląda na to, że ProgressMonitor ma funkcjonalność, którą próbujesz zaimplementować.

+0

InvokeAndWait w show() powoduje zablokowanie oczekiwania na zamknięcie modalnego okna dialogowego. Myślę, że właśnie tego Zarkonnen stara się unikać z wszystkimi dodatkowymi wątkami. –

+0

@Skip Head: tak, masz rację. –

+0

Potrzebuję go jako modalnego okna dialogowego "proszę czekać, przetwarzanie", aby poinformować użytkownika, że ​​program jest zajęty. Musi to być modalne, aby powstrzymać użytkownika od kliknięcia przycisków, gdy program jest zajęty (ponieważ wtedy zarówno EDT, jak i wątek roboczy będą manipulować stanem). – Zarkonnen

1

Możesz spróbować otworzyć okno dialogowe zamiast ukryć je, ale wymagałoby to jego odbudowania, jeśli chcesz je ponownie wyświetlić.

0

Trochę czasu snu (100ms) między setVisible (prawdziwego) i setVisible (fałsz) rozwiązuje ten problem w niektórych przypadkach. zobacz także https://bugs.openjdk.java.net/browse/JDK-5109571 A gdy próbujesz użyć dispose zamiast setVisible (false), nie wydaje się, aby wystąpił warunek wyścigu