// 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");
}});
}
}
}
ответ
Итак, выясняется, что то, что происходит, когда вы показываете()/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);
}
+1 для ответа на свой вопрос два года спустя. Есть ли значок для посвящения? –
Это явно какой-то вид гонки. Я не думаю, что это так же просто, как ответ Эрика Робертсона. Код Dialog show()
довольно сложный, он содержит специальную логику для вызова из потока отправки событий, а также отправляет события в очередь событий. Возможно, порядок, в котором размещены события, каким-то образом затронуты задержками потоков.
Возможно, вам необходимо
Как указано в пропущенном заголовке, invokeAndWait будет блокироваться до тех пор, пока диалог не будет закрыт.SwingUtilities.invokeAndWait()
, таким образом вы гарантируете, что
setVisible(true)
закончил выполнение, прежде чем позвонить по телефону
setVisible(false)
.
И зачем вам это нужно?
EDIT: Это мой сценарий того, что происходит:
- вы звоните d.show(), что сообщения
setVisible(true)
событие - нить ставится так спать планировщиком и пинки EDT в и начинает выполнять первое событие
- EDT выталкивается до завершения первой задачи и отправляет фактическое событие, которое показывает диалог
- Ваш поток выполняет d.hide(), который отправляет событие setVisible (false). Поток закончен и EDT пинается в
- EDT закончил первую задачу, помещает свое отображаемое событие в очередь событий
- Он переходит к следующему событию, а вуаля - событие setVisible (false)!
- Это испортило все состояние диалога, и оно остается видимым и невосприимчивым.
EDIT2: Похож на то, что ProgressMonitor обладает функциональностью, которую вы пытаетесь реализовать.
InvokeAndWait в show() заставляет модальное диалоговое окно блокировать ожидающее закрытие. Я думаю, это то, чего Zarkonnen пытается избежать со всеми дополнительными потоками. –
@Skip Head: да, ты прав. –
Мне нужно это, как «подождать, обработать» модальный диалог, чтобы сообщить пользователю, что программа занята работой. Он должен быть модальным, чтобы пользователь не нажимал кнопки, пока программа занята («потому что тогда и EDT, и рабочий поток будут манипулировать состоянием). – Zarkonnen
Вы можете попробовать вместо dispose()
диалог, а не скрывать его, но это потребует от вас перестроить его, если вы хотите снова его показать.
немного времени сна (100мс) между SetVisible (истинной) и SetVisible (ложь) решает эту проблему в некоторых случаях. см. также https://bugs.openjdk.java.net/browse/JDK-5109571 И когда вы пытаетесь использовать dispose вместо setVisible (false), никакого состояния гонки не возникает
Можете ли вы сделать диалог не модальным? Тогда вы можете закрыть его, не вникая в Threads. –
См. Также: http://stackoverflow.com/questions/3886264/non-blocking-modal-swing-progress-dialog – Zarkonnen