From QuickBuf to DumbBuf

こんにちは。サーバの標準エディタを emacs にしたあげく vi を emacs のシンボリックリンクにした方とは、それ依頼お話していない vimmer です (嘘。今もっすご興奮しています。1 年ぶりに vimfiles の中身が更新されましたよ!

複数ファイルを同時に編集するときはバッファーを一覧表示したり、簡単に切り替えたりするバッファーマネージャ系プラグインがかかせませぬ。今までは QuickBuf というプラグインを使っていました。ただ、QuickBuf は表示できる行数を計算し、その行数を超えるファイル数を開くと "No room to display buffer list..." っていうエラーが発生する難点がありました。脳内メモリが少なすぎてファイル名が覚えられないし、バッファー内から補完もしたい。args *.py で 50 ファイルぐらい軽く開いちゃう人なのでそれは困る・・・。そこで出会ったのが DumbBuf です!これすごいですよ!!インストール後、QuickBuf 同様、_vimrc に dumbbuf_hotkey を指定します。例) let g:dumbbuf_hotkey=';;' これで快適なバッファーマネジメントができるようになりました。

ホットキーに割り当てたキーマップをタイプすると、バッファーが一覧表示されます。

ここまでは Quick Buf と一緒です。QuickBuf と異なるところは、バッファ内のファイル数が 10 行を超えると、自動的にローテートしてくれます。もちろん j, k で選択行を上下、<c-f>, <c-b> でページ送り/戻しができます。ページ送りだけで、ファイル選択が断然楽になりましたね!

あと、普通に / でファイル名検索もできますよ!

これさえあれば 100 ファイルぐらい開いても、お望みのファイルに一瞬でたどり着けますね!!!痒いところに手が届きすぎです>< 作者の id:tyru さんありがとうございます!

WSGI on Qt (PyQt or PySide)

こんにちは、Python 会の。。。思いつきませんでした。初 "Python Web フレームワーク アドベントカレンダー2010" に参加させていただくことになりました。よろしくおねがいします。アドベントカレンダー参加者のブログは cielquis.net に綺麗にまとめられていますよ。

ごめんなさい。Web アプリケーションフレームワークの作成なんて 1 日じゃむりぽ><。メインで使ってるフレームワークは、Djangowebapp ぐらい。。。最近 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!" と表示しています。実行すると、以下のようなウィンドウが表示されます。

step1.png

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!" が表示されます。

step2.png

STEP 3: WSGI on PySide でフォーム操作

簡単な GET リクエストが動いたので、簡単なフォーム操作をしてみたいと思います。せっかくなので、WSGI サーバの起動と停止もできるようにして見ました。ソースは長くなっちゃったので bitbucket にのっけました。Qwidget を継承した QWSGIFormWidget に __call__ を追加し、フォームを制御しています。QWSGIFormWidget に テキストインプット (QWSGILineEdit) やセレクトボックス (QWSGIQComboBox) を追加し、REQUEST_METHOD が POST だった時にそれぞれを POST された値で更新 (update(value)) するようになってます。html()update(value) を追加した QWidget のサブクラスであれば、他のフォーム部品も動くはずです。起動すると以下のように、デスクトップアプリケーションとブラウザ間で通信することができます。

step3_1.png

"run" ボタンを押すと Web サーバが起動

step3_2.png

ブラウザからアクセス

step3_3.png

値を変えて "submit"

step3_4.png

一瞬で各値が更新されます

もちょっと時間があれば、html()update(value) を動的に追加したり、バリデーション実装して WAF っぽくしたかったです。あと、update(value) 時に emmit() を実行したりすると、PySide 側のイベントと連動することができます。予断ですが、Qt には QtNetwork.QTCPServer クラスや QTCPSocket クラスが用意されており、QTCPServer を継承した Web サーバをごりごり書くこともできます (サンプルはこっちの方が多い)。WSGI いいですね!

誰得

さて、明日はいよいよクリスマスイブですね!明日のアドベントカレンダーですが、英語が得意なイケメンアメリカ県民日本人の Google API エキスパートな @IanMLewis 先生から、素敵なクリスマスプレゼントがあると思います。