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 先生から、素敵なクリスマスプレゼントがあると思います。
コメント
コメントを投稿