dullhikoのだるいブログ

殆ど日記のだるいブログ

ポストモーテムとは…

2005年09月14日 | Python
debugger.pyでraiseで偽ブレイクポイントということで。

よく考えたら、この方法じゃコンティニューできないんだなあ…
どうにかできたら面白いんだけど。

こういうタイプのデバッガの起動を「ポストモーテムデバッガの起動」、と言うらしいが
ポストモーテムって検死解剖って意味なんだってね(((( ;゜Д゜)))ガクガクブルブル
そういうもんかぁー!
やっぱり知らない言葉とかをそのままスルーするのは良くないな。

ActiveBasicを入れてみて思いついた(そしてdebugger.py)

2005年09月12日 | Python
ActiveBasicというフリーのBasicがある。
ふと思い立って、入れてみた。
これがなかなか面白い。結構APIがむき出しなのである。
簡単そうに見えて実は手ごわい割に、破棄だのなんだのと下らないことは考えなくて済む…これはなかなかいいですよ。
さて。それはともかく。
このActiveBasicの統合開発環境では、実はブレイクポイントを設置「しない」のだ。(できないというよりは、しないのだ)
何度も行番号のところで右クリックしたりいろいろ考えてみたのだが、当然ながら設定できるわけがない。そういうものではないのだ。
どうやってやるのか。

本文中に debug と、デバッグ命令文を書き込むのである。
するとそこでデバッガが立ち上がり、ブレイクしたのと同じことになる。
これはちょっと感心した。
というのも、エディタでブレイクポイントを設定すると、削除や追加によって行番号が変わると当然ながらズレてしまう。
俺もvimのsignでやってみたが、結局ズレるので困っていたりするのだ。
エディタ側で削除とかを監視しなければならない。
(まあ、Vimなら出来そうにも思うが、とりあえず放置してある)

そこで、このdebug実行文なら、そんな処理を実装しなくても手軽にいつでもズレないブレイクポイントを設定できるというわけだ。

で。本題に入る。そこで思いついたのがかの名作スクリプトdebugger.pyである。
これを生かしてdebug実行文のようなものを作るにはどうすればいいか。
しばし考えた。10分ほど。

結論。とりあえず

raise "break"

とでもしとけば大抵は目的どおりの機能が果たせる。これ便利。

pygtkに役立つサイト発見 & ストックIDからpixbufを得るには

2005年09月11日 | Python
実に役立つPyGtk FAQサイトを発見した。(英語だが)

http://www.async.com.br/faq/pygtk/
PyGTK FAQ Wizard

迷ったらココだ!
というわけで、このページから得た情報と俺の実験の結果をまとめて書いておこう。

TreeViewにアイコン付きコラムが欲しいと思った。
まあ、実用上は要らないんだが。何事も実験だ。
そこで、gtk.CellRendererPixbufになんとかしてstock-idを渡そうと努力したんだが、どうにもならなかった。ググっても、何もでない。
大抵はPixbufを独自でつくり、そいつをCellRendererPixbufに突っ込むというやりかただ。

そこで、発想を逆に変えてみた。つまり、Pixbufにストックアイコンを読み込む方法はどうすればいいか?ということだ。

Pixbuf周りをいろいろ調べてみたのだが、どうもそういう方法がない…
そこで先ほどのFAQのページを眺めてみたら、ありました。

8.11. How do I get a Pixbuf of a stock icon?(ストックアイコンからPixbufを得るには?)

そうか…gtk.Widgetにそんな便利なメソッドがあるとは…
こいつを使い、

column = gtk.TreeViewColumn("Icons", renderer, pixbuf=0)

などとすれば、簡単にアイコンを付加できた。
そこで、文字列形式のストックID、たとえば'gtk-open'なんかはどうなる?と思い

t_pixbuf = self.BaseWindow.render_icon('gtk-open', gtk.ICON_SIZE_MENU)

とやってみたが、こいつも無事に通る。問題なし。
いや、わかってみれば簡単な話だった。

PyGTKのツリービューに苦戦する その1

2005年09月11日 | Python
すでにシリーズ化決定しているというタイトルで。

GtkのTreeViewって初めて触ったが、面白い設計になってる。
Windows APIのツリービューのシンプルさも捨てがたいが。

まず、ツリービューの中身を保持するのはGtk.TreeModelクラス。実際にはGtk.TreeStoreというその派生クラスが一般的なようだ。
Win32のツリービューではNodeのItem(data)っていう感じの32bit長のデータにキャストして持たせてるような感じでいまいち分離して無い感じだったが、GTKは完全にツリービューが「ビュー」なのだな。

そして、rendererというのがあって、それで描画を簡単かつ柔軟にカスタマイズできるようになっている。これは面白い。

さて、今回苦戦したのはヘッダのカラムである。
カラムもGtk.TreeColumnという独立したクラスになっており、そいつをビューにappendするという方式になっている。
よく知らずにやってた俺はカラムだけを2つほどappendし、そしてモデルのset_valueメソッドを呼び出し、

model.set_value(iter, 2, "hogehoge")

ってな感じでやっていた。ところが。
カラム番号が不正という内容のランタイムエラーが出てしまう。
ちゃんとカラムは二つappendしているし、
len(treeview1.get_columns())
でもカラム数は2と帰ってくる。ちゃんと実行できているわけだ。

さらには、ためしに

model.set_value(iter, 0, "hoge-")

とやってみても、PyObjectじゃねぇよとか言われてしまう。
何がいけないんだ?と散々悩んだわけだが。
コピペしたサンプルコードをちゃんと読んでいなかったのが失敗の元だ。

model = gtk.TreeStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING)

ここのTreeStoreでの引数でカラムの種類を設定していたのだ。だから2は存在しないし、0はPyObjectじゃないとダメってことだったわけだ。
というわけで、最も単純なツリービューのカラム挿入の手順を以下にまとめておこう。

1 #重要。ここでカラムの種類を制限している。 2 model = gtk.TreeStore(gobject.TYPE_STRING) 3 4 treeview1.set_model(self.model) 5 renderer = gtk.CellRendererText() 6 column = gtk.TreeViewColumn("Header1", renderer, text=0) 7 treeview1.append_column(column) 8 # 追加したからといってカラムを自由にできるわけじゃない。 9 # でも追加しないと何も始まらない。 10 11 iter = model.insert_before(None, None) 12 model.set_value(iter, 0, "hoge")


Gtk.TreeStoreを作るときの引数と、TreeColumnを作るときの引数に気を配ればいいということだろう。

Pythonのevalでハマる

2005年09月09日 | Python
eval。
この組み込み関数は、文字列を指定された名前空間上で評価(実行)してくれるというありがたい関数だ。
pdb.pyを弄っていて知った。結構勉強になったな。
って、世間ではPythonに限らずこの手のは常識らしいが。
またおれだけ知らないのかよ…orz

で…こいつの使い道はと言えばたとえば俺の作っているgladeのXMLからのpyGtk用コンバータだと、
GtkWindowInfo
GtkVBoxInfo
などなど、と、XML上で表記されているクラス名末尾にInfoを付けたPythonクラスを作成して、そのクラスが自分のプロパティを解読してpythonコードに変換すると言う仕組みになっている。
ここで、以前は
1 2 def __generateInfo(self,name): 3 # 名前からクラスオブジェクトを生成する 4 5 if name == "GtkWindow": 6 return GtkWindowInfo(self.currentWidget) 7 else: 8 return WidgetInfo(self.currentWidget) 9

のようなコードを書いて、そんでそのつど生成してた。
こんなのだとクラスが増えるごとにメンテナンスの手間がかかるし、間違いも起きる。だいいち、頭悪そうなコードだ。(いや実際悪いけどな)
そこで、
1 2 def __generateInfo(self,name,parent): 3 # 名前からクラスオブジェクトを生成する 4 5 try: 6 return eval("%sInfo(parent)" % name) 7 except: 8 print "error in %s" % name 9 return WidgetInfo(parent)

とすれば、命名規則にしたがっている限りメンテ不用でオブジェクトが生成できるというわけだ。

そこで。ハマった、というのは何かといえば、上のコードで言えば当初、
parentをself.currentWidgetとしていたのだ。つまり
1 2 def __generateInfo(self,name): 3 # 名前からクラスオブジェクトを生成する 4 5 try: 6 return eval("%sInfo(self.current.Widget)" % name) 7 except: 8 print "error in %s" % name 9 return WidgetInfo(parent)

こうなっていたのだ。(追記:よく見るとこの時点で間違いが判るはず…)
gladeのXMLにおいて<child>タグ直前に生成したウィジェットをself.currentWidgetに代入してあるため、そいつが親ウィジェットになるという仕組みになっていた。

まあともかく、self.currentWidgetでは例外が出る…というか、全然ダメ。
もちろん、実際にはtry節のなかで呼んでいるので、ジェネリックなベースウィジェット生成クラスが呼び出されまくるということになっていたわけだが、ともかく、機能していない。
evalにglobals()とかlocals()とか渡してみてもダメ。
selfは鬼門なのだろうか?どういう引数を渡せばいいんだろう?
と考えたがわからなかったので、設計の方を変えて逃げた。

いずれ追求してみたいところではある。

って、再度調べてみたら普通にself.currentWidgetでOK。というわけで、追記ついでにvimのTOhtmlコマンドを駆使してリストを埋め込んでみた。普段こういう配色でやってます。まあそれはともかく。
なぜさっきはダメだったのか…アンドゥしまくって調べてみた。
結果。

self.current.Widgetとtypoしていた

orz
というわけで、再現リストも忠実にcurrent.Widgetにしておいた

pdbをさらに改造した

2005年09月08日 | Python


ちょっと画像でかかった。
まあともかく。
watch系コマンドを装備した改造版pdb.pyを製作した。
ただし、watchと言ってもコマンド実行ごとに表示されると鬱陶しくてかなわないので、実際にはwatchコマンドを指定したときにだけ表示される、aliasの組み込み版みたいな感じだ。
名付けてE_pdb.py (Enhanced pdb.py)
だが。
こういうの(勝手に改造しただけのやつ)ってどう公開していいのかわからないので公開はしない。
ていうか、俺以外に需要ないから意味無いしw
普通ericとか使うわなぁ。Winpdbは俺には全然使い方が理解できんけど…
WindowsでもWxPythonについてくるActiveGridIDEなんてのもある。
ただ、俺の環境では俺が書いたスクリプト開こうとしたらMS932がどうのと言って開いてくれなかった。UTF-8で保存してあるわけだが…まあいいか。

Pythonのreでも置換

2005年09月07日 | Python
reで置換するのもsubメソッドってのがあってですな。
ほとんどvimと同じで。引数の並びが違うけど。
俺の場合、今まで置換するのに

わざわざsearchしてフォーマットして文字列を再構成していた

という恥ずかしい過去を持っていることを告白しておこう。ああ恥

頭の悪い俺にもようやくimportがわかりかけてきたぞと。

2005年09月05日 | Python
実は、いままで俺は
importが何であるか真に理解してはいなかった

単に機械的に importが必要だから書いておこう、とか
なんかimport書いても思ったように動かずダメだから
よくわかんないけどfrom hoge import * にしてやれ、みたいな使い方であった。

そこで備忘録を兼ねて俺が理解したところを書いておこうと思う。つまりここに書かれている記述が正しいかどうかは保証しない。ただ、この時点ではテストコードにおいて正常に動作を確認した。
まず、

1) importは、#includeとかとはちょっと違う
C/C++のincludeってのはヘッダを参照するわけだ。
そんな感覚でimportすると、ちょっと違うことになる。
importってのは、「名前空間の読み込み」でもあるのだ。

たとえば、hoge.pyというPythonのスクリプトファイルがあった、としよう。
この中にはclass hogeが定義されている。
さて、他のスクリプト、ここではfoo.pyとしてみよう。
そのfoo.pyからそのclass hogeを再利用しようと思って

import hoge

とする。ここまでは普通だ。
そして、foo.pyにて

tmp = hoge()

とすると、これはエラーになる。そんなクラスは無い、と。
何故か。
import hogeで、名前空間hogeが自動生成されているからだ。
偶発的なクラスやグローバル変数や関数の名前の衝突の危険性を考えれば、実に納得の行く仕様である。
C++のincludeだったら、自分でnamespaceを書かねばならないところだ。

で、正解はどうなるか、といえば

tmp = hoge.hoge()

これでいいのである。
先日、pythonのクラスのスタティックメンバみたいな変数を代入のオーバーライドでは保護できないと喚いていた俺であるが、これを知れば一目瞭然。
あれは、クラスのスタティックメンバのようなものではなく、クラスの名前空間下のグローバル変数のようなものだったのだ。(って、C++のstaticもよく考えればそんなようなもんか…)

2) じゃあfrom ~ importって何よ?
実はまだよく判っていないのだが、俺の理解では、

名前空間を解除してimportする

と言うことだと思う。
上の例で言えば、hoge.pyをfoo.pyでimport hogeするのではなく

from hoge import *

としたとする。
すると、

tmp = hoge()

このコードが通るのである。
この結果からして、名前空間をfoo側のままでhogeをインポートした…としか俺には思えない。
となれば…fromはあまり使わないほうがいいようにも思えるな。

Pythonで仮想関数みたいなことをするには

2005年09月04日 | Python
普通、仮想関数と言えば、(てか、俺はC++しか知らんのだけど)

基底クラス
baseclass::virtualfunc1()
派生クラス
derivclass::virtualfunc1()

こうなっていたら、baseclass内部でのvirtualfunc1()呼び出しも
全てderivclassのvirtualfunc1()として処理される。
単なる隠蔽では、baseclass内部での呼び出しまでは置き換えられない。
手軽なイベントハンドラ的な処理が可能だ。

pythonでは、これが単にオーバーライドするだけ(つまり継承側で同じ名前・同じ引数のメソッドを宣言するだけで)で、これを行える。まあ、細かい挙動は違うかもしれんけれども。

ただし。__なプライベートメソッドでは、この仮想関数的オーバーライドはできない(プライベートだから当然か…)
普通のpublicなメソッドでなければダメだ。
アンダースコア一個ならprotectedとかならよかったんだけど…まあ、そんなくだらないことに凝ってもしかたないから、別にいいのか。


ちなみに、同じ名前ではあるが違う引数の個数で宣言すると、元のメソッドは呼べなくなるようだ。多重定義はない、ということか。
具体的に書くとたとえば

class base:
  def internalMethod(self):
    print "base::internalMethod called"
  def func(self):
    self.internalMethod()

というベースクラスに対して

class deriv(base):
  def internalMethod(self):
    print "deriv::internalMethod called"

なら、
test = deriv()
test.func()
とすれば、deriv::internalMethod called という出力をエラーなしに得られるが

class deriv(base):
  def internalMethod(self,arg):
    print "deriv::internalMethod called = %s" % arg

などとしてしまうと、
test = deriv()
はともかく
test.func()
の時点で例外を発生して止まってしまうのであった。インタプリタだから当然…なのか?

pdbのリスト表示を日本語対応に改造する

2005年09月03日 | Python
pdbのlistコマンドでのリスト表示が文字化けするので。

1) まず、何はともあれUTF-8形式でpdb.pyを保存しなおす。

2) pdb.pyの2行目あたりに
# -*- coding: UTF-8 -*-
を追加。

3) 635行目 (上で行を追加してるので635行目になってる、元々は634行目だった行)の
print s + 't' + line,
これを
print s + 't' + unicode(line,'utf-8'),
に書き換える

これで、listコマンドでも日本語が化けなくなるぞ!