Java Swing对话框,如何在捕获WindowClosing事件时调用wait方法

时间:2015-04-29 02:33:46

标签: java swing jruby wait

我有一个JRuby脚本,它打开一个Java对话框来报告脚本的进度。

我正在捕获一个windowclosing事件,并希望对话框等到JRuby脚本中的某些清理发生,然后处置。相反,当用户按下右上角的红色x按钮时,对话框就会挂起。

如何正确调用wait方法等待该标志更改?我正确使用锁定对象吗?

一个jruby脚本调用此对话框 如果用户按下右上角的红色X,则对话框会捕获窗口关闭事件并设置“取消”'旗。 该脚本密切关注该标志,然后开始关闭一些长时间运行的任务,什么不是。完成后,它会更新对话框上的标志,表示已进行清理。 同时,对话框正在循环,等待该标志改变。然后它调用dispose()。

我尝试过睡觉。出于某种原因,这会在我的JRuby和对话框之间产生一些问题,清理工作正常,但对话框没有处理。

使用wait,with synchronize(this)会生成IllegalMonitorException,但脚本会清除并且对话框会在异常之外正确处理。

已经看过一些关于如何同步等待方法的其他帖子,非常想了解这一点。

非常感谢您的帮助。

Dialog类如下:

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

public class MyDialog extends JDialog {
private boolean userCancelled;
private boolean scriptCleanedUp;

//private static Object lock = new Object();

public MyDialog(lock) {
    userCancelled = false;
    scriptCleanedUp = false;
    setDefaultCloseOperation(2);

    //[..] add various controls to dialog
    addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent we) {
            // the jruby script keeps an eye on this flag to see if the user has cancelled the dialog
            userCancelled = true;

            /* once cancelled, wait for script to flag that it has performed its cleanup
            */

            /* here is the problem area, what do I need to synchronize to use the wait method?
            */
            while (!scriptCleanedUp) {
                try {
                      synchronized (lock) {
                        lock.wait(1000000000);
                    }

                   // Thread.sleep(1000);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            dispose();
        }
    });
    super.paint(super.getGraphics());
}
public boolean user_cancelled() { return userCancelled; }
public void setScriptCleanedUpToTrue() { this.scriptCleanedUp = true; }

public static void forBlock(MyDialogBlockInterface block)
{
    MyDialog dialog = new MyDialog(new Object());
    dialog.setVisible(true);
    block.DoWork(dialog);
    dialog.dispose();
}
}

如果有帮助,这就是JRuby脚本调用对话框的方式

MyDialog.forBlock do |dialog|
    #do long running jruby task here
end

1 个答案:

答案 0 :(得分:2)

您的代码存在很多问题,包括:

  • 您正在Swing事件线程上进行长时间运行的调用,这会占用这个关键线程,因此可以保证冻结您的GUI。
  • 您直接打电话给paint(...),这几乎不可能完成。

我敢打赌,只要确保你的对话框是模态 JDialog就可以调用你的大部分问题,如果你在后台线程中进行长时间运行的调用,例如一个SwingWorker,而不是试图等待释放锁,使用回调机制通知对话框自行关闭。

例如:

import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.*;

/**
 * http://stackoverflow.com/a/29933423/522444
 * @author Pete
 *
 */
@SuppressWarnings("serial")
public class TestMyDialog2 extends JPanel {
   private static final int PREF_W = 400;
   private static final int PREF_H = PREF_W;

   public TestMyDialog2() {
      add(new JButton(new MyDialogAction("Please press this button!", this)));
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      // let's make this reasonably big
      return new Dimension(PREF_W, PREF_H);
   }

   private static void createAndShowGui() {
      TestMyDialog2 mainPanel = new TestMyDialog2();

      JFrame frame = new JFrame("TestMyDialog2");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }

}

@SuppressWarnings("serial")
class MyDialogAction extends AbstractAction {
   private JDialog dialog;
   private MyWorker myWorker;
   private TestMyDialog2 testMyDialog2;

   public MyDialogAction(String name, TestMyDialog2 testMyDialog2) {
      super(name);
      int mnemonic = (int) name.charAt(0);
      putValue(MNEMONIC_KEY, mnemonic);
      this.testMyDialog2 = testMyDialog2;
   }

   public void dialogIsClosing(WindowEvent e) {
      if (myWorker != null && !myWorker.isDone()) {
         myWorker.setKeepRunning(false);
      } else {
         if (dialog != null && dialog.isVisible()) {
            dialog.dispose();
         }
      }
   }

   @Override
   public void actionPerformed(ActionEvent e) {
      Window mainGui = SwingUtilities.getWindowAncestor(testMyDialog2);
      dialog = new JDialog(mainGui, "My Dialog", ModalityType.APPLICATION_MODAL);
      dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
      dialog.add(Box.createRigidArea(new Dimension(200, 100)));
      dialog.addWindowListener(new DialogWindowListener(this));
      dialog.pack();

      myWorker = new MyWorker();
      myWorker.addPropertyChangeListener(new MyWorkerListener(dialog));
      myWorker.execute();
      dialog.setLocationRelativeTo(mainGui);
      dialog.setVisible(true);
   }
}

class MyWorker extends SwingWorker<Void, Void> {
   private volatile AtomicBoolean keepRunning = new AtomicBoolean(true);

   @Override
   protected Void doInBackground() throws Exception {
      // to emulate long-running code
      while (keepRunning.get()) {
         Thread.sleep(200);
         System.out.println("Long running background code is running");
      }

      System.out.println("Doing shut-down process. Will close in 10 seconds");
      for (int i = 0; i < 10; i++) {
         System.out.println("Countdown: " + (10 - i));
         Thread.sleep(1000); // emulate a long running shut-down process
      }
      return null;
   }

   public void setKeepRunning(boolean newValue) {
      this.keepRunning.getAndSet(newValue);
   }
}

class MyWorkerListener implements PropertyChangeListener {
   private JDialog dialog;

   public MyWorkerListener(JDialog dialog) {
      this.dialog = dialog;
   }

   @Override
   public void propertyChange(PropertyChangeEvent evt) {
      if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
         dialog.dispose();
         try {
            ((MyWorker) evt.getSource()).get();
         } catch (InterruptedException e) {
            e.printStackTrace();
         } catch (ExecutionException e) {
            e.printStackTrace();
         }
      }
   }
}

class DialogWindowListener extends WindowAdapter {
   private MyDialogAction myDialogAction;

   public DialogWindowListener(MyDialogAction myDialogAction) {
      this.myDialogAction = myDialogAction;
   }

   @Override
   public void windowClosing(WindowEvent e) {
      myDialogAction.dialogIsClosing(e);
   }
}