WSGI on Qt (PyQt or PySide)
こんにちは、Python 会の。。。思いつきませんでした。初 "Python Web フレームワーク アドベントカレンダー2010" に参加させていただくことになりました。よろしくおねがいします。アドベントカレンダー参加者のブログは cielquis.net に綺麗にまとめられていますよ。
ごめんなさい。Web アプリケーションフレームワークの作成なんて 1 日じゃむりぽ><。メインで使ってるフレームワークは、Django か webapp ぐらい。。。最近 dis られてるので、Django 記事書くのもあれだ・・・。
ということで本題です。Python は WSGI (Web Server Gateway Interface) という Web アプリケーションと Web サーバ間の共通インターフェースがあります。そのインターフェースを実装したオブジェクトは、Web アプリケーションとして動作させることができます。ということで、デスクトップアプリケーションを Web アプリケーションにする方法を書きたいと思います。今回は Qt の Python binding な PyQt / PySide で WSGI アプリケーションを作ってみたいと思います。実装は PySide ですが、 PyQt でも動くはずです。
STEP1: PySide で Hello World!
ほんと Web アプリケーションじゃなくて申し訳ないですが、最初に PySide で "Hello World!" を表示します。
import sys from PySide import QtCore, QtNetwork, QtGui class QWSGILabel(QtGui.QLabel): def __init__(self, text, parent=None): super(QWSGILabel, self).__init__(text, parent=parent) class QHttpWidget(QtGui.QWidget): def __init__(self, parent=None): super(QHttpWidget, self).__init__(parent=parent) self.setLayout(QtGui.QVBoxLayout(self)) # Create label instance qwsgilabel = QWSGILabel("Hello World!", parent=self) self.layout().addWidget(qwsgilabel) if __name__ == '__main__': app = QtGui.QApplication(sys.argv) window = QHttpWidget() window.show() sys.exit(app.exec_())
QLabel を単純に継承した QWSGILabel に "Hello World!" と表示しています。実行すると、以下のようなウィンドウが表示されます。
STEP2: WSGI on PySide で Hello World!
STEP1 で作成した QWSGILabel を、さっそく Web アプリケーションにしてみます。
import sys from wsgiref.util import setup_testing_defaults from wsgiref import simple_server from PySide import QtCore, QtNetwork, QtGui ENCODING = 'utf-8' class QWSGILabel(QtGui.QLabel): def __init__(self, text, parent=None): super(QWSGILabel, self).__init__(text, parent=parent) def __call__(self, environ, start_response): """WSGI Web application interface""" setup_testing_defaults(environ) start_response('200 OK', [('Content-type', 'text/html'), ]) return self.text().encode(ENCODING) # Return label text class QHttpThread(QtCore.QThread): u"""Http threads""" def __init__(self, wsgi_server, parent=None): super(QHttpThread, self).__init__(parent=parent) self.wsgi_server = wsgi_server self.__is_run = False def run(self): self.__is_run = True while (self.__is_run): self.wsgi_server.handle_request() def stop(self): self.__is_run = False class QHttpWidget(QtGui.QWidget): def __init__(self, host='localhost', port=8000, parent=None): super(QHttpWidget, self).__init__(parent=parent) self.setLayout(QtGui.QVBoxLayout(self)) # Create label instance qwsgilabel = QWSGILabel("Hello World!", parent=self) self.layout().addWidget(qwsgilabel) # Create and start WSGI Web server wsgi_server = QHttpThread( simple_server.make_server(host, port, qwsgilabel), parent=self) wsgi_server.start() if __name__ == '__main__': app = QtGui.QApplication(sys.argv) window = QHttpWidget() window.show() sys.exit(app.exec_())
QWSGILabel に __call__
関数を追加し、ラベルにセットされているテキストを返すシンプルな Web アプリケーションを作成しています。そして、QThread を継承した QHttpThread に Web サーバを登録し、スレッドを開始しています。実行すると、STEP1 と同じウィンドウが起動します。起動中にブラウザからアクセスすると、"Hello World!" が表示されます。
STEP 3: WSGI on PySide でフォーム操作
簡単な GET リクエストが動いたので、簡単なフォーム操作をしてみたいと思います。せっかくなので、WSGI サーバの起動と停止もできるようにして見ました。ソースは長くなっちゃったので bitbucket にのっけました。Qwidget を継承した QWSGIFormWidget に __call__
を追加し、フォームを制御しています。QWSGIFormWidget に テキストインプット (QWSGILineEdit) やセレクトボックス (QWSGIQComboBox) を追加し、REQUEST_METHOD
が POST だった時にそれぞれを POST された値で更新 (update(value)
) するようになってます。html()
と update(value)
を追加した QWidget のサブクラスであれば、他のフォーム部品も動くはずです。起動すると以下のように、デスクトップアプリケーションとブラウザ間で通信することができます。
"run" ボタンを押すと Web サーバが起動
ブラウザからアクセス
値を変えて "submit"
一瞬で各値が更新されます
もちょっと時間があれば、html()
と update(value)
を動的に追加したり、バリデーション実装して WAF っぽくしたかったです。あと、update(value)
時に emmit()
を実行したりすると、PySide 側のイベントと連動することができます。予断ですが、Qt には QtNetwork.QTCPServer クラスや QTCPSocket クラスが用意されており、QTCPServer を継承した Web サーバをごりごり書くこともできます (サンプルはこっちの方が多い)。WSGI いいですね!
誰得
さて、明日はいよいよクリスマスイブですね!明日のアドベントカレンダーですが、英語が得意なイケメンアメリカ県民日本人の Google API エキスパートな @IanMLewis 先生から、素敵なクリスマスプレゼントがあると思います。
コメント
コメントを投稿