Hello Blogger
Blog
-
Adobe MAX Japan 2009/AS3でグラフィックス描画
Adobe MAX Japan 2009 に参加。
<!–more–>
{smartads}Commodore64 や AMIGA を Flash の先祖だとして紹介していた講演が興味深い。Adobe のイベントでメガデモを観る事になるとは!ちなみに Ralph Hauwert という Papervision3D という 3D ライブラリの開発者の方である。氏は rePhlex という、いわばビジュアル版シンセサイザのようなものをもっか開発中のようで、こちらも注目である。
ところで、最近の Flash 環境にはすっかりごぶさたです。今回のイベントがちょうど良いきっかけになったので、超久しぶりに Flash の勉強をやることにします。正味数年ぶりだと思うのでリハビリだなこりゃ。環境は CS3/ActionScript3.0 でいきます。描画周りはそれほど変わってないという認識なので、AS3 がメイン。
<h2>ActionScript によるグラフィックス描画</h2>
まずは ActionScript による単純な描画から。完全にスクリプトのみで、描画を行う事が可能。描画機構も AS3 で大きく刷新されたようである。以下のコードを新規ドキュメントの 1フレーム目、フレームアクションに記述する。ちなみに AS3 からクリップアクションは記述できなくなった。
<pre lang=”Actionscript” line=”1″>
var sp:Sprite = new Sprite(); //SpriteはMovieClip のタイムライン無し版(軽量)
var bg:Shape = new Shape(); //ShapeはSpriteのaddChildできない版(最軽量)//表示リストに追加
this.addChild(bg);
this.addChild(sp);drawBackground(bg.graphics); //背景Sprite描画
drawSprite(sp.graphics); //前景Shape描画// Shapeを移動する(センタリング)
sp.x = (stage.stageWidth-sp.width)/2;
sp.y = (stage.stageHeight-sp.height)/2;//背景Sprite描画
function drawBackground(g:Graphics) {
var m:Matrix = new Matrix();
m.createGradientBox(stage.stageWidth, stage.stageHeight, 90*Math.PI/180);
g.beginGradientFill(GradientType.LINEAR, [0x000000, 0x666666],[1.0, 1.0], [0, 255], m);
g.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
}//前景Shape描画
function drawSprite(g:Graphics) {
g.lineStyle(4, 0x000000);
g.beginFill(0xff0000, 0.5);
g.drawEllipse(30, 0, 100, 100);
g.beginFill(0x00ff00, 0.5);
g.drawEllipse(0, 50, 100, 100);
g.beginFill(0x0000ff, 0.5);
g.drawEllipse(60, 50, 100, 100);
g.endFill();
}
</pre>
すると、結果はこうなる:
<script type=”text/javascript” src=”/js/swfobject/src/swfobject.js”></script>
<script type=”text/javascript”>
swfobject.embedSWF(“/wp/wp-content/uploads/2009/02/as30graphicstest1.swf”, “swfEmbedded”, “250”, “250”, “9.0.0”);
</script>
<div id=”swfEmbedded”>
<p>Alternative content</p>
</div>ポイントは軽量な Sprite/Shape の導入、表示に追加するには addChild、描画するには各オブジェクトの graphics プロパティ経由あたり。細かい描画メソッドは、何か見ながら書けばよろしかろう。
ちなみに this.addChild() している箇所、this を trace すると [object MainTimeline] が返り、root の trace も結果の表示は一緒なんだけど、root.addChild() しても同じ結果が得られなかったのは不明。だけどとりあえずは良しとする。オブジェクトの identity が見られると良いのだけど。
-
WSGIことはじめ
WSGI (Web Server Gateway Interface) は Django や Google App Engine を始めたくさんの Python Web フレームワークが準拠している仕様で、これについての秀逸なチュートリアルの翻訳。日本語訳が秀逸かどうかは知らないので、怪しい部分は原文にあたって教えてください。
WSGI ことはじめ
====================
著者(Author): Armin Ronacher
原文(Original Text): http://lucumr.pocoo.org/articles/getting-started-with-wsgi
翻訳(Japanese Translation): hkurosawa
ライセンス(Lisence): http://creativecommons.org/licenses/by-nc-sa/2.0/at/deed.ja
====================やっと論文も終わって、プロジェクトや記事を書くための時間ができた。
ずっと書きたかったものの一つが、特定のフレームワークや実装を
必要としない WSGI チュートリアルなんだ。さあ始めよう。
* WSGI ってなんだ?
基本的に、WSGI はたぶんキミのしってる CGI よりも低層のものだ。
だけど CGI と違うのは、WSGI はスケールするし、マルチスレッドでも
マルチプロセス環境でも動作する、なぜって、これはどう実装されるかは
ぜんぜん気にしてない仕様だからだ。事実、 WSGI は Web アプリケーションと
ウェブサーバの CGI、mod_python、FastCGI または wsgiref と呼ばれる
コアに WSGI を組み込んだ Python 標準ライブラリのスタンドアロンサーバ
みたいな層の間に位置して、だから CGI とは異なる。WSGI は PEP 333 で定義されていて、有名な django や pylons を含む
様々なフレームワークに適用されている。キミがサボって pep 333 を読まないなら、要約はこれだ:
・WSGI アプリケーションは呼び出し可能な Python オブジェクト(関数か、2つの引数を取る __call__ メソッドを持つクラス:1 つが WSGI 環境とレスポンスを開始する関数)。
・アプリケーションは与えられた関数でレスポンスを開始して、渡される項目が書き出しとフラッシュを意図するイテレータ(iterable) を返さないといけない。
・WSGI 環境は CGI 環境みたいなもので、サーバやミドルウェアからいくつかのキーが追加されたものだ。
・アプリケーションをラップすることでミドルウェアを追加できる。たくさん情報があるからとりあえずは無視して、基本的な WSGI アプリケーションを見ていこう:
* 拡張版 Hello World
これは単純だけど単純すぎるってことはない WSGI アプリケーションの例で、
Hello World! の World を url パラメタで指定して出力できる。from cgi import parse_qs, escape def hello_world(environ, start_response): parameters = parse_qs(environ.get('QUERY_STRING', '')) if 'subject' in parameters: subject = escape(parameters['subject'][0]) else: subject = 'World' start_response('200 OK', [('Content-Type', 'text/html')]) return ['''Hello %(subject)s Hello %(subject)s! ''' % {'subject': subject}]見ると分かるけど、 start_response 関数は 2 つの引数を取っている。
ステータス文字列と、レスポンスヘッダを表すタプルのリストだ。
ここでもどこでも使われていないから分からないんだけど、 start_response 関数は
ある物を返す。これは write 関数を返して、ウェブサーバの出力ストリームに
直接書き込むことができるんだ。これはミドルウェア(これは後で触れるね)を
迂回してしまうので、この関数を使うのは非道いアイデアだけど、
デバッグ目的でなら便利に使える。だけど、このアプリケーションはどうやって開始する?
誰もこの関数を呼んでないんだから、ウェブサーバはおろか
Python だってこれをどう扱っていいか分からない。
面倒だからここでは WSGI をサポートしたサーバの設定なんかしたくないけど、
Python 2.5 以降にバンドルされている wsgiref というWSGI の
スタンドアロンサーバを使うことができる。
(Python 2.3 か 2.4 用ならダウンロードもできるよ)ファイルにこれを追加してみて:
if __name__ == '__main__': from wsgiref.simple_server import make_server srv = make_server('localhost', 8080, hello_world) srv.serve_forever()ファイルを実行すると http://localhost:8080/?subject=John では
Hello John! て出てくるはずだ。* パスのディスパッチ
キミは、たぶん CGI とか PHP なら使ったことがあるよね。ならば、
大抵はユーザがアクセスできる複数のパブリックなファイル(.pl/.php) が
あって、それらで何かするということは知ってる。WSGI ではそうじゃない。
全てのパスを処理するただ一つのファイルがあるだけだ。だから、
さっきの例のサーバがまだ動いているなら、同じコンテンツを
http://localhost:8080/foo?subject=John からでも見られる。アクセスされたパスは WSGI 環境の PATH_INFO 変数に、
アプリケーションの実際のパスは SCRIPT_NAME に保存される。
開発サーバの場合は SCRIPT_NAME は空かもしれないけど、
http://example.com/wiki に wiki がマウントされていれば
SCRIPT_NAME 変数は /wiki になるだろう。この情報で、幾つもの
独立したページをステキな URL で提供することができる。この例ではたくさんの正規表現があって、現在のリクエストにマッチさせている:
import re from cgi import escape def index(environ, start_response): """この関数は "/" にマウントされて、hello world のページ へのリンクを表示する。""" start_response('200 OK', [('Content-Type', 'text/html')]) return ['''Hello World Application This is the Hello World application: continue '''] def hello(environ, start_response): """上の例と同様だけど、URL で指定した名前を使う。""" # 存在すれば、url から名前を取得する。 args = environ['myapp.url_args'] if args: subject = escape(args[0]) else: subject = 'World' start_response('200 OK', [('Content-Type', 'text/html')]) return ['''Hello %(subject)s Hello %(subject)s! ''' % {'subject': subject}] def not_found(environ, start_response): """URL が何もマッチしないときに呼ばれる。""" start_response('404 NOT FOUND', [('Content-Type', 'text/plain')]) return ['Not Found'] # URL を関数に割り当てる。 urls = [ (r'^$', index), (r'hello/?$', hello), (r'hello/(.+)$', hello) ] def application(environ, start_response): """ WSGI アプリケーションのメイン。上から順番に現在のリクエストを 関数にディスパッチしてからその正規表現は 'myapp.url_args' として WSGI 環境に保存して、上の関数がこの url プレースホルダに アクセスできるようにする。 何もマッチしなければ、'not_found' 関数が呼ばれる。 """ path = environ.get('PATH_INFO', '').lstrip('/') for regex, callback in urls: match = re.search(regex, path) if match is not None: environ['myapp.url_args'] = match.groups() return callback(environ, start_response) return not_found(environ, start_response)けっこうあるね。だけど URL ディスパッチがどう動くか分かると思う。
基本的には、http://localhost:8080/hello/John にアクセスすると
よりカッコいい URL で上と同じ結果になって、間違った URL を入力すると
404 エラーのページになる。今度はこれをずっと改善して、
environ のリクエストオブジェクトへのカプセル化と、
start_response 呼び出しと返すイテレータのレスポンスオブジェクトへの
置き替えをしてみよう。これは werkzeug や paste みたいな
WSGI ライブラリもしていることだ。環境に何かを追加することで、僕たちはミドルウェアが通常やっていることができる。
だから例外をキャッチして、それをブラウザに書き出すものを書いてみよう。# トレースバックの取得とレンダリングに必要なヘルパー関数のインポート from sys import exc_info from traceback import format_tb class ExceptionMiddleware(object): """使用するミドルウェア""" def __init__(self, app): self.app = app def __call__(self, environ, start_response): """例外をキャッチできるアプリケーションの呼び出し""" appiter = None # 単にアプリケーションを呼び出して、例外をキャッチする以外は # 出力を変更しないで戻す try: appiter = self.app(environ, start_response) for item in appiter: yield item # 例外が発生した場合、例外情報を取得してレンダリングできる # トレースバックの準備をする except: e_type, e_value, tb = exc_info() traceback = ['Traceback (most recent call last):'] traceback += format_tb(tb) traceback.append('%s: %s' % (e_type.__name__, e_value)) # ここまでで、レスポンスを宣言していないかもしれない。 # ステータスコード 500 で応答してみて、もう応答済みならば # 発生する例外は無視する。 try: start_response('500 INTERNAL SERVER ERROR', [ ('Content-Type', 'text/plain')]) except: pass yield 'n'.join(traceback) # wsgi applications might have a close function. If it exists # it *must* be called. # wsgi アプリケーションには close 関数があるかもしれない。 # ある場合は呼び出し *しないといけない* 。 if hasattr(appiter, 'close'): appiter.close()では、このミドルウェアはどうやって使うか?WSGI アプリケーションが前の例みたいに
application な場合、ラップすれば良い:application = ExceptionMiddleware(application)これで、発生する例外は全てキャッチされてブラウザに表示される。
もちろん、まさにこのことプラスより多くの機能をやってくれるライブラリは
たくさんあるからこれをする必要は無いんだけども。* デプロイ
アプリケーションが “完成” したのでこれをどうにかして本番サーバに
インストールしなきゃいけない。もちろん mod_proxy の後ろで wsgiref を
使うこともできるけど、もっと洗練された方法だってあるんだ。多くの人は
WSGI アプリケーションを FastCGI 上で使う方法を好む。flup が
インストールされていれば、キミがやることは myapplication.fcgi を
定義するだけだ:#!/usr/bin/python from flup.server.fcgi import WSGIServer from myapplication import application WSGIServer(application).run()apache の設定はこんな風になる:
ServerName www.example.com Alias /public /path/to/the/static/files ScriptAlias / /path/to/myapplication.fcgi/静的ファイル用の節もあることが分かる。もし開発途中、
WSGI アプリケーションの中で静的ファイルも提供したい場合には
いくつかのミドルウェアが利用可能だ。
(werkzeug、paste と Luke Arno のツールの “static” もこれを提供している)* NIH / DRY
“Not Invented Here(自家製じゃない)”問題を避け、同じことを何べんも繰り返さないこと。
既存のライブラリとユーティリティを利用せよ!でも多すぎて!どれを使おう!オススメはあるよ。** フレームワーク
Ruby on Rails が登場してからというもの、みんなフレームワークの話をしている。
Python にもメジャーなのが 2 つある。1 つはモロモロをすごい
抽象化した Django と呼ばれているもので、もう 1 つは WSGI にずっと近い
Pylong と呼ばれているものだ。Django はアプリケーションを配布しないならば
ヤバいフレームワーク。Web ページを一瞬で作る時。一方で Pylons は
デベロッパの作業を必要だけどデプロイはずっと簡単だ。** ユーティリティライブラリ
多くの場合、全部入りのフレームワークは必要ない。それはキミのアプリケーションには
大きすぎるか、フレームワークで解決するには複雑すぎる。(フレームワークで
なにを解決してもいいんだけど、フレームワークの「助け」を借りないよりもずっと
ややこしくなり得るんだ)。こんな時のためにいくつかのライブラリがある:
・paste — Pylons の舞台裏で使われている。request と response オブジェクトを実装している。
たくさんのミドルウェア。
・werkzeug — pocoo のために書いた最低限の WSGI ライブラリ。unicode 対応の request / response
オブジェクトと先進的な URL マッパ、インタラクティブデバッガを備える。
・Luke Arno’s WSGI helpers — Luke Arno による独立モジュールでの様々な WSGI ヘルパー。** テンプレートエンジン
ボクがよく使い、推薦するテンプレートエンジンのリスト:
・Genshi — 世界最高の XML テンプレートエンジン。でもちょっと遅くて、だから
本当に良いパフォーマンスが必要なら別なものにしないといけない。
・Mako — バカ速いテキストベースのテンプレートエンジン。これは
ERB、Mason、Django テンプレートのミックスだ。
・Jinja — 安全でデザイナー向けでかなり速い、テキストベースのテンプレートエンジン。
モチ個人的にはこれを選ぶ:D* 結論
WSGI スゲー。簡単に個人的なスタックが作れる。
複雑すぎると思う場合は werkzeug や paste を見てみれば、
問題を制限なくずっと簡単にしてくれる。この記事が役立つことを望みます。
-
Pythonのtraceモジュールの補記
Python ドキュメント翻訳プロジェクトの libtrace に取りかかっているのだけど
説明が少ないのでいくつか試してみたメモ書き。間違っていたら指摘してください。
(more…)