PySide チュートリアル - helloworld から ユニットテストまで

ここ 2 ヶ月ほど PySide でお仕事してました。もともと PyQt で動いてたアプリケーションだったのですが、けっこう仕様変更が大きかったこともあり、どうせなので PySide で作り直してみることにしました。両フレームワークの差異はこのページにまとまっています (id:doloopwhile さんが翻訳されています)。大きくは、

  • モジュール名が PyQt から PySide に
  • PyQt の API は QString や QVariants 等 Qt が提供する型と、unicode 等 Python のデータ型の両方をサポートしていましたが、PySide では Python のデータ型のみをサポート
  • 画像等のリソースファイルをバイナリに変換したりするツール類の名称がそれぞれ変更
    • pyuic4 -> pyside-uic
    • pyrcc4 -> pyside-rcc4
    • pylupdate4 -> pyside-lupdate

PySide への変更はそんなに問題ではありませんが、まだ開発中ということもあり、pyside-rcc4 にバグがあったり、signal-slot が遅いということもあるようです。この辺りは今後に期待です。

ということで自分の復習 & メモと、PySide がちょっとでも普及すればいいなということで、PySide のチュートリアルを作ってみました。helloworld ということで、ボタンをおしたらテキストボックスに "Hello world" と表示するだけのなんの面白味もないウィジェットと、そのユニットテストを作ってみます。ソースは bitbucket に公開しました。

  1. 環境を整える
    インストールするもの
    推奨
    • virtualenv - py2exe でフリージング (実行ファイル形式に変換) する際に余計なライブラリが含まれるのを防ぐため
  2. ウィジェットを表示してみる
    Python インタプリタから以下のように打つと、ダイアログを表示することができます。
    import sys
    from PySide import QtGui
    app = QtGui.QApplication(sys.argv)
    dialog = QtGui.QDialog()
    dialog.exec_()
    
  3. UI を作る
    今回作る helloworld アプリケーションはテキストボックスとボタンのみのウィジェットです。 サンプルコードの setup_ui の部分で UI を設定しています。 Qt デザイナと pyside-uic を使う方法もありますが、私は使ったことがないです。。。UI を作っていくポイントを抜粋すると、
    • 各ウィジェット (QWidget 及びサブクラス) に、レイアウト (QLayout のサブクラス) を設定する
      • QVBoxLayout: 縦に配列していく
      • QHBoxLayout: 横に配列していく
      • QGridLayout: グリッド状に配列していく
      • 他にも QFormLayout や QStackedLayout 等があります
    • 各レイアウトに子供のウィジェットを追加していく
    • ウィジェットをネストさせることで、より複雑な UI も表現できます
    UI と併せて、クラスのインターフェースも実装します。Python の property を利用してもいいですが、Qt にも QtCore.Property が用意されています。異なる点は、
    • 型を明示する
    • settar が呼び出された後に signal を送出することができる (Python の property で書いて、setattr 時に emit しちゃってもいいけど・・・)
    です。可能であれば、QtCore の Property で実装しちゃいましょう。サンプルコードでは text をプロパティ化しています。変更された際に通知する signal は text_changed として登録しています。
  4. ユニットテストを書く
    UI ができたら、ユニットテストを書きます (テストファースト :-)。Qt には QtTest というテスト用のフレームワークが用意されています。PySide では、このページにも記載されているように、Python の unittest モジュールを使ってユニットテストします。Qt はテストも signal-slot ベースです。ウィジェットにある signal が創出され、slot が処理した後に、ウィジェットがどうなっているか。QTest にいくつかの基本的なイベントを発生させるメソッドが用意されています。サンプルコードではボタンをクリックした際のテキストボックスの状態をテストしています。まだ signal-slot を実装していないので、テストは失敗するはず。
  5. イベント (signal-slot) を実装する
    ユニットテストを通過できるように signal-slot を実装していきます。signal-slot の書き方は何種類かあります。サンプルコードの setup_events の箇所でイベントをセットアップしています。ここに connect をたくさん書いてもいいですが、子ウィジェットのイベントを再帰的にコネクトする connectSlotsByName を指定するのが好きです。コード量が少なくなるのでこの書き方を多用しています (イベントハンドラの追加をするたびに connect とか書きたくない)。
    connectSlotsByName を利用すると、slot メソッドを def on_<object name>_<signal name>(<signal parameters>) という風に書けるようになります。 この object name は、各子ウィジェットに対し setObjectname で指定してあげる必要があります。サンプルコードでは、base_layout, lineedit, button すべてにセットしていますが、今回の要件を満たすには button ウィジェットだけセットすればいいです。これでサンプルコードのように on_button_clicked で button の clicked イベントの slot を作成できます。

以上、なんの面白くもないチュートリアルですが、 PySide での開発の流れが少しでも見えたら良いかなーと。

コメント

このブログの人気の投稿

Python から Win32 API 経由で印刷する

Disqus のスケール - Django 編

#PySpa アドベント (23 日目)