クローズボックスでアプリケーションを終了したい
余談:_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 メソッドは 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 を実行し、ウィンドウ左上のクローズボックスをマウスでクリックすると、ウィンドウは閉じられ、アプリケーションも終了します。