MacPythonについてのページ(よ)>
Python(MyApp) で Mac アプリケーションを作る|目次

第5回 ウィンドウのクローズでアプリケーションを終了する


このページの目次

クローズボックスでアプリケーションを終了したい
    余談:_quit メソッドはどこにあるの?
クローズボックスでアプリケーションを終了する


クローズボックスでアプリケーションを終了したい

前回までのものでは、ウィンドウ左上のクローズボックスをクリックするとウィンドウが閉じます。もう一度開くことはできず、アプリケーションを終了することしかできません。そこで、ウィンドウをクローズするとアプリケーションを自動的に終了するようにします。

アプリケーションの終了は、MyApp.py の MyApp クラスに定義されているんだろうと予想して、MyApp.py を見てみます。

MyApp.py の119行目では File メニューの Quit アイテムが定義されています。

MyApp.py
119
--------------------------------------------------
    quititem = FrameWork.MenuItem(m, "Quit", "Q", 'quit')
--------------------------------------------------

Quit アイテムが選択されるとコールバック 'quit' を実行します。
ボタンのコールバックは self.speak のように指定しましたが、'quit' のような場合は 「 domenu_ 」を加えた domenu_quit メソッドを実行します。
このへんについてはWリファレンスの Menubar とMenu クラスの最後と、「Wウィジェットを使う」の 2.2 メニューについてもっとに書かれています。

domenu_quit はこのように定義されています。

MyApp.py
191-193
-----------------------------------
   def domenu_quit(self):
      # buh-bye
      self._quit()
-----------------------------------

だから、MyApp クラスの domenu_quit か _quit を呼び出せば、アプリケーションは終了するはずです。

余談:_quit メソッドはどこにあるの?

_quit メソッドはどこで定義されているんでしょう? _quit メソッドは MyApp.py に書かれていないので、MyApp クラスのスーパークラスである Wapplication.Application にあるんだと思います。

そこで Python2.0:Mac:Tools:IDE:Wapplication.py のApplication クラスを見てみると..._quit メソッドはありません。Application クラスは FrameWork.Application を継承しているので、そちらにあるんだと思います。(ここの文章は、私が実際にファイルを開きながら書いてるのがわかりますね(^^;)。

Python2.0:Mac:Lib:FrameWork.py のApplication クラスを見てみると、ありました。

FrameWork.py
121-122
-----------------------------------
  def _quit(self, *args):
      self.quitting = 1
-----------------------------------

シンプルですね。self.quitting を1にしているだけです。self.quitting は mainloop のところで出て来ます。

FrameWork.py
152-165
---------------------------------------------------
  def mainloop(self, mask = everyEvent, wait = 0):
    self.quitting = 0
    saveparams = apply(MacOS.SchedParams, self.schedparams)
    try:
      while not self.quitting:
        try:
          self.do1event(mask, wait)
        except (Application, SystemExit):
          # Note: the raising of "self" is old-fashioned idiom to
          # exit the mainloop. Calling _quit() is better for new
          # applications.
          break
    finally:
      apply(MacOS.SchedParams, saveparams)
---------------------------------------------------

いろいろ難しそうなことが書いてありますが、while not self.quitting: とあるので、『self.quitting が1でない間、mainloop を続ける、つまりアプリケーションを動かせ』ということなんでしょうねぇ。

MyApp.py を使ったアプリケーションを作っていて、『難しい機能を付け加えるにはどうしたらいいんだろう』と迷ったら、こんなふうに MyApp クラスのスーパークラスを見てみたり、Python IDE のソースである Python:Mac:Tools:IDE 内のファイル(実を言うと、MyApp.py は PythonIDEMain.py の骨格部分を拝借したもののようです。)を見てみると、役に立つと思います。

クローズボックスでアプリケーションを終了する

『ウィンドウを閉じたら MyApp クラスの _quit を呼び出す』という動作を書けばいいんだと思います。

これには bind を使います。

第4回で、ボタンにコマンドキー+ s の操作を bind で割り当てる方法を書きました。

speakwin.py
----------------------------------------------

import W

class Win(W.Window):
  #メインウィンドウのクラス
  def __init__(self):
    #ウィンドウを表示
    W.Window.__init__(self, (10, 40, 250, 180), 'SpeakText', 
      fontsettings = ('Osaka',0,12,(0,0,0)))
    
    self.prompttext = W.TextBox((10,10,50,22), text = 'Input:')
    self.text = W.EditText((60,10,140,22))
    self.speakbutton = W.Button((90, 60, 60, 22), title = 'Speak', 
      callback = self.speak)
    
    self.open()
    
    #self.setdefaultbutton(self.speakbutton) <-これをコメントアウトして
    #下のようにすると、コマンドキー+ s でボタンのクリックを実行できる
    self.bind("cmds", self.speakbutton.push)
    
  def speak(self):
    import macspeech
    macspeech.SpeakString(self.text.get())
----------------------------------------------

上のように self.bind("cmds", self.speakbutton.push) を書いておくと、『コマンドキー+ s が押された』という操作をキャッチして、self.speakbutton の push を実行します。

似たような動作を『コマンドキー+ s の操作』じゃなくて『ウィンドウがクローズされた』という動作にすればいいんです。

...という考え方で私は発見したのではありません。Python2.0:Mac:Tools:IDE:PyConsole.py の PyOutput クラス(Python IDE の Output ウィンドウを作っているクラスです)の setupwidgets メソッドに使われています。
下のようになっています。
Python2.0:Mac:Tools:IDE:PyConsole.py
271
----------------------------------------------
  self.w.bind("<close>", self.close)
----------------------------------------------

"<close>" の他にも用途によって "<open>"、"<select>" なんかを指定できるようです。詳しくは Wwindow.py を見てみて下さい。

そこで、
self.bind("<close>", self._quit)
を加えたいところなんですが、これだと _quit メソッドを self(Win クラス)に対して実行しようしてしまいます。Win クラスは(そして、そのスーパークラスの W.Window も)_quit メソッドを持っていないので、MyApp.py を実行すると Attribute エラーが出ます。_quit メソッドはアプリケーションのクラス(MyApp クラス)に送らなければなりません。

そのためには、Win クラスから MyApp クラスを参照できるように、変数(あるいはオブジェクトと言うんでしょうか?)として、Win クラスに渡すように書き直します。

うまく説明できないので、ソースを書いちゃいます。

MyApp.py
変更前:100-101
----------------------------------------------
    import speakwin
    self.main = speakwin.Win()
----------------------------------------------
変更後:100-101
----------------------------------------------
    import speakwin
    self.main = speakwin.Win(self) #selfとして、MyApp インスタンスを Win に送る
----------------------------------------------

さらに speakwin.py も変更します。

speakwin.py
変更前:3-6
----------------------------------------------
class Win(W.Window):
    #メインウィンドウのクラス
    def __init__(self):
        #ウィンドウを表示
----------------------------------------------
変更後:3-
----------------------------------------------
class Win(W.Window):
    #メインウィンドウのクラス
    def __init__(self, arg):   # arg として MyApp インスタンスを受け取る

        self.parent = arg   # argをself.parent に代入して MyApp を参照できるようにする

        #ウィンドウを表示
        ・
        ・
        ・
        self.setdefaultbutton(self.speakbutton)
        self.bind("<close>", self.parent._quit) # self.parent で MyApp を呼び出せる
        ・
        ・
        ・
----------------------------------------------

これまで Win は _quit を送る対象を呼び出せませんでした。

しかし、 MyApp.py の訂正後には self.main に Win インスタンスを代入する時に、MyApp 自身(self)を Win に 送り、Win はそれを arg として受け取り、自分(self)の parent に代入しています。したがって、Win インスタンスは self.parent を呼び出せば MyApp を呼び出せます。self.parent._quit とすれば、MyApp インスタンスに _quit メソッドを送っていることになります。

以上の訂正をして MyApp.py を実行し、ウィンドウ左上のクローズボックスをマウスでクリックすると、ウィンドウは閉じられ、アプリケーションも終了します。


<▲このページのTOPへ>
戻る   次へ
Python(MyApp) で Mac アプリケーションを作る|目次
MacPythonについてのページ(よ)トップへ

by ©Hioryuki Yoshimura, 2002-2003.
Last modified at 2003/1/22