2015-04-29 51 views
2

我有一個JRuby腳本,它打開一個Java對話框來報告腳本的進度。Java Swing對話框,如何在捕獲WindowClosing事件時調用wait方法

我正在捕獲一個windowclosing事件,並希望對話框等到JRuby腳本中發生了一些清理,然後進行處理。相反,當用戶按下右上方的紅色x按鈕時,對話框會掛起。

如何正確調用wait方法來等待該標誌更改?我正確使用鎖定對象嗎?

jruby腳本調用此對話框。
如果用戶按下右上角的紅色X,對話框將捕獲windowclosing事件並設置'取消'標誌。 腳本會留意那個標誌,然後開始關閉一些長時間運行的任務,而不是什麼。完成後,它會更新對話框上的標記以表示已進行清理。 同時,對話框正在循環,等待該標誌發生變化。然後它調用dispose()。

我試過使用睡眠。出於某種原因,這會擾亂我的JRuby和對話框之間的某些東西,清理會發生,但對話框不會處理。

使用wait,with synchronize(this)會生成一個IllegalMonitorException,但是腳本將清除並且對話框會正確地處理除了異常之外的東西。

看了一堆關於如何合併等待方法的其他帖子,非常想了解這一點。

非常感謝您的幫助。

對話框類,如下所示:

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 
+0

是否JRuby的呼叫碼也顯示某種類型的擺動窗口的諸如一個JFrame? –

+0

不,它沒有。不知道這是否有幫助,但還有其他一些Swing的東西正在發生 - JRuby代碼由Java應用程序運行,並且該應用程序使用Swing作爲接口。因此該應用程序正在運行JRuby代碼,然後調用我的小對話框。 –

回答

2

你已經得到了很多與代碼,包括問題:

  • 你正在Swing事件線程上進行長時間的調用,這會綁定這個關鍵的線程,因此g保證凍結您的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); 
    } 
} 
+0

太棒了,謝謝。我會放棄並報告。非常感謝您添加完整的代碼來嘗試! –

相關問題