2010年10月30日土曜日

課題2:解答編

だいぶ間が開いてしまいましたが、解答編を書いときます。

まずは、ソースコード全体を。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import gtk
import glib

class SlideShow:
    
    def __init__(self):
        
        #GTK.Builderを構築
        wTree = gtk.Builder()
        #Gladeファイルの読み込み
        wTree.add_from_file("SlideShow.ui")
        #MainWindowオブジェクトを取得する
        self.mainWindow = wTree.get_object("MainWindow")
        #シグナルとシグナルハンドラをdic形式で作成する
        dic = {"on_MainWindow_destroy":self.on_MainWindow_destroy}
        #シグナルハンドラとシグナルを結びつける
        wTree.connect_signals(dic)
        #スライドショー用のGTKImageオブジェクトを取得する
        self.imgSlideShow = wTree.get_object("imgSlideShow")
        #画像のパスを取得
        self.imagePath = []
        #os.walk関数により、再帰的にフォルダを降りていき画像を探す
        for base, path, imPath in os.walk("/usr/share/backgrounds"):
            for img in imPath:
                limg = img.lower()
                #jpg/png/jpegファイルだけを対象にする
                if limg.find("jpg") > 0 or limg.find("png") > 0 or limg.find("jpeg") > 0: 
                    self.imagePath.append(base+"/"+img)
        #初期画面をロード
        #初期画面を表示するためのカウンタを初期化
        self.count = 0 
        #gtk.Imageに画像ファイルを表示する関数をコールする
        self._loadImage()
        #ウィンドウを表示する
        self.mainWindow.show_all()
        self.timeout = glib.timeout_add_seconds(2,self._timeouf,self)
        
    
    def on_MainWindow_destroy(self,widget):
        #GTKのメッセージループを終了する
        gtk.main_quit()
    
    def _timeouf(self,event):
        #gtk.Imageに画像ファイルを表示する関数をコールする
        self._loadImage()
        return True
        
    def _loadImage(self):
        #現在のカウンタを使用して画像ファイル名を取得。
        wallpaper = self.imagePath[self.count]
        #現在のウィンドウサイズを取得(x,y)のタプルで返る
        size = self.mainWindow.get_size()
        #新規にPixBufを作成し、指定されたファイルを読み込む
        pixbuf = gtk.gdk.pixbuf_new_from_file(wallpaper)
        #読み込んだPixBufの縦と横のサイズを取得
        x = float(pixbuf.get_width())
        y = float(pixbuf.get_height())
        #アスペクト比を算出
        aspect = y / x
        #pixbuf.scale_simpleメソッドを使用し、ウィンドウにマップされたImage用のpixbufを作成する
        #その際、アスペクト比を合わせる。基準は横幅
        pixbuf2 = pixbuf.scale_simple(size[0], int(size[0]*aspect),gtk.gdk.INTERP_BILINEAR )
        #使い終わったPixBufを削除    
        del pixbuf
        #ウィンドウにマップされたgtk.Imageに新規にリサイズしたPixBufをアサイン
        self.imgSlideShow.set_from_pixbuf(pixbuf2)
        #アスペクト比に合わせてウィンドウサイズをリサイズ
        self.mainWindow.resize(size[0],int(size[0]*aspect))
        #画像リスト用のカウンタをカウントアップ
        if len(self.imagePath) > self.count+1:
            self.count += 1
        else:
            self.count = 0

if __name__ == "__main__":
    #イメージ表示ウィンドウのコンストラクタを実行する(オブジェクトを作成する)
    SlideShow()
    #GTKのメッセージループを実行する
    gtk.main()


これに、課題2の時に母体コードとして提供したSlideShow.uiファイルを組み合わせれば、2秒置きに画像が表示されるスライドショーが完成します。

今回、ポイントとなるのは、以下の関数というかメソッドなわけですが。
def _loadImage(self):
        #現在のカウンタを使用して画像ファイル名を取得。
        wallpaper = self.imagePath[self.count]
        #現在のウィンドウサイズを取得(x,y)のタプルで返る
        size = self.mainWindow.get_size()
        #新規にPixBufを作成し、指定されたファイルを読み込む
        pixbuf = gtk.gdk.pixbuf_new_from_file(wallpaper)
        #読み込んだPixBufの縦と横のサイズを取得
        x = float(pixbuf.get_width())
        y = float(pixbuf.get_height())
        #アスペクト比を算出
        aspect = y / x
        #pixbuf.scale_simpleメソッドを使用し、ウィンドウにマップされたImage用のpixbufを作成する
        #その際、アスペクト比を合わせる。基準は横幅
        pixbuf2 = pixbuf.scale_simple(size[0], int(size[0]*aspect),gtk.gdk.INTERP_BILINEAR )
        #使い終わったPixBufを削除    
        del pixbuf
        #ウィンドウにマップされたgtk.Imageに新規にリサイズしたPixBufをアサイン
        self.imgSlideShow.set_from_pixbuf(pixbuf2)
        #アスペクト比に合わせてウィンドウサイズをリサイズ
        self.mainWindow.resize(size[0],int(size[0]*aspect))
        #画像リスト用のカウンタをカウントアップ
        if len(self.imagePath) > self.count+1:
            self.count += 1
        else:
            self.count = 0
中身に関しては、ソースコードの一行ごとにコメント入れたので、そちらを参照してもらうとして。

これが、2箇所でコールされている、ということです。
__init__と_timeoufの2箇所ですね。
もちろん、もっとシンプルな処理の場合、2箇所に同じ処理を書いてもいいのですが、基本的に、gtk.Imageに指定された画像ファイルを表示する、という全く同じ処理を行っているわけです。

さすがに、ここまで長いと理解できると思いますが、同じ処理を何度も書くのはムダなんですね。
どのようにムダかというと、以下の点でムダ。

同じことを2回書かなくてはならない(コピペでいいじゃんという説はある)
もしも修正した場合、同じ箇所を2回直さないとならない(コピペでいいじゃんという説はある)
同じコードが2度も現れると読みにくい

いずれの場合も、同じ手間を2度行わなくてはならないのがムダ、ということになります。
で、まあ2箇所くらいなら、別にコピペでもいいんですが、これが20箇所とかになると、さすがにコピペも厳しいし、読みづらくなるし、コピペ漏れも出てきます。
そのため、同じ処理はまとめてしまって、関数、あるいはローカルメソッドして定義してしまうほうが効率がよいわけです。

ローカルメソッドとするか、関数とするかは、スコープの問題なので、各種クラスで汎用的に使われる処理であれば、関数として、どこからでも呼べるようにしておくべきでしょう。
例えば、エラーメッセージのメッセージボックスを表示するための処理、とか、デバッグ用のログ出力を行う処理とか、ですね。
#デバッグログとか、日時出力とか行うので、それなりに長い処理になったりする。

このように、効率の悪くなるコードを関数やローカルメソッドにしてしまうプログラムの事を構造化プログラミングといいます。
いつぞや番外で触れましたけど。

見直せば、もっと構造化できそうなところもないではないですが、まあ、無理に構造化しても実行が遅くなったり、却って見にくくなってりします。
その他、Pythonでは、リスト内包表記が使えるので、あまり見ませんがループのネストが深くなる場合や、条件式の内側が、かなり大きくなる場合に、ローカルメソッドにして、処理そのもの流れを見やすくするなど、構造化プログラミングの手法としてあったりします。
#これもやり過ぎると実行速度に影響が出たりします。

いずれにせよ、ひとが見やすいように、処理を分割し、関数化、あるいはローカルメソッド化することにより、ムダを省き、バグが発生しにくくしましょうよ、ということを、今回の課題では伝えたかったんですが、ちとネタがディープ過ぎて伝わり難いというね。(笑)
これなら、リストの処理で、内部関数とか、そういうのをネタにした方が良かったかな。(^^;

まあ、例えて言うなら、毎日の通勤路、自分は階段を降りる時に、階段を登ってくるOLさんの胸の谷間を見るために、若干右よりに降りていく、すれ違う瞬間だけ、さっと左側に視線を走らせる、ということを毎日やっているとして、これをルーチン化して、無意識に行うのと同じようなことです。

意識して、毎度毎度やっていたのでは、不自然な動きになり、OLさんに警戒されてしまうでしょう。
そこを不自然にならないように、無意識に行えるように自分の中でルーチンとして一連の動きが処理されるようにまとめてしまう、ということです。
#例えがハイエンド過ぎて分かり難いか。
見事谷間を目撃できたら、その日はいいことがありますよ、的な占いのようなものでもあるかも知れません。
そのために、いかに自然に振舞うか。それがルーチン化の要でもあります。

プログラムだって同じなのです。毎度繰り返されるような処理はひとかたまりにして、ひとつのブロックとして、単に呼び出せば、それが実行される、という形にまとめておいた方が、いろいろと都合がいいわけです。
ルーチンを見直す時とかですね。
例えば、先の例だと、季節ごとにOLさんの服装も代わります。その場合、右によるか、左によるか、季節ごとの見直しが必要です。
しかもターゲットのOLさんがひとりでない場合、全体的に見直しになってしまいます。
そこで、きちんと関数として、まとめておくことで、季節ごとに、一箇所修正することで、無事に谷間を拝めるようになるのです。

朝の通勤時、OLさんの谷間を拝めるかどうか、それだけで朝の気分は違います。
それだけ、ルーチン化、関数化というのは重要なことなのです。

今回の課題で主張したかったのは、この点ですね。
「谷間を見逃すな」「同じ処理を2度書くな」ということです。

では、ぼちぼち、次のネタを考えることにしましょうかね。

2010年10月17日日曜日

課題2:スライドショーを作成する

えー、課題の2個目です。
いきなり敷居が高くなりますが、スライドショーを作成してみましょう。
プログラム的には、パスは固定で、任意のフォルダの画像を連続で表示する、というものになります。
とりあえず、壁紙のパスをソースでは設定していますが、まあ、自分の画像ファイルならどこでもいいと思います。

で、敷居が高いので、母体ソースを添付します。
ここからダウンロードしてください。

一応、コメントしていますが、この母体に関数(メソッド)を追加して、gtk.ImageであるimgSlideShowに画像を連続で表示していきます。
今はタイムアウトの時間を2秒で設定していますが、まあ、任意の秒数にしてみてもいいでしょう。

解答では、画面の横幅を固定して、画像のアスペクト比に合わせてウィンドウの高さを変更する、というのを予定していますが、まあ、そこまでは作りこまなくともよいです。
可能なら、挑戦してみましょう。
ここが参考になります。

とりあえず、完成後の画像イメージ。


まあ、正直、アスペクト比を保持したままのイメージ表示は敷居が高いので、画像ファイルをイメージとして表示する、までを目標にやってみてください。
なお、サンプルソースを見てもらえば解ると思いますが、画像ファイルのパスはself.imagePathにリストとして保持しています。
フォルダ階層をたどるサンプルにもなってますので、今後、使い途があるかも知れません。
まあ、これはこれで調べればすぐに解る類のものなのですが、今回は関数化というのをテーマとしたかったので、そこはサンプルコードとして出してしまいます。

そんじゃま、また、そのうち解答編で。

2010年10月11日月曜日

課題1:解答編

課題1の解答編です。
まあ、解答ってほどのものではありません。Gladeの使い方のおさらいです。




















トップレベルには、何もないウィンドウを使用し、MainWindowと名前を付けます。
コンテナは、水平コンテナでひとつだけにします。別にこれは垂直コンテナでも構いません。
最後の今回のポイントは画像コントロールを使用し、画像ファイルを指定する、というものです。
この画像コントロールにはプログラムから画像を指定することも可能ですが、Gladeから指定してしまう方が簡単でしょう。
注意すべきは、画像もプログラムと同じパス、つまり同じフォルダに置いておくこと、ってことでしょうかね。
なので、プログラム本体は大変シンプルになります。
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import gtk

class ImageDisplay:
    
    def __init__(self):
        
        #GTK.Builderを構築
        wTree = gtk.Builder()
        #Gladeファイルの読み込み
        wTree.add_from_file("ImageDisplay.ui")
        #MainWindowオブジェクトを取得する
        mainWindow = wTree.get_object("MainWindow")
        #シグナルとシグナルハンドラをdic形式で作成する
        dic = {"on_MainWindow_destroy_event":self.on_MainWindow_destroy_event}
        #シグナルハンドラとシグナルを結びつける
        wTree.connect_signals(dic)
        #ウィンドウを表示する
        mainWindow.show_all()
    
    def on_MainWindow_destroy_event(self,widget):
        #GTKのメッセージループを終了する
        gtk.main_quit()

if __name__ == "__main__":
    #イメージ表示ウィンドウのコンストラクタを実行する(オブジェクトを作成する)
    ImageDisplay()
    #GTKのメッセージループを実行する
    gtk.main()

こんだけ。
☓ボタンで終了するために、MainWindowのシグナル、destroyをハンドルしているので、Gladeの方でも忘れずにシグナルを設定して置きましょう。

こんなの何に使うの?
という疑問もあるでしょうが。(笑)
これでウィンドウデコレーションを外せば、スプラッシュスクリーンに使えます。
その場合には、コンストラクタでタイマーをセットして、ある一定時間後にウィンドウを破棄する処理を走らせることになりますけど。
まあ、その辺は説明してないので、使い方い場合は別途、ということになるでしょうか。

コンストラクタの中身は、もはや定型的な処理です。
gtk.Builderを作成し、そのオブジェクトにGladeファイルを読み込み、MainWindowを取得して、表示を行う、これだけですね。

さて、とりあえず、おさらいということで、課題1を考えてみましたが。
次は関数定義と、その実行、関数にするべき場合とは、的な課題を考えたいものですがね。
まあ、なかなか課題を考えるのは難しいもので。先生とか偉いですねぇ。(笑)

まあ、とりあえず、プログラムなんて、おっぱいを揉むことから比べれば、圧倒的に簡単だし、試行錯誤もラクなので、色々と試したいことがあれば、どんどんとチャレンジしていけばいいと思います。

また、Pythonってのが、ある意味(構文的に)制限の多い言語なので、「これをやりたい」という場合に、それほど選択肢がなく、誰が書いても似たような書き方になる言語のひとつなので、ある意味「プログラミングの学習」には向いている言語ではないかと思います。

こういう書き方もある、こんな風にも書ける、そういう自由度の高い「表記が可能」な言語の場合、何が正しいのか解りにくい場合も多いと思いますけど。
#ロジックに関して言えば、こういう書き方もある、こうも書ける、はありえます。文法的な部分に制限が多いということですけどね。

さて、次の課題、ネタは何にしたものか。関数定義とか内部処理なんで、外側に現れにくいから、難しいんだよなー(笑)

2010年10月7日木曜日

課題編:課題1-Gladeを使って画像を表示してみましょうか。

まあ、これから課題編ということで。
これまでに学んだ(と思われる)知識をベースに、課題に挑戦して貰おうかと思います。(笑)

第一弾は、これ。

































Gladeを使って、画像を表示してみましょう。
なお、ついでに☓ボタンで終了出来るようにすることも忘れずに。

思い出した頃に、解答編を書きます。(笑)

ついでに、素材として、この画像もアップロードしときますが、別に表示する画像はなんでも構いません。
ちなみに、画像を表示する部分までは、Gladeにより処理できます。
プログラムは一切必要ありません。
※ヒント(笑)

では、挑戦してみてください。

おっと、忘れるところだ。
これ、素材。

2010年9月17日金曜日

番外編--オブジェクト指向

えー、番外なんで、別に読まなくてもいいです。
てか、おっPyとあんまし関係ないエントリだし、考え方的な話で、説明とか、もっと先のエントリでしようかな、と思ってた話なので、ついてこれない可能性もあるんで。
ただ、リンク先は見といた方がいいんじゃないかと思います。
構造化プログラミングとか、その辺の歴史に触れていて、成り立ちとか書いてあるので、知らないひとには面白い記事でしょう。
まあ、知ったからといって、どうにかなるか、というとどうにもならないんですが。(笑)

こんな記事を見つけまして。
割とというか、かなり解り易い記事だなー、とは思うんですが。
ちとイメージと違うのが、オブジェクト指向がグローバル変数を駆逐した、って部分で。
この、おっPyでもグローバル変数は使ってるんですよ。
まあ、タプルとして定数扱いなので、弊害は発生しないし、変数の解説のところでも、グローバル変数の危険性については、ちょっとだけ触れてますけど。

元記事でも触れられてますが、ワタクシにとってのオブジェクト指向とは、データの隠蔽と抽象化なんですよね。
グローバル変数とか、わりとどうでもよくて。
確かに構造化プログラミングでは、手を入れられてないデータ構造に手を出したのがオブジェクト指向プログラミングだとは思うんですが、駆逐したのはグローバル変数ではなくて、整理されてないデータ構造だと思うんです。
グローバル変数を駆逐するだけなら、ある程度構造化プログラミングでも可能ですし、事実、C言語なんかでは、そのように書かれている入門書も多いんじゃないでしょうかね。

構造化プログラミングのGOTOと同様に、グローバル変数も使い方によっては強力な武器になるわけで、別に使っちゃいけないものではないんですよ。
むやみやたらと使うと、元記事のようにデバッグ不能に陥るので、なるべくなら使わない、という方針がよいのは間違いないんですが。

ただ、そここだわると、オブジェクト指向プログラミングも、実はかなり不自由になるんじゃないの、とも思うわけです。
データ構造の隠蔽と抽象化、まあ、元記事で出納係の話が出てましたが、これがまさにそれで、金庫というか、お金の入れ物を隠蔽して、その入れ物を出納係で抽象化しているのが、オブジェクト指向だと思うんですね。

で、結果として、出納係に「お金を渡す」、「お金をもらう」という二つのメソッドだけ知っていれば、お金の出し入れが可能になる、なので、例として出ていたたこ焼き屋以外にも、この出納係というのは、流用が可能で、中のお金の管理の仕組みを知らずとも、出納係さえ使えば、「お金を渡す」ことと「お金をもらう」ことが出来るわけで。

その中の仕組み、データの管理と構造を、外側から隠蔽してしまう、ってのが一番のオブジェクト指向のメリットだとワタクシは思っているので、グローバル変数がどうのってのは、あんまし関係ないような気がしているんですよね。
てか、普通に使えるし。
抽象化したオブジェクトをグローバルに置いて、アプリケーションの設定は、全部そこからもらう、なんてこともアリだと思うし。
きちんと手順が決まっていて、中身が隠蔽されていることが重要であって、そのような設計になっていれば、ローカルでもグローバルでも構わないのがオブジェクト指向じゃないのかな、とワタクシは考えてます。
なので、抽象化さえ出来てしまえば、継承とかは、まあオマケみたいなもんかな、とも思うんですよね。
抽象化するために、継承出来ないと、その機能が生きないので、継承は必要かも知れませんが、データ構造とそのアクセスを制限する、つまり隠蔽して抽象化出来てしまえば、それで十分オブジェクト指向プログラミングの意義は果たせると思うわけです。

その点では、元記事の言うところの「データ構造にメスを入れた」のは、オブジェクト指向であるのは間違いないと思いますが。

最初読んだ時に、ピンと来なかったのは、たぶん、そのデータのスコープを問題にしてたからなんだろうな、とは思うんですが。

ま、いずれこの辺の話は、もう少し噛み砕いで、突っ込んだエントリを書きます。
プログラム設計の辺りで。(笑)

まずは番外ってことで。

2010年9月15日水曜日

正しく動作させるために--アプリケーションのデバッグとか

この辺、割と重要な話なのに、全然書いてなかったので追記します。
最初にNetBeansのインストールの辺りで参照した、このサイトの下の方に、デバッグの方法が記載されています。
すごく簡単に。(^^;

これだけでデバッグ出来る達人は、おそらく、こんなの読んでないと思うので、ちょっと解説しときましょう。

まず文法エラーのチェックから


Pythonではインデントが重要という話をしています。
で、その手の文法エラーはNetBeansだと、左側の行番号のところに赤丸がついて、エラーがあることを示してくれます。
複数ある場合には、Alt+Enterで、その解説を見ることが出来るんですね。
まあ、英語ですけど。
#一部日本語だったりする。













で、この場合には、インデントが間違ってるよ、とか表示してくれるので、この辺の指示に従うことで、ある程度の文法エラーは、チェック可能です。

また、赤丸が付いてない場合、F6を実行した時に実行時エラーが出る場合があります。
例えば、こんな感じで。












例えばこの場合は、31行目でエラーがでてるよって表示になってます。
で、リンクになってるので、下線がついてる部分をクリックすると、その行に飛んでくれるんですね。
大抵は、そのエラー発生行に、Typoがあったり、そもそも、代入してない変数を使おうとしてたりとか、そんなに大外しなことはないです。
しかしながら。
エラーメッセージがアテにならない場合もあり、例えばこの例は、リスト(タプル)の宣言が間違っていたんですが、実際には間違っていたのは上の行という、比較的解りにくいパターンになります。
なので、エラー発生行を中心に、前後に間違いがないかどうか、文法を確認するといいですね。

実行時に思うように動かない場合--printデバッグ


まあ、プログラムなので、こんなこともよくあります。(笑)
一発で動くものを書けるなんて思わないほうが良くて、まあ、何度もトライ&エラーを繰り返しながらプログラムってのは完成に近づいていくものなのです。

そう、気になる事務のあのコだって、いきなりおっぱいが揉めるわけもないのです。
何度もデートに誘ったり、断れられたり、食事に行ってみたりと、トライ&エラーを繰り返さないと、おっぱいには近づけないのと同じです。(違

で、比較的、どんな環境でも使えるのが「print」文を使ったデバッグ手法です。
以下のメソッドで、実際にデバッグプリントを埋めこんでいます。
#コマンド実行のメソッド
    def execCommand(self,command):
        print command   #受け渡されたコマンドのデバッグ用プリント
        ret = commands.getoutput(command)
        print ret             #実行結果のデバッグ用プリント
        return ret
これは、渡されてきたコマンドと、コマンドを実行した結果を端末に表示するために、print文を埋めこんでいます。
実際に、あるチェックボックスをチェックした際に、期待したコマンドが実行されているか、そして、その実行結果がエラーを返してないか、というのを確認するためにです。

まあ、これ、結構言語問わず、最終手段的に使われる方法なんで、覚えておくといいかも知れません。(笑)
#JavaScriptなんかでもalertデバッグとか、よくやる。(笑)

期待されたコマンドが実行されていなければ、コマンドの定義をしているタプルが間違ってるし、実行結果にエラーがあれば、コマンドのオプションが間違ってたりするわけです。
まあ、本当にはるか昔、いにしえの業なので、困ったときのprint頼り、みたいなもんです。
小規模なアプリケーションというか、ツールレベルなら、こんだけでも困りませんね。(笑)

実行時に思うように動かない場合--いよいよ本命デバッガを使う


NetBeansのPythonバインディングを使用している理由の一つに、Pythonデバッガがきちんと組み込みで使えるというのがあります。

試しに、この部分で実行を止めてみたい、というところの行番号をクリックしてみましょう。
赤い■がついたと思います。
















行も赤く反転しましたね。
これが「ブレークポイント」といい、実行時に、ここまで来たら、実行を一時的に中断する、というデバッガの機能です。
イメージの問題ですが「だるまさんがころんだ」という遊びに似ているでしょうか。(笑)

まあ、IDEやデバッガにもよりますが、あちこちに設定することが出来るので、思い通りに動作しない場合、関係しそうなところには、あらかじめブレークポイントを設定しておく、というのも手かも知れません。

ちなみに、OKボタンが押されたところに、ブレークポイントを設定しましたので、これでデバッグ実行をすると、普通にウィンドウが表示されて、操作が可能です。
てか、そういう普通に操作しつつ、悪いところを探すのが、デバッガの目的なのです。

NetBeansだと、デバッグメニューから主プロジェクトのデバッグ、またはCtrl+F5でデバッガが起動します。


















で、実際に実行すると、プログラムの先頭で一旦停止するので、そのままF5を押すと、プログラムが実行されます。赤枠で囲ったところがデバッグ用のツールバーになりますので、マウス操作も可能ですね。


















今回の場合は、OKボタンを押したシグナルハンドラにブレークポイントを設定したので、それ以外の操作を行う分には、一時停止は行われません。
んでは、OKボタンを押してみましょう。










このようにブレークポイントを設定したところで止まりました。
この後は、ステップイン、ステップオーバー、ステップアウトの三つのコマンドを使って、実行を追いかけて行きます。
ちなみに、それぞれ、こんな役目です。
  • ステップイン  :メソッドの中まで入っていって、1行ずつ実行する
  • スタップオーバー:メソッドの中には入らずにメソッドも一行と捉えて一行ずつ実行する
  • ステップアウト :そのメソッドから抜ける

で、ステップ実行していきながら、変数の中身を確認してみましょうか。
みくつべ♪のインストールにチェックをつけていたので、ステップオーバーを行っていくと、以下の画面の状態になります。

































緑の行まで来たところで、下の赤枠で囲ったところのように、print(cmd)とするこで、for cmd in installCommand[0]:でcmdに入ったコマンドがなんであるのかを、確認することが出来ます。

このように、一行ずつ実行していくことで、分岐が思った通りに分岐しているのか、それとも、間違っているのか、ループに正しく入っているのか、などを追いかけていくことができるわけです。
また、適当な場所で変数の値を確認することで、意図通りの動きになってるのかを確認しながら、進めていくわけですね。
これがデバッガを使ったデバッグになります。

こう、お互いの気持ちを確かめ合いながら、一歩々々進めていくようなものです。
やはり、いきなり押し倒したのでは、相手に嫌われかねません。
#案外、押し倒されるのを待っているかも知れませんが。

初めてプログラムを作る場合には、デバッグ実行しながら、どのようにプログラムが実行されていくのかを、ステップインしながら見ていくと、案外面白いかも知れませんね。(笑)

GUIアプリケーションだと、なかなか実行イメージが掴めない場合もあるので、IDEでのステップ実行は、手っ取り早く、どのようにプログラムが実行されているのかを確認する手段にも成り得ます。
各シグナルハンドラの入り口にブレークポイントを置いて、色々と操作を試してみるのも一興ではないかと。

どうも、実行の順番とかがピンと来ないんだよな、なんて場合には、試してみるのも手ではないかと思います。

とはいえ。
もう少し、変数の値の確認とか、手間要らずに出来るといいんですがね。
専用のIDEではないので、このくらいがイイトコかも知れませんが。
ちと、惜しい。
まあ、デバッガが面倒なら、printデバッグでも、かなりのデバッグは出来ますので、やりやすい方法を選んで構わないでしょう。

実際、ワタクシは、小さいツールばかり作ってるので、printデバッグの方が多いくらいですし。(笑)

とまあ、デバッグの手法というか、手段をさらっと説明してみました。
プログラムなんてのは、なかなか思った通りに動いてくれないものです。
原因究明のプロセスは、推理に似ているので、ひとつ、推理ゲームのつもりで、楽しんでみるのも、またひとつのやり方ではないか、とワタクシなぞは考えますが。

苦労してデバッグして、きちんと動くようになった瞬間の感動というのは、また格別なものです。
ぜひとも、その感動を味わってください。(笑)

そう。初めて、あのコのおっぱいに触ったときと、同じくらいの感動が得られるはずです!(ぉ

てなところで、今回はこの辺で。

2010年9月11日土曜日

ドロップダウンリストの解説など

一応、前回ソースコードまでは出したのですが。
なんのことか解らないと思うので、解説をします。(笑)

        #スキン選択コンボボックスを取得
        self.cmbAudaciousConf = self.wTree.get_object("cmbAudaciousCfg")
        #スキン選択コンボのリストを取得
        lsAudaciousConf = self.wTree.get_object ("listAudaciousCfg")
        #スキン選択リストをコンボボックスに設定
        self.cmbAudaciousConf.set_model(lsAudaciousConf)
        cell = gtk.CellRendererText()
        self.cmbAudaciousConf.pack_start(cell, True)
        self.cmbAudaciousConf.add_attribute(cell, 'text', 0)
        self.cmbAudaciousConf.set_active(0)
これがドロップダウンリスト(コンボボックス)を使う上で追加した部分になります。
#なんか、毎度書くの面倒だから関数化した方がいいような気がしてきた。
Pythonだから、ということではなく、GTKのコンボを使う上での問題と言っていいかと思いますが。
正直、Glade上でコンボとその中身のリストを結びつけているので、この処理自体をコードとして書かせること自体ナンセンスであるとは思ってます。
が、書かないと動作しないので、もう、まったくお兄ちゃんったら、って感じなのです。

で、何をしているかというと、リストのセル、ここでいうと選択用の項目ですね、そのセルのレンダラ、つまり、どのように表示するべきものであるのか、という情報を生成して、コンボボックスに設定しているのです。
self.cmbAudaciousConf.set_model(lsAudaciousConf)で、取得したリストモデルをコンボに設定しています。
cell = gtk.CellRendererText()でセルレンダラを生成しています。テキスト用のセルレンダラですね。
self.cmbAudaciousConf.pack_start(cell, True)でさらに、生成したセルレンダラをコンボボックスに設定し、self.cmbAudaciousConf.add_attribute(cell, 'text', 0)先に設定したリストモデルの1番目の項目はテキストですよ、と指定していることになります。
こうしないと、設定した選択項目がコンボボックスに出てくれないのですから、困ったものなんですよね。
まあ、近い将来解消されることを願うばかりです。
#GTK3.0では改善されているといいな。

self.cmbAudaciousConf.set_active(0)の行ですが、デフォルトのコンボボックスは、なにも選択されてない状態になっているので、1番目の項目を選択状態にしておく、という処理ですね。

で、実際にコンボボックスから、選択されている項目番号を取得するには以下のようにします。
#選択されているスキンの番号を取得
            sel = self.cmbAudaciousConf.get_active()
今回は、選択されているコンボの項目番号を取得し、その項目番号から、対応するコンフィグ名を得るリストを別途定義し、そこからコンフィグ名を得ています。
以下の部分です。
            #適用スキンのコンフィグ名を定義
            confName = ("mikumiku001","mikumiku_002 ","mikumiku_003")
            #選択されているスキンの番号を取得
            sel = self.cmbAudaciousConf.get_active()
            #コンフィグファイルのパスを生成
            configpath = os.path.expanduser("~") + "/.config/audacious/config"
            #選択スキンをコンフィグファイルに設定
            self.editConfig("skin=", "skin=/usr/share/audacious/Skins/"+confName[sel], configpath)
これ、例えば、コンボボックスに定義するリストモデルの列を2列にして、表示名と設定項目の二つを定義し、選択されたリストモデルの行から設定名を得る、ということも出来ます。
そのためにコンボボックスとリストモデルを接続している、と言ってもいいくらいなのです。
しかしながら、ちとおっさんには敷居が高い使い方になるかなー、と思ったので、まずは「効率はよくないけど、扱いが簡単」な実装例として出してみました。

では、ソースコード全文を記載。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import commands
import os
import os.path
import pygtk
import gtk

__author__="kaoru"
__date__ ="$2010/08/28 13:42:04$"

#インストールコマンド群
#インストールコマンド群
installCommands=(
    (   #みくつべ♪インストールコマンド群
        "gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update",'gksudo "apt-get -y install mikutube"'
    ),
    (   #みくかべ♪インストールコマンド群
        "gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update",'gksudo "apt-get -y install mikukabe"'
        ),
    (   #audaciousスキンインストールコマンド群
        "gksudo apt-get update",'gksudo "apt-get -y install audacious"',
        #gksudo に渡す際に"でコマンドをくくる必要があるので、文字列定義としては'を使っている
        'gksudo "wget -P /usr/share/audacious/Skins/ http://mangareview.up.seesaa.net/image/mikumiku001.tar.gz"',
        'gksudo "wget -P /usr/share/audacious/Skins/ http://mangareview.up.seesaa.net/image/mikumiku_00220.tar.gz"',
        'gksudo "wget -P /usr/share/audacious/Skins/ http://mangareview.up.seesaa.net/image/mikumiku_003.tar.gz"',
        'gksudo "tar xvzf /usr/share/audacious/Skins/mikumiku001.tar.gz -C /usr/share/audacious/Skins/"',
        'gksudo "tar xvzf /usr/share/audacious/Skins/mikumiku_00220.tar.gz -C /usr/share/audacious/Skins/"',
        'gksudo "tar xvzf /usr/share/audacious/Skins/mikumiku_003.tar.gz -C /usr/share/audacious/Skins/"',
        'gksudo "rm -f /usr/share/audacious/Skins/mikumiku001.tar.gz"',
        'gksudo "rm -f /usr/share/audacious/Skins/mikumiku_00220.tar.gz"',
        'gksudo "rm -f /usr/share/audacious/Skins/mikumiku_003.tar.gz"',
    )
)

class MikunchApp:

    def __init__(self):

        #Set the Glade file
        self.gladefile = "mikunchu.ui"
        self.wTree = gtk.Builder()
        self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__)) + "/"+self.gladefile)
        #Create our dictionay and connect it
        dic = {
                "on_btCansel_clicked" : self.on_btnCancel_clicked,
                "on_btOK_clicked" : self.on_btnOK_clicked,
                "on_TopLevel_destroy" : self.on_TopLevel_destroy }
        #みくつべ♪のチェックボックスを取得
        self.chkMikutube = self.wTree.get_object("chkMikutube")
        #みくかべ♪のチェックボックスを取得
        self.chkMikukabe = self.wTree.get_object("chkMikukabe")
        #audaciousのチェックボックスを取得
        self.chkAudacious = self.wTree.get_object("chkAudacious")
        #スキン選択コンボボックスを取得
        self.cmbAudaciousConf = self.wTree.get_object("cmbAudaciousCfg")
        #スキン選択コンボのリストを取得
        lsAudaciousConf = self.wTree.get_object ("listAudaciousCfg")
        #スキン選択リストをコンボボックスに設定
        self.cmbAudaciousConf.set_model(lsAudaciousConf)
        cell = gtk.CellRendererText()
        self.cmbAudaciousConf.pack_start(cell, True)
        self.cmbAudaciousConf.add_attribute(cell, 'text', 0)
        self.cmbAudaciousConf.set_active(0)
        #シグナルとシグナルハンドラを接続
        self.wTree.connect_signals(dic)

        #トップレベルウィンドウを取得
        self.mainWindow = self.wTree.get_object ("TopLevel")
        #トップレベルウィンドウを表示
        self.mainWindow.show_all()

    def on_TopLevel_destroy(self, widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def on_btnOK_clicked(self,widget):
        #適用を行ってアプリケーションを終了する
        if self.chkMikutube.get_active() == True:
            #みくつべ♪インストールコマンドを実行
            for cmd in installCommands[0]:
                self.execCommand(cmd)
        if self.chkMikukabe.get_active() == True:
            #みくかべ♪インストールコマンドを実行
            for cmd in installCommands[1]:
                self.execCommand(cmd)
        if self.chkAudacious.get_active() == True:
            #Audacious用のスキンインストールと適用
            for cmd in installCommands[2]:
                self.execCommand(cmd)
            #適用スキンのコンフィグ名を定義
            confName = ("mikumiku001","mikumiku_002 ","mikumiku_003")
            #選択されているスキンの番号を取得
            sel = self.cmbAudaciousConf.get_active()
            #コンフィグファイルのパスを生成
            configpath = os.path.expanduser("~") + "/.config/audacious/config"
            #選択スキンをコンフィグファイルに設定
            self.editConfig("skin=", "skin=/usr/share/audacious/Skins/"+confName[sel], configpath)
            #壊れているスキンも有効にする設定をTRUEに
            self.editConfig('allow_broken_skins=',"allow_broken_skins=TRUE", configpath)

        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def on_btnCancel_clicked(self,widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    #コマンド実行のメソッド
    def execCommand(self,command):
        print command   #受け渡されたコマンドのデバッグ用プリント
        ret = commands.getoutput(command)
        print ret             #実行結果のデバッグ用プリント
        return ret

    #設定ファイルの書き換え
    def editConfig(self,keyword,replaceString,configPath):
        print keyword
        print replaceString
        #ファイルをオープン
        f = open(configPath)
        def edit(f):
            #キーワードが見つかったら置き換え文字列を返すローカル関数
            for txt in f:
                if txt.strip().find(keyword) >= 0:
                    yield replaceString+"\n"
                else:
                    yield txt
        #ファイルを読み込みながらキーワードを探して置換するローカル関数をコールし、新たな書き込み用のリストを生成する
        cnf = [x for x in edit(f)]
        #ファイルをクローズ(読み終わったので)
        f.close()
        #書き込み用に再度ファイルを開く
        fw = open(configPath,"w")
        #生成したリストを書き込む
        fw.writelines(cnf)
        #書き込んだのでファイルを閉じる
        fw.close()

if __name__ == "__main__":
    MikunchApp()
    gtk.main()
コメント増やして、試験的なゴミコードが残っていたので削除しました。(^^;

んで、最後にNetBeansのプロジェクトをアーカイブしたものを置いときます。

これで、基本的にセットアップヘルパに必要な項目は、ある程度であれば実装可能になるかと思います。
中身に関しては、詳細には、PythonチュートリアルGTKリファレンスを見れば、もっと柔軟な対応が可能かと思いますので、そのへんはトライ&エラーを繰り返してみてください。

「習うより慣れろ」なんて言葉があるくらいなので、自分で手を動かして、「これはOK、これはNG」を「体験」してしまった方が、手っ取り早い場合もあるわけです。
特におっさんには。(笑)

そう、気になる事務のあのコのパンツの色を妄想したり、胸チラ、ブラチラに悶々としているくらいなら、思い切って、声を掛けてみるのも、また手なのです。
人生において一番重要なおっぱいには触れないかも知れませんが、食事くらいなら付き合ってくれるかも知れませんし、そこの辺をうまくやれば、もしかしたら、おっぱいだって揉ませてくれるかも知れません。

妄想は大事ですが、妄想するだけでは、やはりダメなのです。
妄想が限界に達したら、やはり行動を起こさなければ。

もう、おっさんだから、とプログラミングも、気になるあのコも諦める必要はありません。
妄想力を高めて、実践していけば、必ずやプログラムは組めるようになりますし、気になるあのコは無理でも、別のチャンスがあるかも知れません。
行動しないヲタクはタダのヲタクなんです。(謎
#ちなみに、行動するヲタクは何かと言えば、単に「行動するヲタク」に過ぎませんが。

たったこれだけのコードで、GUIは実装可能です。
もちろんこれは、ほんの入り口に過ぎませんが。
バックエンドに、数々のCLIコマンドがあるから、GUIフロントエンドの処理が簡単に書ける、というだけに過ぎません。
バックエンドの行っている処理をPythonで書くことも、もちろん可能ですが、すでにあるものは有効に使いましょう。
コマンドはちょっと、というひとも、これを使えば、簡単にみくんちゅ♪が導入可能になるように、Webで調べたコマンドも、実は頻繁に使う必要があるなら、しかも、時々に応じてオプションを変える必要があるなら、GUIでフロントエンドを「自分の使いやすいように」作ってしまえばいいんです。

そうして、プログラミングに慣れてしまえば、気になるあのコの事務のルーチンワークを手助けするツールだって書けるようになるかも知れません。
そしたら、おっぱいだっt(ry

まずは、これを母体として、改造するだけで、ある程度のことは出来ると思います。
もっと凝ったことをしたければ、webに大量のサンプルが転がっていますし、Pythonで作られたツールであればOSSであることが多いと思います。
参考にする資料なんて、探せばいくらでもあるんです。

ちと乱暴なやり方であることは認めますが、目的を持ったトライ&エラーは、きちんと試行錯誤として働き、身につきます。
手を動かして覚えたことは忘れないんです。

書物による知識の蓄積も、まあ悪くはないでしょう。
ただ、おっさんには、そんなヒマはないでしょう。
まず、手を動かすことです。
そのための扉を用意し、ちょとだけ隙間を開けました。
この扉を開け放つためには、もっと力を蓄える必要があるかも知れません。
でも、もう扉の前に立っていて、僅かな隙間からですが、広大なプログラミングの世界が見えているのです。
そこで、立ち止まってしまうの自由です。
もっと力を蓄えて、広大なプログラミングの世界に踏み込むのも、また自由です。

このブログが、あなたの、プログラミングへのきっかけになれば、ワタクシとしては、大変喜ばしいことだと思います。

ま、まだまだ道標にもなってない、本当にサンプル解説だけの乱暴なやり方ですがね。

オプション記事も記載したことだし、これで本当に第一部完、となります。

第二部は、いつから始めるかは解りませんが、ちょっとだけ、道標になりそうな話でも書いていきましょうかね。(笑)

では、また近いうちに。

2010年9月9日木曜日

選択するという機能を加えるために--ドロップダウンリストを作ってみよう

一応、第一部完のつもりだったんですが、GTKウィジェットの使い方の中でも、わりと頻繁に使うと思われる、ドロップダウンリストに関してなんもやってなかったので、オプションとして追記します。

例によって、Gladeの作業です。


スロットを追加します。
そんで、コンテナから、横方向の水平ボックスを追加します。数は2個としときましょう。


左側にチェックボックスを追加します。


そしたら、次は右側にコンボボックスを追加しましょう。


このままでは、コンボボックスに表示する中身がないことになるので、「ComboBoxモデル」 を選択します。
現時点ではモデルはなにもないので、新規に作成して、名前を指定しましょう。
とりあえず、空のコンボボックスの選択用モデルが出来上がります。

そしたら、そのモデルに選択用の中身を並べていきます。






文字列なので、種類をgchararrayにするのを、忘れない様に。
そして、一応タイトルも付けときましょう。
その後、その下の箱の中に選択肢を入れていきます。


一応、これでGladeの作業は終わりになります。

コンボボックスに関しては、libGlade使ってた時の方が簡単だったかな、と思わないでもないです。
まだ、この辺に関してはGTKBuilderが熟成されていないのか。

この後、ソースコードを貼っとこうかと思ったんですがー。
ま、貼っときますか。(笑)

その前に。
これ、実行すると、こんな感じになります。

audaciousの方にチェックをつけて、ドロップダウンから選択すると、その選択したスキンが適用されます。
まあ、その前に設定ファイルがないと書き換えが行えないので、一度は実行しといて貰わないと困るわけですが。(笑)
#ま、その辺は、本格的に運用するもんでもないサンプルだからなー、くらいのところで。

というわけで、詳細解説は次回。
面倒なんだ、ドロップダウンは。(^^;

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import commands
import os
import os.path
import pygtk
import gtk

__author__="kaoru"
__date__ ="$2010/08/28 13:42:04$"

#インストールコマンド群
installCommands=(
    (   #みくつべ♪インストールコマンド群
        "gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update",'gksudo "apt-get -y install mikutube"'
    ),
    (   #みくかべ♪インストールコマンド群
        "gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update",'gksudo "apt-get -y install mikukabe"'
        ),
    (   #audaciousスキンインストールコマンド群
        "gksudo apt-get update",'gksudo "apt-get -y install audacious"',
        #gksudo に渡す際に"でコマンドをくくる必要があるので、文字列定義としては'を使っている
        'gksudo "wget -P /usr/share/audacious/Skins/ http://mangareview.up.seesaa.net/image/mikumiku001.tar.gz"',
        'gksudo "wget -P /usr/share/audacious/Skins/ http://mangareview.up.seesaa.net/image/mikumiku_00220.tar.gz"',
        'gksudo "wget -P /usr/share/audacious/Skins/ http://mangareview.up.seesaa.net/image/mikumiku_003.tar.gz"',
        'gksudo "tar xvzf /usr/share/audacious/Skins/mikumiku001.tar.gz -C /usr/share/audacious/Skins/"',
        'gksudo "tar xvzf /usr/share/audacious/Skins/mikumiku_00220.tar.gz -C /usr/share/audacious/Skins/"',
        'gksudo "tar xvzf /usr/share/audacious/Skins/mikumiku_003.tar.gz -C /usr/share/audacious/Skins/"',
        'gksudo "rm -f /usr/share/audacious/Skins/mikumiku001.tar.gz"',
        'gksudo "rm -f /usr/share/audacious/Skins/mikumiku_00220.tar.gz"',
        'gksudo "rm -f /usr/share/audacious/Skins/mikumiku_003.tar.gz"',
    )
)
class MikunchApp:

    def __init__(self):

        #Set the Glade file
        self.gladefile = "mikunchu.ui"
        self.wTree = gtk.Builder()
        self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__)) + "/"+self.gladefile)
        #Create our dictionay and connect it
        dic = {
                "on_btCansel_clicked" : self.on_btnCancel_clicked,
                "on_btOK_clicked" : self.on_btnOK_clicked,
                "on_TopLevel_destroy" : self.on_TopLevel_destroy }
        self.commands = []
        self.configs = []
        self.chkMikutube = self.wTree.get_object("chkMikutube")
        self.chkMikukabe = self.wTree.get_object("chkMikukabe")
        self.chkAudacious = self.wTree.get_object("chkAudacious")
        self.cmbAudaciousConf = self.wTree.get_object("cmbAudaciousCfg")
        lsAudaciousConf = self.wTree.get_object ("listAudaciousCfg")
        self.cmbAudaciousConf.set_model(lsAudaciousConf)
        cell = gtk.CellRendererText()
        self.cmbAudaciousConf.pack_start(cell, True)
        self.cmbAudaciousConf.add_attribute(cell, 'text', 0)
        self.cmbAudaciousConf.set_active(0)
        
        self.wTree.connect_signals(dic)

        self.mainWindow = self.wTree.get_object ("TopLevel")
        self.mainWindow.show_all()

    def on_TopLevel_destroy(self, widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def on_btnOK_clicked(self,widget):
        #適用を行ってアプリケーションを終了する
        if self.chkMikutube.get_active() == True:
            #みくつべ♪インストールコマンドを実行
            for cmd in installCommands[0]:
                self.execCommand(cmd)
        if self.chkMikukabe.get_active() == True:
            #みくかべ♪インストールコマンドを実行
            for cmd in installCommands[1]:
                self.execCommand(cmd)
        if self.chkAudacious.get_active() == True:
            #Audacious用のスキンインストールと適用
            for cmd in installCommands[2]:
                self.execCommand(cmd)
            sel = self.cmbAudaciousConf.get_active()
            configpath = os.path.expanduser("~") + "/.config/audacious/config"
            confName = ("mikumiku001","mikumiku_002 ","mikumiku_003")
            self.editConfig("skin=", "skin=/usr/share/audacious/Skins/"+confName[sel], configpath)
            self.editConfig('allow_broken_skins=',"allow_broken_skins=TRUE", configpath)

        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def on_btnCancel_clicked(self,widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    #コマンド実行のメソッド
    def execCommand(self,command):
        print command   #受け渡されたコマンドのデバッグ用プリント
        ret = commands.getoutput(command)
        print ret             #実行結果のデバッグ用プリント
        return ret

    #設定ファイルの書き換え
    def editConfig(self,keyword,replaceString,configPath):
        print keyword
        print replaceString
        f = open(configPath)
        def edit(f):
            '''キーワードが見つかったら置き換え文字列を返すローカル関数 '''
            for txt in f:
                if txt.strip().find(keyword) >= 0:
                    yield replaceString+"\n"
                else:
                    yield txt
        cnf = [x for x in edit(f)]
        f.close()
        fw = open(configPath,"w")
        fw.writelines(cnf)
        fw.close()

if __name__ == "__main__":
    MikunchApp()
    gtk.main()

微妙に増えてますが、増えてるのは、ほとんどがコマンドです。
長いんで、分割して書いてますけど。

これ、実行すると、audaciousのスキンを書き換えてくれます。

まあ、画像突っ込んだせいで、ページもでかくなりましたし、追加したコードの解説は次回やりますかね。
オプション部分だし、まあ、そんなにあせらんでもよいでしょう。

基本的に、これくらいの母体があれば、ダイアログベースのフロントエンドは、これを改造することで行けるはずです。

まあ、第2部は、もう少し「プログラム設計」的な部分を中心に、やっぱりGUIフロントエンドをやろうかと思ってますが。

なんか、こういうのをネタにして欲しい的なものがあれば、コメントいただければ考えてみます。

まあ、とはいえ、ここで頑張っても、人生において、もっとも重要なおっぱいには近づけない、というのがワタクシ的には腑に落ちないものがありますが、まあ、プログラミングとおっぱいは、まるで関係ないですからねぇ。
非常に残念です。

2010年9月8日水曜日

設定ファイルの編集機能を加えてみる

まあ、CLIツールを組み合わせても出来るのですが。
てか、むしろそっちの方が綺麗に作れる場合が多いのですが。
なにしろ、先人の知恵の結晶ですし。

とはいえ、ちょっとはPythonのリストっぽい機能を使っておかないと、Pythonのリスト機能の強力さが解らんかなーと思って機能を実装してみました。

やりたいのは、設定ファイルを読み込んで、そのキーワードでマッチする行を、新たな設定行と置き換える、というものです。
一応ターゲットとしては、Audaciousのconfigを書き換える、ことを目的としていますが、似たような構造のconfigなら書き換えることが出来ますし、まあ、置換後の文字列を工夫することで、XMLなんかも書き換えは可能です。
#単なるテキストファイルだしな。

    #設定ファイルの書き換え
    def editConfig(self,keyword,replaceString,configPath):
        f = open(configPath)
        def edit(f):
            '''キーワードが見つかったら置き換え文字列を返すローカル関数 '''
            for txt in f:
                if txt.strip().find(keyword) >= 0:
                    yield replaceString+"\n"
                else:
                    yield txt
        cnf = [x for x in edit(f)]
        f.close()
        fw = open(configPath,"w")
        fw.writelines(cnf)
        fw.close()
やってることは、キーワードと、書き換える文字列(キーワード含む)、書き換える設定ファイルのパスを受け取って、設定ファイルを開き、ローカル関数のジェネレータでキーワードとマッチしたら置き換える文字列を返して、マッチしなかったら、そのままの文字列を返す、という処理をして、リスと内包表記で新規にリストを生成し、それを設定ファイルとして書き出す、ということだけです。
Pythonではテキストファイルのオープンは組み込み関数で行えます。
そして、組み込み関数は読み込んだテキストをリストとして返してくれるのです。
なので、リストを処理するジェネレータにより、簡単に置換処理は記述可能になります。
書き込みも、リストを渡すとまとめて書いてくれるfileオブジェクトのメソッドがあるので、簡単に済みます。
ちと、このメソッド内でやってることを整理してみましょうか。
  • 設定ファイルを開いてリストに読み込む
  • 設定ファイルからキーワードを探す(設定ファイルの前後の空白は削除してキーワードと比較)
  • キーワードがれば置換文字列を、なければ、そのままの文字列を返す
  • 返された文字列をリストとして変数に格納
  • 設定ファイルとして書き込む
てな感じですかね。
言葉にしてみれば、案外たいしたことやってないんですが。(笑)
これ、例えばC言語なんかで書くと、もう少し面倒なんですよね。
実は、リスト内包表記だけでなんとかならないかなーと考えたんですが。
#他にもmapとかの高階関数でなんとかならんかな、とも思ったんですが。lambdaとか使って。
新しい設定を末尾に追加、ならもっとシンプルに書けるんですが、「同じ位置で置換」だと、ワタクシのPythonレベルでは、これが精一杯でした。(笑)
#もっとうまい書き方があるよ、というPythonエキスパートの方がいらっしゃれば、ご指導いただけるとありがたいですね。いや、マジで。

とはいえ。
Pythonのリスト構造、およびそのサポート機能があるから、この手のファイルの扱いもお手軽にできるわけで、リストの強力さを示す一旦でもあります。
ちなみに、ジェネレータに関しては、この辺に詳しい説明があります。
リストを処理するための専用機能って感じですかね。
そのジェネレータを、メソッドの中のローカル関数として定義して使用してます。
#他で使わないので。

で、このメソッドは、インストールコマンドを発行し、設定ファイルのダウンロードや展開などが終わった後、自動的に設定ファイルを書き換えるために使います。
サンプルの呼び出しとしては以下の通りでしょうか。
        configpath = os.path.expanduser("~") + "/.config/audacious/config"
        self.editConfig("skin=", "skin=/usr/share/audacious/Skins/xxxxx", configpath)
一行目でaudaciousの設定ファイルのパスを生成して、2行目で、書き換えを指示しています。xxxxxが具体的なスキンのファイル名ですね。

ちと、Pythonのリスト機能の強力さを示すために作ってみたサンプルなんですが、強力さがまったく伝わらないのは残念な限りです。(^^;

今のままでは、猫に小判、宝の持ち腐れ状態なので、この辺を綺麗に説明できるといいんですがね。
ま、ネタもそれ向きじゃないし、ある程度仕方ないのか。
リスト化された文字列を一気に加工、とか、そういうのが簡単に書けたりするんですが。
例えば、単なるテキストファイルを読み込んで、HTMLとして出力する、とか。

気になる事務のあのコにアピールするポイントを持っているのに、自分では全然気がついてない、という状態でしょうか。
そのことに気づけば、おっぱいが揉めるかも知れないのに、大変残念なことです。

というわけで、呼び出しの部分はコメントアウトしてますが、全体。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import commands
import os
import os.path
import pygtk
import gtk

__author__="kaoru"
__date__ ="$2010/08/28 13:42:04$"

#インストールコマンド群
installCommands=(
("gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update",'gksudo "apt-get -y install mikutube"'),    #一つめのチェックボックスのコマンド群
("gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update",'gksudo "apt-get -y install mikukabe"'),    #二つめのチェックボックスのコマンド群
("gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update","gksudo "apt-get -y install mikukabe"')     #三つめ以降は、このように増やしていく

class MikunchApp:

    def __init__(self):

        #Set the Glade file
        self.gladefile = "mikunchu.ui"
        self.wTree = gtk.Builder()
        self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__)) + "/"+self.gladefile)
        #Create our dictionay and connect it
        dic = {
                "on_btCansel_clicked" : self.on_btnCancel_clicked,
                "on_btOK_clicked" : self.on_btnOK_clicked,
                "on_TopLevel_destroy" : self.on_TopLevel_destroy }
        self.chkMikutube = self.wTree.get_object("chkMikutube")
        self.chkMikukabe = self.wTree.get_object("chkMikukabe")
        self.wTree.connect_signals(dic)

        self.mainWindow = self.wTree.get_object ("TopLevel")
        self.mainWindow.show_all()

    def on_TopLevel_destroy(self, widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def on_btnOK_clicked(self,widget):
        #適用を行ってアプリケーションを終了する
        if self.chkMikutube.get_active() == True:
            #みくつべ♪インストールコマンドを実行
            for cmd in installCommands[0]:
                self.execCommand(cmd)
        if self.chkMikukabe.get_active() == True:
            #みくかべ♪インストールコマンドを実行
            for cmd in installCommands[1]:
                self.execCommand(cmd)
        #configpath = os.path.expanduser("~") + "/.config/audacious/config"
        #self.editConfig("skin=", "skin=/usr/share/audacious/Skins/Default", configpath)

        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def on_btnCancel_clicked(self,widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    #コマンド実行のメソッド
    def execCommand(self,command):
        print command   #受け渡されたコマンドのデバッグ用プリント
        ret = commands.getoutput(command)
        print ret             #実行結果のデバッグ用プリント
        return ret

    #設定ファイルの書き換え
    def editConfig(self,keyword,replaceString,configPath):
        f = open(configPath)
        def edit(f):
            '''キーワードが見つかったら置き換え文字列を返すローカル関数 '''
            for txt in f:
                if txt.strip().find(keyword) >= 0:
                    yield replaceString+"\n"
                else:
                    yield txt
        cnf = [x for x in edit(f)]
        f.close()
        fw = open(configPath,"w")
        fw.writelines(cnf)
        fw.close()

if __name__ == "__main__":
    MikunchApp()
    gtk.main()

これで、ほぼ予定していた機能は実装し、ある程度の説明は終わりました。

まだ完全な理解は得られていないと思いますが、あちこちで指し示したPythonのチュートリアル、それから、そこから辿れるリファレンスや、キーワードで検索などしてみれば、今後の拡張や、理解不足な部分は、補えるのではないか、と思います。

何より、このサンプルを、自分なりに改造してみて、失敗して、色々と身に染みることで、得られることが大きのではないかな、と思います。
これを、そのまま使うのではなく、まず改造してみる、そのことから始めて見るといいのではないでしょうか。

気になる事務のあのコを誘うのだって、マニュアル通りでは、彼女も飽き飽きしているかも知れません。オリジナルなアプローチだとうまく行く可能性だってあるし、直球で「おっぱいを揉ませてくれないか?」でうまく行く可能性だって0ではないのです。(そんなオカルトありえません)

というわけで、一旦、第一部完、かな、と思ってますが、オプションというか、補足として、GTKコントロールの他のコントロールの使い方、特にセットアップヘルパ辺りでは重要な、ドロップダウンリストによる選択のサンプルなど、次回以降取り上げてみようかと思います。
ま、この辺もネットを漁れば、やまほどサンプルなんて出てくるんですが、せっかくなのでGladeを使った方法を説明してみましょうかね。



設定ファイルの書き換えが解らん、との話がありましたので、ちと追記します。
「らしい書き方」をしてしまったが為に、初学者であるおっさんには、ちと難解な構造になってしまっていたようです。(^^;

で、まあ、何も無理してローカル関数やジェネレータ、リスト内包表記なんぞ使わんでも、以下の書き方で書けます。
ま、上記三つを使うには題材に無理がありましたかね。
こちらの表記のほうが結果シンプルになるので、何やってるのかは解り易いんじゃないかと。
まあ、実装の方法はいくつかあり、常にベストを探す必要はないでもないんですが、今回、ちとリストにこだわり過ぎたがための失敗ですな。(笑)

まあ、ぬるく書いてるので多めに見てやってください。
ってことで、もう少し何やってるのか解りやすく書き直したeditConfigメソッドを以下にご紹介。
    #設定ファイルの書き換え
    def editConfig(self,keyword,replaceString,configPath):
        print keyword
        print replaceString
        #ファイルをオープン
        f = open(configPath)
        #新しいコンフィグファイル用の空リスト
        cnf =[]
        #変数tに開いたファイルから一行づつ読み込む
        for t in f:
            #キーワードが含まれていれば、行を置き換える
            if t.strip().find(keyword) >= 0:
                cnf.append( replaceString+"\n")
            else:
                cnf.append(t)
        #ファイルをクローズ(読み終わったので)
        f.close()
        #書き込み用に再度ファイルを開く
        fw = open(configPath,"w")
        #生成したリストを書き込む
        fw.writelines(cnf)
        #書き込んだのでファイルを閉じる
        fw.close()
ぶっちゃけ、ローカル関数(ジェネレータ)にしてたのを外に出しただけ。(笑)
リスト内包表記にこだわらなければ、こっちの方がシンプルですな。

f=open(configPath)で設定ファイルを取得したあと、for t in f:の部分で、tという変数にfというリストから、一行づつ、中身を取り出しています。
その後に続くif文で、キーワードがある場合には、置換用の文字列(行)と置き換えて、新しいリストに追加し、それ以外のものは、そのまま新しいリストに追加する、というシンプルな処理です。

これなら、おっさんにも、まだ解りやすいんじゃないかと思うんですがね。
さて、どんなもんでしょうか。

2010年9月7日火曜日

リストとかタプルとか

前回、コマンドを実行するために格納する領域として「リスト」の一種であるタプルを使いました。
これ、Lisp由来の機能と聞きますが、まあ、Pythonを強力なものとしているひとつの理由でもあるデータ構造なんです。

どのくらい強力かというと、気になる事務のあのコのおっぱいに触れるかも知れないくらい強力なデータ構造なんですよ。(無理です)
ちなみに、詳細な説明はこの辺にあります。
あと、この辺も参照しとくといいかも知れません。

ある値の連続した構造をもつデータをPythonで「リスト」と呼びます。
この、「値が連続している」ということが重要で、例えばfor文なんかで一括でループ処理をする場合に便利だったりします。
まあ、Pythonの場合には、このリスト構造を処理するための、かなり強力な機能があるのですが、ま、それはまた後ほどってことで。

なんで、Pythonリストが強力かというと、「変数の連続したもの」であるからです。
Pythonの変数は四次元ポケットである、と説明しました。何でも入るんです。その何でも入るのが、連続して繋がってるのがリストなんですね。
データを表現するのに、これほど「都合のいい」入れ物は他にないと思うんですよ。
もう、なんでも入ります。
リストの中にリストだって入れられるわけです。

そう、先日のプログラム構造のこの部分が、リストの中にリストが入った入れ子の状態ですね。

#インストールコマンド群
installCommands=(
("gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update",'gksudo "apt-get -y install mikutube"'),    #一つめのチェックボックスのコマンド群
("gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update",'gksudo "apt-get -y install mikukabe"'),    #二つめのチェックボックスのコマンド群
("gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update","gksudo "apt-get -y install mikukabe"')     #三つめ以降は、このように増やしていく
)

一つめのチェックボックスに対応する項目としてinstallCommand[0]として、コマンド群を取得していましたが、これは2次元のリストなので、installCommand[0][0]としてやることで、"gksudo add-apt-repository ppa:khf03353/ppa-kaorin"という文字列が取得できます。
まあ、リストなので、for文に入れてしまうのが簡単なので、前回のプログラムでは、そのまま、一次元のリストとして扱っていましたが。

んで。
リストには、以下の2種類があります。
  • [] リスト:中の値を入れ替えることができるし、追加、削除も可能。変数の塊。
  • () タプル:中の値を入れ替えることも追加/削除も不可能。定数の塊。
コマンド群はタプルとして定義しました。
これは、プログラム実行中に、値が書き変わってしまっては困るからです。
間違えて代入しても、タプルなら実行時に怒られるので安心です。(笑)

ただ、どちらもリストには違いありませんし、値(中身)の書き換えや、連結、削除が可能かどうか、という点を除けば、性質的な違いはありません。

まあ、アレですよ、リストは出し入れ自由で、いろいろ出来ますが、注意しないとキケンですが、タプルは、見てるだけなので安全ってことです。(なにを
#中出し厳禁。

どちらも女体データ構造としては同様に魅力的ですが、用途が違う、ということでしょうか。

ま、こんな説明では、イマイチ便利さが伝わらないんじゃないかと思うんですが。
コンピュータにデータ処理をさせるというのは、言ってしまえば、連続データを処理するのが一番効率がよいわけです。
データベースなんか、まさにその典型なんですが。
で、その連続データを、「形を気にせず扱える」ことが、このリスト構造の強みなのです。

とはいえ、今回のサンプル、この手のリスト構造の強力さをアピールするようなツールではないので、まあ、仕方ないのか。
いずれにせよ、連続するデータを扱う場合には、このリスト構造を使うと便利、ということだけでも覚えておいて貰いましょう。

せっかくなので、次回、設定ファイルの書き換えをやります。
少し、ホンの少し、今よりもリストの強力さが解るようなサンプルのメソッドを作ります。(^^;

コマンドを実行する

いよいよ、コアの機能の実装です。
そもそも、みくんちゅ♪セットアップヘルパなので、みくんちゅ♪の各種ツールやスキンをインストールするためのコマンドが実行できないと何の意味もないのです。
そのために、コマンド実行を可能とします。

なんていうんですかね、今の状態は。
そう、「生殺し」これです。ガマン汁も限界、目の前にアレが見えているのに、そこにたどり着く手段が見えない状態、ではないでしょうか。

まあ、意図して生殺しにしたわけではなく、それなりの準備が必要なので、ここまで引っ張ってきたわけですが。
前戯が重要なのと同じです。(ぉ
特におっさんは(ry

まあ、与太はともかく。
まずは、コマンドを実行するためのメソッドから見てしまいましょうか。
    def execCommand(self,command):
        print command   #受け渡されたコマンドのデバッグ用プリント
        ret = commands.getoutput(command)
        print ret       #実行結果のデバッグ用プリント
        return ret
こんだけです。簡単ですね。(笑)
焦らされた割には、案外本番はあっけないものなのです。

デバッグ用のprint文が入っているので、ちょいと長くなってますが、実質はcommands.getoutput(command)一行のメソッドとなります。
これは、Pythonのライブラリに用意されている、コマンドを実行して、出力結果を返す、というメソッドで、詳細はここにあります。
マトモなプロセス間通信を自前で行う場合には、別の手段も用意されていますが、今回は上位ライブラリのこのメソッドで十分かと思います。
おっさんですので、面倒な手間は省きたいのが本音でしょう。

このメソッドは、単純に、引数として与えられたコマンドを実行するだけです。
その結果をメソッドの戻り値として返していますが、まあ、実際には利用しないと思います。
マトモなエラー処理を組み込んで、エラー発生時にはコマンドを中断するとか、そういう異常系の処理を組み込む場合にはキチンとチェックする必要がありますが、まずは「最低限」の動作を目標としているので、異常系処理は手抜きをしてしまいます。(笑)
#他人にリリースするときには注意しましょう。公開とかな。
#ま、そういう意味では、このツール、公開前提なので、ちゃんとトラップした方がいいんだけどね。
で、一点注意が。このコマンド実行のメソッドですが、当然のごとく、端末でYes/Noとかの問い合わせがある場合には、その入力ができません。(ぉ
なので、コマンド設定の際に注意が必要です。具体的には後述しますが、コマンドのオプションには大抵、こういうバックエンドで使われることも考慮したオプションが用意されているものですので、コマンド指定時に、そのオプションを指定してあげないと、ハングアップします。(^^;

では、肝心のコマンドはどこから渡されて、どこから、このメソッドが呼ばれるのか。
まあ、もうお解りかと思いますが、OKボタンを押されたシグナルハンドラから、呼ばれます。
具体的にはこのようにコールされるわけです。
#OKボタン押下
    def on_btnOK_clicked(self,widget):
        #適用を行ってアプリケーションを終了する
        if self.chkMikutube.get_active() == True:
            #みくつべ♪インストールコマンドを実行
            for cmd in installCommands[0]:
                self.execCommand(cmd)
        if self.chkMikukabe.get_active() == True:
            #みくかべ♪インストールコマンドを実行
            for cmd in installCommands[1]:
                self.execCommand(cmd)
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

キモは、以下の部分。
            #みくつべ♪インストールコマンドを実行
            for cmd in installCommands[0]:
                self.execCommand(cmd)
繰り返しのためのfor文を使用して、commands[0]というリストの1項目目から、さらにリストを取得して、cmdに受け取り、それをexecCommandのメソッドの引数としてメソッドコールをしているのです。
インストール時には複数のコマンドを実行することになる場合が多く、例えば以下のような書き方もあります。
                …
                self.execCommand("gtsudo apt-get update")
                self.execCommand('gtsudo "apt-get -y insall mikutube"')
                …
コマンド数分だけ、メソッド呼び出しを並べてしまう、という方法ですね。
この方が或いはわかり易かったかも知れませんが、せっかくPythonを使ってるので、柔軟なリストを使用するのがスジというものではないでしょうか。
ウラスジが気持ちイイようなものです。(違

ちなみに、先程コマンドのオプションの話を出しましたが、上記の例の場合、apt-get -y install がそれに当たります。apt-getは周辺ライブラリのインストールが必要な場合、問い合わせを行いますので、このオプションですべてYesと答える、という指定をしています。
イエスマンは嫌われるかも知れませんが、この場合にはハングアップしてしまうので、仕方ないのです。Noと言える勇気は必要かも知れませんが、無回答でだんまりを決め込まれるほど、困ることはないので、選択できないなら、Yesと答える方が安全な場合が多いのです。(ぉ
似たような例だと、rmコマンドなんかもそうですね。削除確認があるので、オプション指定をしてあげないと、GUIフロントエンドで使う場合には不自由です。

Pythonの変数は非常に柔軟なポケットである、という話をしました。
まさにドラえもんの四次元ポケットのごとく、なんでも入ります。
このリストをひとつの変数に格納していることなど、その典型ではないでしょうか。
ちなみに、このリストは、グローバル変数として、以下のように定義されています。
#インストールコマンド群
installCommands=(
("gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update",'gksudo "apt-get -y install mikutube"'),    #一つめのチェックボックスのコマンド群
("gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update",'gksudo "apt-get -y install mikukabe"'),    #二つめのチェックボックスのコマンド群
("gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update","gksudo "apt-get -y install mikukabe"')     #三つめ以降は、このように増やしていく
)
これは、「タプル」という形式のリストです。
ま、詳細は次回やりますけど、どのように格納されているかのイメージだけ図示します。
この様に2次元の表イメージで作成されたコマンド群を定義し、それを実行しているわけです。
行の部分が、それぞれのアプリケーション用のコマンド群、列の部分がfor文で逐次実行されるコマンドそのもの、ということになりますね。
このような形式で、チェックボックスと、それに対応するコマンド群の行、およびコマンドを追加していけば、みくんちゅ♪セットアップヘルパは、完成します。
#とかいいつつ、実は重要な機能がひとつ抜けてるけど、コマンドでも実現できるしなー。

全体像としては以下のようになります。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import commands
import os
import os.path
import gtk

__author__="kaoru"
__date__ ="$2010/08/28 13:42:04$"

#インストールコマンド群
installCommands=(
("gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update",'gksudo "apt-get -y install mikutube"'),    #一つめのチェックボックスのコマンド群
("gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update",'gksudo "apt-get -y install mikukabe"'),    #二つめのチェックボックスのコマンド群
("gksudo add-apt-repository ppa:khf03353/ppa-kaorin","gksudo apt-get update","gksudo "apt-get -y install mikukabe"')     #三つめ以降は、このように増やしていく

class MikunchApp:

    def __init__(self):

        #Set the Glade file
        self.gladefile = "mikunchu.ui"
        self.wTree = gtk.Builder()
        self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__)) + "/"+self.gladefile)
        #Create our dictionay and connect it
        dic = {
                "on_btCansel_clicked" : self.on_btnCancel_clicked,
                "on_btOK_clicked" : self.on_btnOK_clicked,
                "on_TopLevel_destroy" : self.on_TopLevel_destroy }
        self.chkMikutube = self.wTree.get_object("chkMikutube")
        self.chkMikukabe = self.wTree.get_object("chkMikukabe")
        self.wTree.connect_signals(dic)

        self.mainWindow = self.wTree.get_object ("TopLevel")
        self.mainWindow.show_all()

    def on_TopLevel_destroy(self, widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def on_btnOK_clicked(self,widget):
        #適用を行ってアプリケーションを終了する
        if self.chkMikutube.get_active() == True:
            #みくつべ♪インストールコマンドを実行
            for cmd in installCommands[0]:
                self.execCommand(cmd)
        if self.chkMikukabe.get_active() == True:
            #みくかべ♪インストールコマンドを実行
            for cmd in installCommands[1]:
                self.execCommand(cmd)
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def on_btnCancel_clicked(self,widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def execCommand(self,command):
        print command   #受け渡されたコマンドのデバッグ用プリント
        ret = commands.getoutput(command)
        print ret             #実行結果のデバッグ用プリント
        return ret

if __name__ == "__main__":
    MikunchApp()
    gtk.main()

アプリケーションとしては、これで完成です。
コマンドを逐次実行して行き、選択されたみくんちゅ♪アプリや設定ファイルなどを自動的にダウンロード、展開してくれることになります。

GUIフロントエンドなんてこんなもんで作れてしまうのです。
全体の行数は100行にも満たない。
まあ、Python内部でロジックを関数やメソッドで定義していけば、行数は圧倒的に増えていきますが、まずは入門というところでは、Linuxに多数用意されている便利なCLIコマンドを利用したフロントエンドから入ってしまう、というのもひとつの手ではないかと、思います。
言語の仕組みさえ覚えてしまえば、いくらでも追加の学習や、調べてしまうことで、拡張すること、自前でロジックを実装することだって出来ますし。
まずは、やってみることです。

もしかしたら、気になる事務のあのコだって、あなたに興味があるかも知れません。
誘ってみれば、食事くらいは付き合ってくれるかも知れないのです。
もしかしたら、人生において、大変重要なおっぱいだって揉ませてくれるかも知れないじゃあないですか。(そんなオカルト有り得ません。)

まずは、何事もチャレンジ、というところでしょうか。
その後のアプローチや、振られたから会社に居づらくなったとしても、ワタクシはなんの責任も負えませんが。(笑)

それから比べれば、プログラミングは、圧倒的に試行錯誤がやり易いのです。
飽きなければ。(笑)

次回、基礎編としては、座学の最後になるかも知れない、リストに関して解説します。
#もうひとつ、機能を追加するかな、とは考えてますが…。ま、リクエスト次第。

2010年9月6日月曜日

分岐を組み込んでみる

とりあえず、前回までの座学で制御文をやったので、せっかくなので、というかメインな機能なので、条件分岐、if文を組み込んでみましょう。
NetBeansを起動してください。

コンストラクタ(__init__)に手を加えます。
        dic = {
                "on_btCansel_clicked" : self.on_btnCancel_clicked,
                "on_btOK_clicked" : self.on_btnOK_clicked,
                "on_TopLevel_destroy" : self.on_TopLevel_destroy }
        self.chkMikutube = self.wTree.get_object("chkMikutube")
        self.chkMikukabe = self.wTree.get_object("chkMikukabe")
下の2行が新規に追加する行です。
何をしているのかというと、GTKBuilderから、チェックボックスのGTKオブジェクトを取得しています。
チェックボタンをGladeで作成したときに、解り易い名前を付けましょう、と書いたことを覚えてるでしょうか?おっさんなので、忘れてますよね。(笑)
なぜ、解り易い名前を付けましょう、と書いたのかというと、この時のためだったのです。
Gladeで設定しているチェック・ボタンの名前を指定して、get_objectすることで、GTKBuilderが保持しているGTKウィジェットツリーからGTKオブジェクトを取得することが出来るのです。
なので、Gladeでの作業の時に解り易い名前を付けましょう、って書いといたんですね。
実は、ここでget_objectに失敗しても、エラーは発生しません。(^^;
代入先の変数にNoneが入るだけなのです。案外解りにくいエラーになるようなので、ここは要注意ですかね。
で、変数ですが、クラススコープの変数に代入していることに注意してください。
なぜなら、この後、別のメソッドの中で使用するので、ローカルスコープの変数では、都合が悪いのです。

そう、つまり、このようなオブジェクトの参照をするのは、家族写真であり、旅行の動画なのですね。
エロ動画ではない、ということです。
少なくとも、クラス内で公開されたものとしておいた方が、扱いがラクです。
#ま、使う時に取得してもいいんだけどさ。

なぜコンストラクタで取得しているのか、という疑問もあるかも知れませんが。
端的に言えば、変数の初期化のひとつである、というだけの理由です。
この手のオブジェクト参照は、クラス内のあちこちで行われる可能性が高く、毎度毎度取得するよりは、初期化の一環として、クラススコープ内で取得し、その変数を参照する方が、手間が掛からない、というだけですね。

速度的なメリットも、微妙にないでもないですが、まあ、少なくともひとに解るレベルではありませんし、そもそもループもしないような処理で、オブジェクト取得の速度にこだわるよりも、別のところにこだわった方が、よほどマシかと思います。
今日の、気になる事務のあのコのパンツの色程度にどうでもいいところです。
#あんがい重要かも知れない、と今思った。が、知りようのないことでもあるのも事実だ。

次に、OKボタンのシグナルハンドラに手を入れましょう。

        def on_btnOK_clicked(self,widget):
        #適用を行ってアプリケーションを終了する
        if self.chkMikutube.get_active() == True:
            print("みくつべ♪インストールコマンドを実行")
        if self.chkMikukabe.get_active() == True:
            print("みくかべ♪インストールコマンドを実行")
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

コンストラクタで設定した変数から、現在のチェック状態を取得して、条件判定を行っています。
チェック状態を取得するGTKオブジェクトのメソッド、get_active()をコールして、その戻り値を比較しています。
まあ、条件は、真偽を判定するので、真偽値を返すget_active()メソッドの場合には、そのままでもよい(== True)は端折ってしまっても問題ありません。
単にワタクシのくせで、真偽値の場合でも、このように比較として書いているだけです。

で、チェックボックスがチェックされている場合には、今のところ処理をなんも書かないわけにも行かないので、文字列を出力するようにだけしてみました。

F6を押して実行すると、以下のようになるかと思います。
これは、すべてチェックをつけて、OKボタンを押したあとの画面スナップですね。
F6で実行時のprintの結果は、あの用に実行時ウィンドウに表示されます。
#デバッグの時も重宝します。


だいぶ完成形に近づいてきました。
あとは、具体的なインストールの中身を記述すれば、一応の完成ということになります。
次回は、その辺少し先に実装してしまいたいと思っています。

今回の条件分岐を組み込んだ全体のソースコードは以下のようになっているはずです。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import commands
import os
import os.path
import pygtk
import gtk

__author__="kaoru"
__date__ ="$2010/08/28 13:42:04$"

class MikunchApp:

    def __init__(self):

        #Set the Glade file
        self.gladefile = "mikunchu.ui"
        self.wTree = gtk.Builder()
        self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__)) + "/"+self.gladefile)
        #Create our dictionay and connect it
        dic = {
                "on_btCansel_clicked" : self.on_btnCancel_clicked,
                "on_btOK_clicked" : self.on_btnOK_clicked,
                "on_TopLevel_destroy" : self.on_TopLevel_destroy }
        self.chkMikutube = self.wTree.get_object("chkMikutube")
        self.chkMikukabe = self.wTree.get_object("chkMikukabe")
        self.wTree.connect_signals(dic)

        self.mainWindow = self.wTree.get_object ("TopLevel")
        self.mainWindow.show_all()

    def on_TopLevel_destroy(self, widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def on_btnOK_clicked(self,widget):
        #適用を行ってアプリケーションを終了する
        if self.chkMikutube.get_active() == True:
            print("みくつべ♪インストールコマンドを実行")
        if self.chkMikukabe.get_active() == True:
            print("みくかべ♪インストールコマンドを実行")
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def on_btnCancel_clicked(self,widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

if __name__ == "__main__":
    MikunchApp()
    gtk.main()

乳首が関係なくて、がっかりしたかも知れませんが、今回作成のGUIツールに、元々乳首は無関係なので、仕方がないのです。残念ですが。(笑)

人生において、おっぱいは大変重要ですが、プログラミングに関しては、あまり関係がありません。
残念なことです。

というわけで、次回はコマンド実行のためのメソッドと、コマンドをプログラム中に記述するための変数の形式、タプルやリストについて、触れたいと思います。
まあ、タプル、リストに関しては、Pythonでは重要な話なので、また別途説明する機会を設けたいと思ってますが、まずは動くものを作ってしまいましょうか。
ガマン汁も限界に達していることでしょうし。

というわけで、次回、プログラムとしては、動作するもの、が完成するはずです。

2010年9月5日日曜日

プログラムの中身を書く--メソッドを書いてみましょう

メソッド、関数を書くというのは、まあよくメソッドを定義する、なんて言い方をされますが。
ある種業界用語なので、まあ、そんなもんなんだ、くらいに思ってもらえばいいのかな、とか思いますが。
何も定義なんて、カッコいい言い方をしなくても良さそうなもんですが、ワタクシなんぞは業界が長いので、もはや定義と言った方がしっくりくるので、ここでも、「メソッドを定義する」とかカッコ良く言ってみたりします。

では、メソッドを定義してみましょう。(笑)

メソッドを定義する


Pythonでメソッド/関数を定義する場合には、以下の書式となります。

def メソッド名(self,引数):
    メソッドの中身…

これは、ハンドラの部分でも出てきたので、まあ、そう書くのだな、というのがすでに解っているかも知れません。
#解っていないかも知れません。(笑)

とはいえ、これはPythonの場合、イヤだと言っても言語仕様で、そう書くのが定められているので、もはや逃げ道はないのです。(笑)
仕方ないので、defから始めましょう。
#defはdefineの略で「定義する」 ってことです。

メソッド名は、変数名と同様に、実は大変重要なものです。
解り易い名前を付ける必要があります。
例えば以下のような名前でしょうか。

def push_leftChikubi():

このように書いておけば、後から見たときにも、「ああ、左の乳首を押された時の動作なのだな」とすぐに解ります。(ぉ
長すぎるのも良くないので、ある程度コンパクトにまとめるのが吉でしょう。

引数ってなんぞ?


ちなみに「ひきすう」と読みます。まあ、読み方なんてどうでもいいじゃん、とか思うんですが、なぜか「引数」は「ひきすう」と読むことになってますので、そう読みましょう。
#なんで湯桶読みなんでしょうね。

パラメータなんていう言い方もしますね。
そのメソッドが呼び出されるときに必要な情報として与えられるものが引数ということになります。
メソッドのアクションを実行するときに必要な「値」なので、パラメータなんでしょうかね。
メソッドの場合には、必ず最初の引数は、自分の属するクラスを表すパラメータ「self」になります。
NetBeansでクラス内にメソッドを書くと、自動的に提案してくれるので、そのまま適用してしまえば、いちいちselfを入力することもないので助かります。
専用環境というのは、このような補完が効くので、非常にラクと言えましょうか。
まあ、普通のテキストエディタでも書けるようなレベルでしかないんですが、いまのところ。
巨大なアプリケーションを作成する場合には、効率面で効いてくるような点でしょうか。

余談になりましたが。(笑)

シグナルハンドラの場合には、selfと同時に、widgetも指定します。
これは、シグナルハンドラの時に触れましたが、シグナルを引き起こしたGTKウィジェットを受け取るためです。
シグナルによっては、他の値が引数として渡ってくることもあります。
この辺が詳しいでしょうかね…と思って探してみたら、まとまってるとこないな。
個々のGTKウィジェットによっても違うので、GTKリファレンスの方に記載があると思われますが。
ま、あんまし使うこともないので、必要になったら調べる、程度の考えで問題ないと思われます。
#てか、知らないってことは使ってないんだよ。(笑)

その他にも、シグナルハンドラ以外にクラス内にメソッドを定義する場合があります。
その場合に、引数を指定する場合があります。
先程の例で行くとこんな感じでしょうか。

def push_leftChikubi(self,push_type):
    指で押された
    舌で押された

左乳首を押された場合の押された種類をpush_typeという引数に入れてメソッドを呼び出すことで、push_leftChikubiのアクションを変えることが出来るわけです。

制御文


引数でアクションを変えることができる、という話をしましたが、そのように関数やメソッド内部で、プログラムの実行順番を制御することを制御文といいます。
制御文は大きく分けると、「分岐」と「ループ」になりますか。
#他になんかあったかな?

まずはよく使うと思われる、分岐の方から。
構文的には、以下のようになります。

if 条件 :
    条件が真だった場合
else:
    条件が偽だった場合

elseはなくてもいいので、よく使う形式としては、if 条件 : 〜の方ですかね。
条件が真だった場合、上限が偽だった場合の部分にインデントが付けられていることに注意してください。
ifの条件を満たしたとき、満たさなかったときに、実行されるブロックも、やはりインデントで区分します。

先程のメソッドを例に取ると以下の感じになります。
#たぶん実行は出来ません。
def push_leftChikubi(self,push_type):
    if push_typ == "指":
        「ああん」という
    if push_typ == "舌":
        「あっ」という

せっかくだから、実行可能な形にしてみると、以下の感じ。
def push_leftChikubi(self,push_type):
    if push_typ == "指":
        print("「ああん」")
    if push_typ == "舌":
        print("「あっ」")

引数に"指"とか"舌"を入れてコールすると、Pythonが喘ぎます。(笑)

ま、与太はともかく。

これで実行時に条件によって処理を分岐させる、ということが可能になります。

ループは「繰り返し」のことです。
同じ処理を何回か繰り返す場合に使います。
ループには大きく二つあり、「決められた回数繰り返す」ものと「条件が真の間繰り返す」ものがあります。
まずは、「決められた回数繰り返す」方から。
forループといいます。
#シャレではなく。

構文的には以下の感じ。
for 変数 in 繰り返すためのリスト:
    繰り返し処理

この場合も、繰り返し処理の部分にはインデントが付けられていて、繰り返しを行う処理ブロックを明確に区分しています。


指定回数繰り返す場合には、組み込み関数であるRangeを使って以下のようにします。
for a in Range(0,10):
    print("「うふふ」")
実行すると、Pythonが「うふふ」と10回言います。気味が悪いですね。(笑)

条件が真である間繰り返す方がwhileというループになり、以下の書式です。
while 条件:
    繰り返し処理

条件の部分を満たしている間ループを繰り返します。
つまり、条件を変更する処理を書き忘れると無限にループすることになります。
注意しましょう。
こちらは例文は記載しませんが、今回はたぶん使わないので。

例によって、詳しくはこちらに記載されています。

というわけで、座学に飽きてきたので、次回は、実際に制御文を使って、実行時に判定をする、ってのを入れてみましょうか。
手を動かさないと飽きるし、そろそろガマン汁もいい感じになってるのではないでしょうか。
とはいっても、乳首関係ないので、変な期待はするだけ無駄です。(笑)

2010年9月4日土曜日

ボタンを押されると何が起きるのか--シグナルとハンドラとプログラム

えー、前回、GTKオブジェクトのシグナルをGladeで設定して、Pythonコードに、そのシグナルハンドラを実装してみる、ということをやりました。

シグナルとハンドラ


そもそもシグナルってなんぞ、という疑問もあると思います。
Gladeを起動して、シグナルのタグを見ると、モノによってはたくさんのシグナルが並んでいます。
これ、なにかっていうと、そのGTKウィジェットに対して行われた操作に対して、GTKウィジェットが発することが出来る「通知」なんです。
ボタンなら、「押されました」とか、チェックボックスなら「チェック状態が変わりました」とかですね。
Linuxの場合には、基本的にユーザー操作はXを経由してGTKに送られ、GTKがプログラムに送ります。
プログラムは、送られてきたシグナルを選んで、ハンドリングして、処理を行うわけです。
イメージとしては以下の感じ。
#イメージね。(笑)


そんで、この「ユーザー操作」を「待ち受ける」処理が、gtk.main()なんですね。
プログラムの下の方にある。
メッセージループなんて言い方もしますが、通知されてくるメッセージ(シグナル)を待ち受けて、メッセージが来たら、それを各ハンドラに振り分けを行うわけです。

ちょっと前に、関数とは、入力に対してアクションを行う箱だ、と説明しましたが、GUIアプリそのものが、そういう側面を持っています。
ユーザー入力に対して、あるいはシステムからのシグナルに対して、「どんなアクションを起こすのか」を延々と記述していくのが、GUIアプリ作成ということになります。

非常にコンピュータ的というか。(笑)
「仕事をしろ」と指示をしないと何もしてくれないのがGUIなのです。
CLI(CommandLineInterface)アプリケーションは、起動すると同時に、物凄い勢いで仕事をしてくれるひとなわけですが、GUIアプリは全然違います。いちいち、これをしろ、あれをしろ、と命じてやらないと何もしてくれないし、プログラムも、そういう書き方になるんですね。
「あれをしろって言われたら、じゃあこうするか」みたいな。
#まあ、CLIアプリは起動時のオプションで、すでにやることが指示済みなので突っ走れるってワケですが。

GUIアプリの基本的な考え方は、シグナルを待ち受けて、シグナルが来たら、対応する処理をする、ってことになります。

とはいえ。
全部のシグナルを処理する必要なんてないので、そこは心配は要らないのです。
処理が必要なシグナルだけを拾って、自分のプログラムでは、それをハンドリングするだけでOKです。
今回のサンプルでは、ウィンドウが閉じられたとき、そして、OK、キャンセルボタンが押された時の三つのシグナルさえハンドルしてしまえば、あとは、GTKの標準の動きに任せてOKです。

ちょっと凝ったこと、たとえば、このチェックボックスをチェックされたら、同時に別のチェックボックスもチェックをつける、とかそういうことをやりたい場合には、また個別に対象のチェックボックスのシグナルをハンドルして、処理を付け加えることになりますが、まあ、おっさんは、そんな凝ったことしなくてもいいと思うのです。
まずはシンプルな処理、シンプルな実装で、GUIとPythonの「最低限の動作」を覚えてしまいましょう。
応用は、その後でもいくらでもよい文献がありますし、自分で調べることもできます。

ボタンが押されたら、どうなるのというイメージをここでは持ってもらえれば十分です。
そして、じゃあ、こっちのボタンをおしたらどうなるのかな〜、と妄想を広げてもらえばいいんじゃないかと思います。(笑)
おっさんですので、妄想はきっと得意でしょう。おっぱいの先っちょを押したら、そこからシグナルが発生して「あっ…」と声が出る、みたいなもんです。シグナルとハンドラの関係は。
おっぱいの先っちょを押す、というシグナルに対して、「あっ…」と声を漏らす、という処理が組み込まれているだけ、なのです。

そう、GUIは、そういうものと捉えるとイメージしやすいかも知れませんし、妄想もふくらませやすいかも知れません。
右側のおっぱいと左側のおっぱいでは、アクションが違うことも考えられるでしょう。
それは、左右のおっぱい、それぞれにシグナルがあり、それが別々のハンドラによって処理されている、ということなのです。

てなところで、妄想を広げて、悶々としといてください。
次回は、実際にハンドラ、関数の定義の仕方について書こうかと思います。
まあ、定型的なものであるので、そこはさらっと流して、関数定義、メソッド定義の書式的な部分の座学ってところでしょうか。

どうして、おっぱいの先っちょをを押すと、声が漏れるのか、その仕組みを実装するためには、どのような手順で行うのか、だと思ってもらってもいいです。(違

プログラムの全体像


前回のサンプルで、一応、シグナルに対してのアクションを記述し、まがりなりにもGUIプログラム、と呼べるものが出来ています。
まあ、具体的なシグナルやそれに対応するメソッドであるハンドル定義は次回やることとして、ここではさらっとプログラムの全体像のおさらいをしとこうかと思います。

そのためには、実行される順番に付いて考えてみるのがいいのかな、と思います。
えっと、まずは食事に誘います。(違

プログラムは、基本的には上から順番に実行されるのですが、サンプルの場合には、importにより、各種ライブラリを使用する宣言をしたあとで、クラス定義がありますので、実際の実行をそこを飛ばされて、if __name__ == "__main__":のところまで来てしまいます。
そこで、MikunchApp()が実行されるわけです。
これは、Pythonでは、MikunchAppクラスが実体を持つことを意味します。
なので、実体を持つときに実行されるコンストラクタが、実行されることになります。
つまり、次に実行されるのは、def __init__(self):なんですね。
そうして、ウィンドウが表示されて、コンストラクタが終了し、gtk.main()が実行されて、ユーザーの入力を待ち受けることになります。
現在のプログラムだと、ボタンを押したり、☓ボタンを押すと、それぞれのハンドラが呼び出されて、gtk.main_quit()が実行され、gtk.main()が終了し、その後には処理するプログラムが何も書かれていないので、プログラムが終了する、ということになります。

実行される順番を整理するとこうなりますかね。
  1. MikunchApp()
  2. MikunchApp.__init__(コンストラクタ)
  3. gtk.main()
  4. ユーザー操作(☓ボタン)
  5. on_TopLevel_destroy(...)
  6. gtk.main_quit()

今回、アプリケーションクラスとして、クラスをひとつ定義しているので、このような構造になってます。
本来であれば、アプリケーションクラスの実体を作る前に、本当に必要なプログラムの引数(コマンドラインのオプション等)の処理を行って、アプリケーションクラスを構築することになります。
そして、最後に、gtk.main()の後で、本当のアプリケーションの後始末を行なってから、プログラム終了という構造になります。

ただ、実際に動作中の処理は、すべてアプリケーションクラスで受け止めて、処理をする、というプログラム構造になってますね。

実行順番と、プログラム構造について、おっさんにも少し解りやすく例示すると、こんな感じでしょうか。
  1. ホテルを選ぶ
  2. ホテルに入る
  3. アプリケーションクラス内:シャワーを浴びる(コンストラクタ)
  4. アプリケーションクラス内:エロビデオを見ながら待つ(シグナル待ち受け)
  5. アプリケーションクラス内:ゴニョゴニョする(シグナル処理)
  6. アプリケーションクラス内:服を着る(シグナル待ち受け終了)
  7. ホテルにお金を払う
  8. ホテルを出る
こんな感じになります。(笑)
で、実際、おっさんにはホテルそのものはどうでもよくて、中でゴニョゴニョが大事なのは理解出来るかと思います。

この中でゴニョゴニョの部分を処理しているのが、アプリケーションクラスのシグナルハンドラってことになりますね。
そう、先に述べている左の乳首と右の乳首では、シグナルも異なれば処理も異なる、という部分に繋がっていたのです。(違

というわけで、プログラムの全体像も、なんとなく理解出来たと思います。
大事なのは、中で何をするか、ってことです。
中出しには責任が伴うのです。(謎

では、次回は予告通り、具体的なシグナル処理とその定義に付いてやっつけて見ましょう。
中でゴニョゴニョ編です。(謎

2010年9月3日金曜日

終了できるようにしてみましょうかね

まあ、アプリケーションなので、終了できないのもアレなので、せめて終了できるようにしましょうか。
手を動かさないと飽きるので。(笑)
というわけで、Gladeを起動してください。
TopLevelのウィンドウをクリックして、右側のプロパティから、シグナルを選択します。
たくさん並んでいるので、ちと紛らわしいかも知れませんが、GtkObjectのクリッカ(+)を押して、開いてみましょう。
destroyというのが見えると思います。
その脇にシグナルの名前、というのがありますので、そこでダブルクリックすると、シグナルの名前を入力できるようになります。
が、まあ、ドロップダウンで選択できるので、選択してしまいましょう。(笑)
こんな感じ。一応赤枠で括ってみました。
ここで選択した名前が重要です。
#ってほど重要でもないので、一番上のを選んでおきましょうか。

きちんと確定したら、保存してGladeは終了してしまいます。

次に設定したシグナルハンドラをプログラムに組み込みます。
NetBeansを起動しましょう。

        self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__)) + "/"+self.gladefile)
        #Create our dictionay and connect it
        dic = {
                "on_TopLevel_destroy" : self.on_TopLevel_destroy }
        self.wTree.connect_signals(dic)

こんな感じになります。
Gladeで作ったファイルを読み込んだ直後くらいに、Pythonのディクショナリ形式の変数を作成し、GTKBuilderのメソッドである、connect_signalに渡して、メソッドとシグナルを結びつけるわけです。
on_TopLevel_destroyは、先程Gladeで設定したシグナルの名前、self.on_TopLevel_destroyは、これからこのクラスの中で宣言するメソッドになります。
この際、一気に、self.on_TopLevel_destroyも書いてしまいましょう。

    def on_TopLevel_destroy(self, widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

単純に、gtk.main_quit()を呼んでいるだけですね。
これは、先に実行していたgtk.main()を終了させるためのメソッドになります。
もしも、終了時になんらかの処理を行う場合には、このメソッドに処理を組み込むことになりますね。
例えば、終了確認とか、設定値の保存とか。
まあ、今はまだ、なんもすることがないので、単純に終了するだけです。
シグナルに結び付けられるメソッドをシグナルハンドラと言ったりします。
#イベントハンドラとかな。
この場合は、☓ボタンを押される、というシグナル(イベント)をハンドリングするので、そういう呼ばれ方をしているわけですね。
この手のハンドラの定型的な書き方が上記の書き方になります。
MikunchAppクラスのメソッドなので、最初の引数(カッコの中身)は、必ずselfになります。
そういう決まりらしいので、これは仕方ないんです。(笑)
次のwidgetというのは、シグナルを起こしたウィジェット、この場合はTopLevelのウィンドウオブジェクトが渡されてきます。
シグナルが発生した場合には、そのシグナルを起こしたオブジェクトに対してのアクションを起こすことが多いので、引数として渡ってくるんですね。
ちなみに、引数ってなんだ、って話になりますが、また、あとで座学します。(笑)
起きるシグナル、引き金となるウィジェットによって、この引数が変わってきます、が、まあ今はそんなに気にしなくてもいいです。
気になるひとは、この辺見ときましょう。

全体像としては、こんな感じになります。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import commands
import os
import os.path
import pygtk
import gtk

__author__="kaoru"
__date__ ="$2010/08/28 13:42:04$"

class MikunchApp:

    def __init__(self):

        #Set the Glade file
        self.gladefile = "mikunchu.ui"
        self.wTree = gtk.Builder()
        self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__)) + "/"+self.gladefile)
        #Create our dictionay and connect it
        dic = {
                "on_TopLevel_destroy" : self.on_TopLevel_destroy }
        self.wTree.connect_signals(dic)

        self.mainWindow = self.wTree.get_object ("TopLevel")
        self.mainWindow.show_all()

    def on_TopLevel_destroy(self, widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

if __name__ == "__main__":
    MikunchApp()
    gtk.main()


これで、ウィンドウの☓ボタンを押したときに、プログラムが終了するようになります。
なんか、プログラムーって感じになってきましたね。(笑)
でも、たったこれだけしか書いてないのです。
#まあ、Glade使ってるから、なんだが。

同様に、ボタンを押した時のシグナルも記述することが出来ます。
単純に、ボタンを押したら終了する、って感じにだけ、しときましょうか?

そんじゃまあ、ふたたびGladeを起動してもらって。
手順はウィンドウの場合と同じです。今度はボタンをクリックして、GtkButtonのClickシグナルに名前を付ける、ってのが異なる点でしょうか。
こんな感じ。やっぱり赤枠を付けときました。
同様に、キャンセルボタンもやってしまいましょう。
それぞれのハンドラも含めた、全体像は以下のようになります。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import commands
import os
import os.path
import pygtk
import gtk

__author__="kaoru"
__date__ ="$2010/08/28 13:42:04$"

class MikunchApp:

    def __init__(self):

        #Set the Glade file
        self.gladefile = "mikunchu.ui"
        self.wTree = gtk.Builder()
        self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__)) + "/"+self.gladefile)
        #Create our dictionay and connect it
        dic = {
                "on_btCansel_clicked" : self.on_btnCancel_clicked,
                "on_btOK_clicked" : self.on_btnOK_clicked,
                "on_TopLevel_destroy" : self.on_TopLevel_destroy }
        self.wTree.connect_signals(dic)

        self.mainWindow = self.wTree.get_object ("TopLevel")
        self.mainWindow.show_all()

    def on_TopLevel_destroy(self, widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def on_btnOK_clicked(self,widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

    def on_btnCancel_clicked(self,widget):
        #ウィンドウを閉じてアプリケーションを終了する
        gtk.main_quit()

if __name__ == "__main__":
    MikunchApp()
    gtk.main()


dicの中身が増えて、それに対応するハンドラのメソッドが増えました。
これで、☓ボタンを押す、OKボタンを押す、キャンセルボタンを押す、のいずれかの操作により、プログラムが終了します。

とまあ、まじめくさって説明してみましたが、こんなの書いてしまえば、15分程度で済むようなところです。(笑)
問題は、ボタンを押されたら、何をするか、ってことなんですが、まあ、そこはもう少し先ってことにして、次はまた座学にしようかなーとか思ってますけど。
そもそも、シグナルってなんだよ、とか。
どういう仕組みで、そのシグナルがプログラムに飛んでくんだよ、みたいなところを軽く説明しつつ、メソッドの引数とかについても触れてみましょうかね。

まあ、何度も書いてますが、身構えるようなもんじゃありません。
知らないから、難しいだけで、解ってしまえば単純なものです。
プログラムなんて、そんな難しいもんじゃないんですよ。
GUIだって、ツールを使えば、こんな簡単に実装出来てしまうんですから。(笑)

2010年9月1日水曜日

変数とか関数とかメソッドとかコンストラクタとか

プログラムが初めて、というひとには耳慣れない言葉が多数出てきました。
ここらで、それらの用語を解説しておこうかと思います。
ちなみに。これらの用語は、たいていの手続き型言語、或いはプログラム言語で、ほぼ共通の用語なので、覚えておいて損はないんじゃないかな、と思います。
まあそれと、厳密な意味とか、そういうのは、もっとまっとうなドキュメントを参照してもらうことにして、ここでは「ああ、そんなものなのだな」というイメージだけつかんでもらえればいいかな、と思ってます。
というわけで、簡単に適当に説明しますので、身構える必要はありません。おっさんは、「なんとなく」解ってればいいと思うんですよ。(笑)

変数とか


変数ってのは何かってぇと。単になんかを入れるポケットと思ってもらえばいいです。
Pythonの場合には、このポケットには、なんでも入れられるので、本当に柔軟な入れ物である、と言えます。
本当になんでも入ります。(笑)
#C言語系だと、入れ物の「型」が決まってたりして、非常に面倒。
イメージとしては「ドラえもんの四次元ポケット」をイメージしてもらえばいいでしょうか。そのくらい何でも入るんです。Pythonの変数は。(笑)
ポケットには、名前が付いていて、Pythonの場合には、名前を付けて「中身」を入れた時点で有効になります。現時点でのサンプルコードだと、以下のような書き方でしょうか。
self.gladefile = "mikunchu.ui"
gladefileという「変数」に”mikunch.ui”という文字列(中身)を入れています。
#代入という言い方を良くします。
#ちなみに、”や'、''' で括られた文字を「文字列」といいます。詳しくはこの辺を参照。(笑)
以降、gladefileの中身が”mikunch.ui”であるよ、と宣言した、ってことですね。変数というのは、その名のごとく、変わる数、なので、後から中身を入れ替えることも可能です。
まだサンプルでは、そういう使い方をしている部分はありませんね。
なお、変数の前にself.とついていますが、これは変数のスコープ、有効範囲を示しているのですが、これは後述します。
#てか、変数のスコープは、実は結構重要な話で。

変数の使い途


変数がポケットであることは「なんとなく」解ったかと思いますし、そもそもポケットなんで、何かを入れとくんだろうな、って想像も付くかと思います。
でも、ビスケット入れて叩いても、割れて二つになったりしないので、そこは注意が必要です。
じゃあ、変数ってどんな時に使うんでしょうね、ってのがイマイチ先の説明では汎用的ではないかな、ってことで、ちょいと補足しようかと思います。

変数には、大きく二つの使い方があって、先に説明した「値を保持しておく」役割が、一番大きいのですが、もうひとつ、一時的な値の保持ってのもあります。
これは後で説明するスコープにも重要な関わりがあるんですが、例えば、計算の結果を、後で使う場合には、その結果を保持しておく必要があります。
普通はメモ書きなんかして、電卓弾いた結果を残しておきますが、これが一時的な値の保持、結果として、旅費の精算なんかするときに、書類に書いて出しますが、その旅費精算の金額が、恒久的な値の保持、スコープの関係は後述するとして、自分のメモ書きは、自分で何度も書き換えて使うので、まあ、何を書いても結果さえ合ってれば構わないし、使い終わったら捨てる、ってのが変数の使い途だったりします。
一方で、旅費精算の結果は、総務に提出しないとならないので、総務が参照できるところに書いておきます。で、こういう総務が参照できるような、みんなが見るような場所に書いとくものは、頻繁に書き換えたりすると、参照する方も面倒なので、なるべく、最終的な結果だけ書いとくようにしとくべきです。

まあ、メモ書きには、オヤジギャグのネタをメモして、ダメだったら捨てる、とかもあると思いますが、プログラム的な変数の使い途は、このようにメモ書きとして使用する場合がほとんどとなります。
メモ書きなんで、追記も出来るし、消して書き直しも出来る。
その上、ドラえもんの4次元ポケットなので、なんでも入るし。音声メモと、手書きメモが混在出来ると思ってもらえばいいでしょうかね。
普通有り得ないから、イメージしにくいと思いますけど。
あ、絵も描けるし、字も書けるくらいなら、イメージしやすいですかね。(笑)
あとで使うものは、基本的に変数に格納、ってことが鉄則ってことでしょうか。
コンピュータってのは、こうして、メモを残しておかないと、何もかも、次の瞬間にはキレイサッパリ忘れてくれるので、後で使うものは、このように明示的にメモを残してあげないとダメなのです。

そういう意味ではおっさんより質が悪いですね、おっさんは、大体三歩ぐらいは覚えているので。

変数のスコープ(有効範囲)


変数にはスコープ(有効範囲)ってのがあります。
まあ、ある意味、賞味期限と思ってもらってれば間違いないんですが。
賞味期限が過ぎると、喰えなくなるように、変数も参照できなくなります。
大きく分けると、グローバル、クラス、ローカルの三種類になるんですが、以下の特徴ですかね。

  • グローバル 賞味期限がなく、どこからでも参照できる。
  • クラス クラス内でのみ使える。selfがついてるのは、これ。
  • ローカル 宣言した関数内でのみ使える

グローバル変数は、あまり使わないし、共通設定とか、参照のみの場合が多いので、あまり気にしなくてもよいですし、乱用はキケンなので、極力使わない、という考えでいてください。
とはいえ、グローバルが有効な場面もありますので、そういう場合には使用を制限するものではありません。
例えば、さっきの例で言えば、総務に旅費精算を通知する場合ですかね。
ここに書いといたから見といてよ、と総務に連絡しとくと、グローバル変数であれば、総務も自分も参照できる、というわけです。
#ま、仮に実際に、本当にそういうプログラム書いたとして、グローバル変数にそんなの格納しちゃダメですけどね。
単に、グローバル変数の多用はプログラムの見通しと、デバッグを大変にする、または、意図しない代入が行われて、意図しない動作をするバグの温床になりやすいので使用は注意って話です。
#総務が確認する前に、他のひとの旅費精算の金額で上書きされちゃうとか、そんな悲惨な結果になります。高いんなら問題ないかも知れませんが、安くなったりすると悲惨ですよね。

変数ではなく、設定値を保持するとか、基本的にアプリケーション実行中は書き換えないような値の保持に使用する分には便利なので、そういう使い方を推奨します。
例えば、アプリケーション起動直後の初期化で、値を設定してしまって、以後は参照のみ、とかいう使い方ですね。

クラススコープの変数は、self.を変数の名前の前につけて宣言し代入します。
前述したサンプルコードがクラススコープになってますね。
でも考えてみれば、前述の変数はクラススコープになってる必要がないので、なんでself付けてるんでしょうね。あとで修正しておきましょうか。(笑)

クラススコープの変数は、そのクラスの中でだけ使えるポケットということになります。
ちと、PCヲタクっぽい例えで行けば、グローバルスコープは、インターネットに公開されたサーバ、と思ってもらえばよいでしょうか。
一方で、クラススコープは、家庭内サーバと考えると解り易いかも知れません。
家庭内に複数のPCがある場合には、どのPCからでも、その家庭内サーバにはアクセスできます。もちろん、インターネットに公開されたサーバにもアクセスできます。
でも、他のご家庭の家庭内サーバにはアクセスできませんよね。
この場合、インターネットがグローバルスコープ、各々のご家庭がクラス、家庭内サーバがクラススコープと考えればわかり易いのではないか、と思うのですが。
そう、ここまでくれば、ローカルは、個々のPCがスコープ、ということが想像が付くと思われます。(笑)

なにも付けずに変数名だけを書いた場合には、基本的にローカルスコープとなって、その関数内でしか参照も代入も出来ない変数となります。
前述の例だと、共有されていない個々のPC内のファイルとでも考えてもらえばイメージが付きやすいでしょうか。
例を書き直すと、ローカルスコープで変数を使用する場合には、以下のようになります。
gladefile = "mikunchu.ui"
self.がなくなっただけ。(笑)
結果として、__init__(self)関数の外からは、gladefileという名前の変数を参照しようとしても、新たに作られた同名のローカル変数として扱われてしまい、代入された"mikunchu.ui"は参照できません。
個々のPCに、hoge.txtというファイルがあったとして、中身が一緒じゃないのと同じことです。
ワタクシの「ひみつ」フォルダの中身と、あなたの「ひみつ」フォルダの中身は、異なりますよね?
#たぶん、異なると思うんだ。
ローカルスコープというのは、そういうことです。

まあ、ぼんやりと「スコープ」に関して、解って貰えたんじゃないかと思います。
実際に、「理解する」のは、痛い目を見てからじゃないと、なかなか実感できないので、実際に自分でオリジナルプログラムを作ったときに、何度も痛い目を見て、身に付けてください。(笑)
なお、Pythonのスコープの詳細はここに書いてありますね。
#まあ、別に見なくてもいいと思いますけど。

スコープの使い分けはどうしたもんでしょうね


基本的に、アプリケーション全体で参照するものをグローバルなスコープとする、ってのは簡単に想像が付くと思いますが。
ローカルスコープとクラススコープの使い分けは、ってえと、少し悩ましいかも知れません。
とはいえ、先程のPCヲタクっぽい例で言えば、ちょっとはわかり易いかも。
ってことで。
例えば、自分のPCだけに秘蔵しているエロ動画は、他のPCから見られたら困るわけです。
#というか、他のひとに見られては困る。
一方で、家族旅行に行った時の動画や写真、そして録画したTV番組などは、家族で共有したい。
つまり、エロ動画はローカルスコープに置き、家族で共有したい動画はクラススコープに置く、と考えれば、少し、スコープの考え方がわかり易くなるのではないでしょうか。

他に見られたくないもの、あるいは使い捨てのものは、ローカルスコープに。
少なくとも、そのクラス内では共有したいものはクラススコープで宣言するとよいでしょう。
まあ、変数のスコープは、実際に使う段にならないと、どのスコープがいいか、なんて解りにくいものですが、そんなイメージで使い分けられれば、まずは十分じゃないでしょうかね。

関数ってなんぞ?


関数。函数なんて書いた時代もあったようで。
基本的に。
関数というのは、「何か入力されるとアクションを起こす箱」です。
いわば、びっくり箱、でしょうか。
開くと中からびよよ〜ん、となんか出てきて、ひとを驚かす。
この場合、「開く」という入力があり「中から何か飛び出す」というアクションがあるわけです。

んで、Python風にびっくり箱を定義するなら、以下のようになるかと思います。
#実際に動くコードではないので、誤解のなきよう。

def びっくり箱(開く):
    中から人形が飛び出す
そのアクションが、計算して値を返すことだったり、画像を表示することだったり、動画を再生することだったりするだけで、基本的な考え方は、上記のものです。
入力に対して、なんらかのアクションを起こすものが関数なのです。

では、メソッドとの違いは何か?
えー、ぶっちゃけ同じです。(笑)
入力を受けて、アクションを起こす、これは関数でもメソッドでも違いはありません。
何が違うか、といえば、関数は独立してアクションを起こすものであり、スコープで言えば、グローバルなスコープに属するものです。

一方で、メソッドはクラスに属するもので、入力を受けてアクションを起こすのは同じですが、「属するクラスに対して」アクションを起こすもの、と言えるかと思います。
クラスに対してアクションを起こす「手段」なので「メソッド」なのですね。
ま、この辺の細かいところは「へー」ですませても問題ありませんので、まあ、そういうものなんだな、くらいで終わらせといていいかと思います。(笑)

なお、やっぱり関数に関しての詳しい説明はここにあります。(笑)

関数の使い途


関数とか、メソッドは、どんな場合に使うのか、どんな使い途があるのか、ってのを、ちと説明しておきましょうか。
プログラムなので、なんらかのアクションがないと成立しないので、この後で出てくるシグナルハンドラなどは、メソッドとして使われます。
ユーザーの入力(操作)に対してのアクションという形ですね。

その他、プログラムでは、基本的には「同じことを2度書かない」という鉄則があります。
#まあ、鉄則ちゅうか、知恵っちゅうか。
そういう場合には、同じ部分を切り出して、関数にするなり、ローカルなメソッドにするなりして、記述するのです。
そのようにすることで、全体の見通しを良くし、かつ、同じ処理は同じ関数を使用することで、間違いを減らす、手間を減らす、バグを減らす、ということが可能になります。
#ま、この辺は、ちと中級というか、テクニック的な話なので、さほど重要じゃないかも知れませんが。

いずれにせよ、GUIプログラミングでは、確実にシグナル(イベント)に対しては、対応するメソッドが必要になります。メソッドの使い途として、最も重要な使い途になります。
また、これらの「処理」というのは、結局のところコンピュータに「このような処理をせよ」と指示を出す部分でもあります。
つまり、関数やメソッドの使い途というのは、「コンピュータに指示を与えるため」に使うのです。
呼び出しタイミングや、分割の仕方などは、様々ですが、使い途としては、このためです。
プログラミング言語で、関数、メソッドというのは、実質的な処理のことなのです。
コンピュータは、バカなので、指示されなければ、何も出来ませんし、また、指示された通りに実行します。
事務のあのコのように、間違えているアナタの書類を、そっと修正してくれたりはしないのです。
日頃から、事務のあのコに出張のお土産を買ってきてたりすれば、気を利かせて、間違いの訂正なんかもやってくれるものですが、コンピュータには、いくら気を使っても、気を利かせてくれるなんてことはありません。実直に、そして素直に指示されたことだけを行います。
その指示を行うのがメソッドであり、関数なのですね。
で、バグを作りこむのも、大抵はコンピュータではなく、アナタの責任です。(笑)

コンストラクタって何?何のために使うもの?


また、コンストラクタという言葉も出てきました。
これは何か、というと、やはり関数、メソッドの一種です。
ちと特殊な用途と言えば特殊な用途ですが、まあ、関数には違いありません。
どこが特殊かというと。
先程説明した「入力」の部分が特殊といえるかも知れません。
#まあ、実際には呼び出されるタイミングなんだけど。
コンストラクタというのは、クラスが実体化されるときに、一度だけ呼び出されます。
そのため、クラスの各種初期化を行うメソッドと考えればよいかと思います。
#一度だけ、というのは、実体化するときに一度、なので複数実体を持つような場合には、その度に呼び出されます。
なんつぅんですかね、こう、そのクラスに対して、「今からお前を使うから、ココロの準備をしておけ!」みたいな感じで呼び出されるってところですかね。
手のひらにひとという字を書いて飲み込むひともいるでしょうし、観客はかぼちゃだ、と思い込むひともいるかも知れませんが、コンストラクタの役割とは、そのようなものです。
そのクラスを、「実際に使うときに必要な初期化を行う」ためのメソッドなのです。

サンプルコードにも__init__(self):というなんか如何にも特殊そうな書き方がされていましたよね?
これはPythonの決まりで、コンストラクタはこう書く、と決まっているのです。
#いわゆる仕様ってやつです。

他にも、特殊なメソッドはいくつかありますが、まあ、まずはコンストラクタだけ知ってれば、そんなに困りません。てか、少なくともワタクシは困ってません。(笑)

というわけで、今まで出てきた専門用語は、大体説明したかな?
まあ詳細はそれなりのドキュメントを参照してもらえばいいと思うんですが、おっさんですので、「なんとなくこんな感じ」さえイメージできれば、まずは十分じゃないのかな、とワタクシは思います。(笑)

gtk.Builderの威力

実は、GTKの中の、GtkBuilderという機能を使って、ウィンドウ表示は実現しています。Gladeは、このGTKBuilderが認識する形式のデータファイルを出力するためのツールなのです。
実体はXML形式のファイルなので、手書きでも構築出来ますが、GUIはGUIで構築した方がイメージに近い形で作業が出来るので、お勧めです。
Gladeも、そんなに使いにくいわけじゃないし。
#使い易いわけでもない。(笑)

GTKBuilderを使う上でのキモは、この3行になります。
        self.wTree = gtk.Builder()
        self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__))+ "/"+self.gladefile)
        self.mainWindow = self.wTree.get_object ("TopLevel")
self.〜という書き方は、このクラス内で使われる変数であることを示しています。
Pythonの尤もキライな部分であり、特徴的な部分でもあります。(笑)
まあ、言語仕様でそうなってるので、ここは諦めましょう。
self.wTree = gtk.Bulder()で、GTKBuilderのインスタンスを作成しています。
これで、self.wTreeにGTKBuilderの実体が保持されるので、以後は、self.wTreeを使って、GTKBuilderを使うことになります。
次の行で、GTKBuilderに、Gladeで作ったuiファイルを設定しています。
add_from_file(ファイル名)という形式で、Builderのメソッド(関数)を呼び出すことで、Gladeで作成されたXMLファイルを読み込みGTKBuilderの中に、GUI用のGTKオブジェクトのツリーを構築してくれるわけです。
これ以前は、GTKのWindowsを作り、コンテナをAddし、そのコンテナにウィジェットをAddして…という面倒な手順を踏む必要がありました。
#簡単に100行くらい費やしてGUIを構築することになります。
まあ、高度な技を使ってウィンドウを構築する場合には、この手順が使えない可能性もありますが、通常GTKの機能の範囲でGUIを構築する場合には、GTKBuilderを使うのが手っ取り早いでしょう。
高度が技の例としてはみくった~♪のGUIがあるでしょうか。
あれは、GTKBuilderだと構築しにくいと思われます。
#ま、ソース見てないから解らんけど、実際の動きから想像するに、いくつかウィジェットを拡張していると思われる。

ちなみに、add_from_file(ファイル名)のファイル名の中身が複雑になっていますが。(笑)
これは、実行ファイル、つまりmikunch.pyの位置から、同じ場所に.uiファイルがあることを想定して、フルパスを生成しているためです。そのために、osライブラリのos.pathライブラリを使用して、絶対パスを取得しています。
また、Gladeファイル名をself.gladefileにわざわざ格納してから使ってますが、実は、この部分というのは定型的な処理なので、他のプログラムからコピペして使うために、わざわざ変数に格納して、ファイル名だけ後から入れ替えるという書き方をしているので、このような面倒な書き方になっているだけです。
場所が明らかなら、或いはカレントフォルダで実行されるのが決まってるのなら、絶対パスを生成する必要はありません。

最後の行、self.mainWindow = self.wTree.get_object(“TopLevel”)というのはGladeで設定したトップレベルウィンドウのオブジェクトを取得しています。
このように、Gladeで設定した名前を使って、GTKオブジェクトを取得しますので、Gladeでの作業の時に解り易い名前を付けときましょう、と言っていたわけです。
いちいちGlade開いて、あのボタンの名前なんだっけ、でもいいんですが面倒じゃないですか。
おっさんは、面倒なことは嫌うものです。(笑)
なので、GTKオブジェクトには、自分で解り易いルールをつけて、きちんと名前をつけましょう。

こうして、self.mainWindowを経由して、トップレベルウィンドウには自由にアクセスできるようになったので、コンストラクタの最後で、 self.mainWindow.show()というGTKのWindowsオブジェクトのメソッドを呼び出すことで、画面にウィンドウが表示された、ということになるわけです。
#showメソッドが、そのまんま、ウィジェットを表示する、というメソッドなわけです。
試しに、self.mainWindow.show()の先頭に#を付けてみてください。そしてF6で実行をすると、実行状態には入りますが、ウィンドウが表示されないのが解るかと思います。
#Pythonでは#で始まる行はコメント行としてプログラム扱いされないので、読み飛ばされます。
その辺、確認したら、元に戻しておきましょう。

ウィンドウを表示するために--クラスを作成する

ウィンドウ(ダイアログ)を表示するためのクラスを作ります。
作らなくても表示は出来るんですが。(笑)
まあ、後々便利なので、クラスを作ってしまいましょうか。

クラスってなんだ?


オブジェクト指向プログラミングなんて言葉がありますが、プログラムの中の色んな機能のなかのひとつのかたまりをオブジェクトといいます。
※まあ、基本ウィンドウオブジェクト、ボタンオブジェクトなんかと考え方は同じなんですが。

機能ごとにオブジェクトを用意して、そのオブジェクトに対して操作を行うのが、オブジェクト指向プログラミングということになるんですが、Pythonの場合、そのオブジェクトを作るのがクラスを作るということになります。
まあ、他の言語でも大体そうなんですが。(笑)
クラスというのは、そのオブジェクトの設計図と考えてもらえば解り易いかも知れません。
クラスは、それだけでは役に立たず、プログラム中で実体として宣言することでオブジェクトとして動作することになります。
※ある意味、キャバ嬢は、偽名を使っているので、指名されて初めて、実態を持った女のコとして、テーブルに付く、と考えると解りやすいかも知れませんが。まあ、その前から存在しているので、リアルな女のコの時、素の状態がクラス、指名されて初めて実体になる、感じでしょうか。もっと解りにくいですね。(ぉ

つまり、ひとつのクラスから、複数の実体を作って、別々のデータを保持することが出来るということです。
※キャバ嬢も、これが出来れば、複数指名が来たときに、あちこちのテーブルを行ったり来たりしなくで済むのでラクじゃないかと思いますが、人間なので、それは難しいですよねぇ。(ぉ

これをインスタンス(実体)化というのですが、今回のサンプルではあまり意識するところでもないので、そんな言葉もあるんだな、程度で押さえてもらえばいいんじゃないかと思います。

クラスを作成しましょうか、でもその前に。


NetBeansを起動して、プロジェクトを開いてください。

まず最初に。
作成したばかりのプロジェクトのソースコードでは、まさに素の状態であり、そのままでは、Pythonの実行ファイルとは認識されない上に、日本語が通らないので、ヘッダを書き換えます。
先頭2行を以下のように変更しましょう。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
これは、シェルスクリプトなどと同様に、Pythonによって実行されるテキストファイルである、ということを先頭行で指示しています。
次の行は、このファイルがutf-8で保存されていることを示しています。
結果として、日本語の文字列もutf-8で扱われるようになる、ってことですね。

次に、使用するライブラリを宣言(import)します。
ライブラリというのは、まさに図書館で、偉大な先人達の作ったクラスオブジェクトが格納されている巨大な図書館だと思えば解り易いかも知れません。
必要な機能があった場合には、まずライブラリを探し、使えるものがあった場合には、使用する宣言を行い、クラスオブジェクトを借りてきて、使用するわけです。
ま あ、本物の図書館と違って、実体を持ってくるわけではなく、クラスを参照するだけですので、返却する必要はありませんし、どんなに多くのプログラムが、そ のライブラリを参照したところで、実体を持って行かれているわけではないので、先に借りられていて、返却待ちとかありませんけど。
まあ、ある意味、これもクラスの恩恵とも言えるんですが。
クラスは設計図であり、実体をクラスを参照して作られます。そのため、クラスのライブラリも参照するだけで、実体はプログラム側で持つので、設計図そのものは図書館に残ったまま、ってことになるわけです。
※風俗の女のコもそうなってれば、指名待ちとかなくて便利なんですけどねぇ…

んで、Pythonで標準的に使用できるライブラリは、こんだけのものがあります。
ざっくり眺めるだけでも、かなりの数があり、たいていのプログラムで使用したいものは、ライブラリとして用意されていることが解るかと思います。

このライブラリが貧弱だと、結局のところ自分でたくさんのプログラムコードを書くことになり、また、先達の偉人たちの偉業と異なり、自分で組んだプログラムほど信用できないものはないので、プログラムのバグ取りが大変ということになるわけです。
ワタクシがPythonを愛用するのは、このライブラリの多さの一言に尽きると思います。
#ま、他にも構文がC言語系に似てるとか、インデントに違和感がないとか、いろいろ理由はないでもないですが。

標準的に用意されているもの以外でも、Pythonには多数のライブラリがあり、別途インストールすることで使用可能です。
Ubuntuの場合には、最初から入っていますが、今回利用するPyGTKというGUIライブラリもその一つですね。
ちなみに、PyGTKのリファレンスはこちらになります。
GTKオブジェクト(というかウィジェット)は網羅されているので、PythonでGTKアプリケーションを書くのに困ることはありません。

というわけで、今回使用するライブラリも宣言しておきます。
import sys
import commands
import os
import os.path
import pygtk
import gtk

どんなライブラリかは、先程紹介したライブラリリファレンスを参照してください。(手抜き)
ちなみに、最後の二つは、PyGTKとGTKライブラリなので、Pythonリファレンスには載っていません。GUIを作る場合のライブラリですね。
Ubuntuだと標準で入っていますが、他の環境だと別途インストールの必要があるかも知れません。

ようやく準備が出来ました。さぁ、クラスを書きましょう


今回、シンプルに行くので、メインのアプリケーションクラスだけで、構築してしまいます。
本来であれば、アプリケーションクラス、データクラスといくつか機能を分割して、今後の拡張や管理をやり易くするのがプログラム設計なんですが、まあ、おっさんですし。(笑)
とりあえず、なんも考えずに下記のコードを書き加えてみましょう。
解説は後ほど。
class MikunchApp:
     def __init__(self):
         #Set the Glade file
         self.gladefile = "mikunchu.ui"
         self.wTree = gtk.Builder()
         self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__))+"/"+self.gladefile)
         self.mainWindow = self.wTree.get_object ("TopLevel")
         self.mainWindow.show_all()
__date__ ="$2010/08/28 13:42:04"の下辺りがいいですかね。
実は、これでクラス定義は終わりです。
classというキーワードがあり、クラスの名前が書いてあります。
その後:がついて、これでクラスが定義されました。
defというのは、Pythonでは関数定義のことで、def __init__(self):というのは、このクラスのコンストラクタを定義している、ということになります。
なんか、関数やらコンストラクタやら、いろいろ用語が出てきましたが、また後ほど解説します。

さらに。
以下を書き加えます。
本来、ウィザードで作られた時にはprint “Hello World!”とか書いてある部分と置き換えてください。
if __name__ == "__main__":
     MikunchApp()
     gtk.main()
これは、ifの行で、このプログラムがどこから開始するのか、を決めています。その次の行で、アプリケーションクラスから、インスタンス(実体)を生成しているのです。
その後、gtk.main()で、一旦プログラムの制御がGTKに渡り、メッセージループという仕組みの中に入ります。

まあ、非常に大雑把な解説なので、「ふ〜ん」程度に聞いといてください。
マトモな解説は後で「じっくり」やります。(笑)

とりあえず、全体としては以下のようになるかと思います。
#!/usr/bin/env python
# -*- coding: utf-8 -*-  
import sys 
import commands 
import os 
import os.path 
import pygtk 
import gtk  
__author__="kaoru" 
__date__ ="$2010/08/28 13:42:04$"  

class MikunchApp:
    def __init__(self):
        #Set the Glade file
        self.gladefile = "mikunchu.ui"
        self.wTree = gtk.Builder()
        self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__)) + "/"+self.gladefile)
        self.mainWindow = self.wTree.get_object ("TopLevel")
        self.mainWindow.show_all()

if __name__ == "__main__":
    MikunchApp()
    gtk.main()

ちなみに、このソースではインデントをスペース4つで表現しています。
Pythonの場合には、プログラムの構文ブロックをインデントで表現しており、このインデントが非常に重要です。
#Pythonが嫌われる理由のひとつでもある。(笑)
インデントが狂ってると実行時エラーになるので、ご注意ください。

Pythonとインデント


ってことで、Pythonでインデントがどれだけ重要なのか、を説明しておきましょう。
#実は上のソースはインデントが狂っていたのを、今修正したのは秘密だ。(笑)
インデントというのは、いわゆる「字下げ」のことです。
Pythonの場合、スペース4つでひとつの字下げ、インデントを示します。
このインデントがなんで重要かというと、このインデントが揃っている部分が、ひとつの処理のカタマリを表現しているからです。
前述の例だと以下がクラスのカタマリ部分になります。
class MikunchApp:
    def __init__(self):
        #Set the Glade file
        self.gladefile = "mikunchu.ui"
        self.wTree = gtk.Builder()
        self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__)) + "/"+self.gladefile)
        self.mainWindow = self.wTree.get_object ("TopLevel")
        self.mainWindow.show_all()
classにはインデントが付いておらず、その内側の部分がインデントが付いています。
さらに分解するとこうなります。
def __init__(self):
    #Set the Glade file
    self.gladefile = "mikunchu.ui"
    self.wTree = gtk.Builder()
    self.wTree.add_from_file(os.path.dirname(os.path.abspath(__file__)) + "/"+self.gladefile)
    self.mainWindow = self.wTree.get_object ("TopLevel")
    self.mainWindow.show_all()
defで定義されたブロックが、字下げされていることになります。
上記のクラスの定義があり、その内側のメソッドの定義が、クラスに属するものであることを示すために、ひとつインデントが付けられていて、さらにdefで定義されたメソッドの内側の処理を表すために、self.gladefile = "mikunchu.ui"には、ふたつインデントが付けられているのです。
より具体的に絵を書いてみましょう。

 
グリーンの領域がクラスの支配するブロック、グレーの領域がメソッド __init__の支配する領域です。
Pythonの場合、このようにブロックをインデントとして区切りをつけて、表現しています。
なので、インデントが狂うと「文法がおかしい」と怒られてしまうのです。

さて実行してみますか


この状態で、F6を押すと。 
 

この様にウィンドウが表示されます。
さあ、感動すべき、自作のウィンドウアプリケーションの初起動です!

とはいえ。
このままじゃ、何も起きません。
試しに、OKボタンやキャンセルボタンを押してみてください。
画面上は押せますが、何も起きません。
当然です。(笑)

その為のプログラムを何も書いていませんので。
ちなみに。
☓ボタンを押すとウィンドウが消えますが、単にウィンドウが非表示になるだけで、アプリケーションは終了しません。(笑)
#笑い事じゃない。

アプリケーションを終了する場合には、NetBeansの実行時ウィンドウ(下の方に出てます)の脇にある、赤い四角いボタンを押して、アプリケーションを終了してください。

何も解らず、言われた通りに書いただけ、とはいえ、この程度のプログラムで、ウィンドウを出すことは出来るわけです。

そんじゃ、次は、どうやってウィンドウが表示されたのか、を少し解説しましょうか。
座学の時間です。(笑)