log
スクリプト中からexecfileされるスクリプトの中で 以下のようにして、自分自身を表示するメニューが追加できるかと思ったけども、できなかった。これはJythonがPython2.1相当で、Pythonにレキシカルスコープが導入されたのが2.2からだからかな?
def func():
print menu
menu = menuMediator.addMenu(....handler = func)
もちろんクラスを作れば(グローバル名前空間を参照するんではなく、クラスの名前空間を使えば)下のように書ける。
class Menu:
def __init__(self):
self.menu = menuMediator.addMenu(... handler = self ...)
def __call__(self):
print self.menu
Menu()
__ スクリプト中からexecfileする際にexecfile(filename)としていると、グローバル空間の変数が全部引き継がれる。呼び出された側で書き換えた際に呼び出した側に影響を及ぼすのかどうかは確認していないけども、もしそうならあんまり好ましくない挙動だ。(例えばあるプラグインがSWT = Noneとやれば、そのプラグインではなく次のSWTを使うプラグインでエラーが発生する。デバッグが困難)。execfileは名前空間を渡して実行できるのでexecfile(filename, {})とやる。これでクリーンな環境で実行され、散らかしたグローバル空間もガベージコレクトされる。この場合、もちろんPythonInterpreter#setによってグローバル空間に置かれたオブジェクトは参照できないが、ビルトイン名前空間に置かれたものは参照できる。
__ 本体のGUI自体をプラグインにしてしまう計画に、想定外の所から待ったがかかった。本体GUIはSWTだが、SWTはメインループが裸なので、単純にプラグインにしたのではそこでプラグインのロードがブロックされてしまう。 プラグインにメインループがあるというのはなんともアレゲだ。 メインループのあるプラグインを二つ入れたりしたら何が起こるやら。 そもそもウィンドウを出さない使い方をするとしても、起動して即終了はあり得ないので、何らかの形でループが回る必要があるのか?今はXML-RPCサーバが別スレッドで回っているので、ウィンドウを閉じたときにサーバをシャットダウンするようになっているけど、これがもし別々のプラグインになったとすると、他のプラグインを終了するプラグインということになってしまうのか?XML-RPCサーバはメインループが隠れているので、特定のフィールドをウォッチして自発的に終了したりなんてことはできないし…。
__ SWTのMenuItemでCHECKやRADIOを選んだ場合の「チェックが付いているかどうか」はgetSelection()で取得する。チェックが付いていない状態のメニューをクリックしたら、チェックが付いてからハンドラが呼び出される。 「チェックが付いていればどこぞのフィールドをTrueにする」なんてハンドラは1行。
med.pause = self.menu.selection
そんなわけで「自動整形停止」のメニューを追加するのは以下の10行。
gettext.addMap({
"ja":{
"stop auto-layout": "自動整形停止"
}})
class Menu:
def __init__(self):
self.menu = menuMediator.addMenu(_("stop auto-layout"), self, parentName = "File", type = SWT.CHECK)
def __call__(self):
med.pause = self.menu.selection
Menu()
と思ったけどこれ画面の再描画まで止めるからリサイズすると真っ白に。しまった。 あ、ちゃんとautoLayoutってフィールドも用意されていた。こっちをFalseにすればいいのか。
マウスによる頂点の移動は現在、実際には頂点を移動せずに「この場所に固定せよ」という物理演算の追加で行っているので、自動整形をオフにすると頂点をドラッグしても動かない(もちろんオンにすると行った移動はちゃんと反映されている)。仕様としては、そう作ってあるんだから当然そうなるんだけども、ユーザにとっては直感的ではない罠。
頂点をドラッグ中に、アンカー位置だけではなくて頂点の位置も更新すればいいだけじゃないかと下のように1行付け足したら、なぜか頂点をドラッグしてもアンカーされなくなってしまった。アンカー位置のテーブルを見てみると、アンカー自体は存在しているのに、頂点たちは自由に動いている。
med.graph.anchorTable.put(target, anchorPos);
target.position = anchorPos;
理由は簡単。正解はこう。
med.graph.anchorTable.put(target, anchorPos);
target.position = anchorPos.clone();
位置の更新時に新しいインスタンスを作ると、100頂点の場合で毎秒6000個の位置オブジェクトが生成されることになってイヤだったので、破壊的に更新しているのだった。その結果、アンカー位置が常に「現在の頂点の位置」に更新されてしまい、まったく役に立たないという罠。
まぁこれで自動整形の一時停止機能が付いた。
__ JythonにはPython2.3から入ったはずの「スライスの3番目の引数」がすでに入っている。入れるのが簡単な機能は入ってるんだなぁ。となると、やっぱり「最新のPythonのドキュメントを見て、使いたい機能が使えるかJythonインタプリタで試してみてから使う」ということになるのかな…。
__ CanvasをSWTからAWTに変えたために起こる現象と対処(たぶん今回1回修正するだけだけど一応記録)
- GCへのキャストでClassCastException: sun.java2d.SunGraphics2Dと怒られる。
- Graphicsへのキャストに帰る
- インポートのswtが付いているものを全部削除
- 「インポートの編成」をする。何回か、java.awt.Colorかorg.eclipse....Colorか、というように聞いてくるので全部AWT側を選ぶ。
- UtilCast.o2swtColorか、古いソースならUtilCast.o2colorとなっているところがエラーになるのでUtilCast.o2awtColorに修正。
- gc.setBackgroundなどがエラーになるのでsetColorに変更
- fillPolygonはawt.Polygonを引数に取るので修正
あれ、意外と簡単に修正できた。これで全頂点のAWT対応が完了。
__ GRINEditのTracを作ってもらったので早速使ってみているのだけども、 チケットシステムがまだ慣れない。TODOからデータを移しているのだけど、 「マウス操作をプラグインとして追加できるようにする」っていうチケットを登録した後で、「頂点を投げる機能を~~という方法で実装する」というチケットを登録しそうになって「いや、これはプラグインとして実装した方がいいから、前に登録したチケットの具体化だな」と思ったとする。これを前のチケットに追記するとチケット一覧にせっかく具体化した内容が表示されない。あっ、そうか、ここで#記法を使ってチケットへのリンクを貼るのか。なるほど。
投げる機能自体はtrivialなのだけど、マウス操作のプラグイン化はmajor。
__ Eclipseで新しいプロジェクトを作る。→フォルダの中身を全削除→そこへSubversionでチェックアウト→EclipseでF5→ビルドできた!
__ SWTのMouseMotionListenerにはmouseDoubleClickがあるが、AWTにはない。ダブルクリックを取得するにはmouseClickedでe.getClickCount() == 2。
__ クラスを作る→テストする→動いた!→エクスポートでJarにする→プラグインフォルダに入れる→プラグインのinit.pyでmed.loadClass→mouseMediatorにadd→エラーは出ないがメニューにも出ない(メニューに追加し忘れた!)→追加した→動いた→念のためワークスペースにある側をリネームしてみる→動かない→PluginClassLoaderがプラグインフォルダをフルパスで指定している!誰だこんなことしたの!(自分)→相対パスになおしたら動いた!
__ マウス操作を追加したときに、それをメニューに追加する作業はとても定型的なのでもっとシンプルに書けるようにすべき。どちらかというとマウス操作を追加しただけでメニューにも追加されればいいと思う。でも今はマウス操作の初期化がメニューの初期化より先だからできない。この順番を変えるリファクタリングは酔ってないときにする。
__ 次は何をしよう~。
XML-RPCハンドラをCommonGateway経由で追加できるようにした。 本体のXML-RPCハンドラも同じ方法で追加。
__ JSON形式での保存について。実はJSONほぼPythonなので、JSONObjectだのJSONArrayだの作らなくてもPyDictとPyListでPythonっぽいものが作れてしまう。違いはtrue/Trueの違いと、コメントが#か//かの違い。前者は何の問題もないのだけど後者は面倒。
__ ひとしきり作業した後でTracのタイムラインを見ると面白い。あとマイルストーンが何のためにあるのか今やっと理解した。大きなタスクをブレイクダウンしたら、その大きなタスクをマイルストーンにすれば、ロードマップの所でどの程度終わったかが確認できるわけですね。
__ 頂点がリッチになるより、コンパウンドな頂点がいいのかも…