Contenu connexe Similaire à PyQtではじめるGUIプログラミング (20) PyQtではじめるGUIプログラミング1. PyQtではじめる
GUIプログラミング
2011-08-27
Python Conference JP 2011
Ransui Iso
Strategic Technology Group / X-Listing Co, Ltd.
Copyright (c) 2011 Ransui Iso, All rights reserved.
2. おまえ誰よ?
Ransui Iso (磯 蘭水)
Work at X-Listing Co, Ltd.
http://www.xlisting.co.jp/
Pythonは1998年から使っています。E-Commerceエンジンやサーチエンジンの開
発、Zopeを用いたWebサイト開発、その他色々を経て、今はネット広告配信シス
テムについての研究開発をしています。最近はCommon Lispでシステム開発をし
ていますが、Pythonもヘビーに使っています。
http://www.facebook.com/ransui
@ransui
Copyright (c) 2011 Ransui Iso, All rights reserved.
3. 今日お話する内容
● QtとPyQtについて
● 開発環境を整える
● HelloWorld
● QApplication と Event Loop
● SIGNALとSLOT
● GUIアプリケーション開発の基礎
● 便利ツール : assistant, designer
● Graphics, Multimedia, Web関連機能
● さらに先へ行くために
Copyright (c) 2011 Ransui Iso, All rights reserved.
4. 対象とするレベル
● Pythonプログラミングの経験
– 午前中のセッション「Pythonチュートリアル」
– 書籍「はじめてのPython」
– Python公式ドキュメント「Pythonチュートリアル」
以上のいずれかをクリアしている方
● GUIプログラミング経験
– 基本的にGUIプログラミング経験が無い方を想定
– tkinter, wxPython, PyGtk等の経験者の方はPyQt独特の
方法を学ぶ機会になるかと思いますが、多分ドキュメント読
めば独学できる内容
Copyright (c) 2011 Ransui Iso, All rights reserved.
5. その他注意点など
● ハンズオンではありません
– セッション時間内での演習時間等は予定していません
– このスライドは自習時の参考にもなるように作っています
● 当日はご参加ありがとうございました
– サンプルコードは以下にあります。
– http://alpa.homeip.net/files/PyConJP2011SampleCodes.zip
● 質問はfacebook上で
– twitterはあまり使っていないので、facebookのメッセージと
かでコンタクトしてくれると反応するかもです。
Copyright (c) 2011 Ransui Iso, All rights reserved.
7. Qtについて
● クロスプラットフォームのC++総合ライブラリ
– 最初期はGUIツールキットとして作られていたが、すぐにC++の総
合ライブラリに成長した
– Windows, MacOSX, X11を主要として複数のプラットフォームに
対応している
● ライセンス形態
– LGPL 2.1 + Commercial のデュアルライセンス
● 読み方
– 「きゅーと」と読むらしい
– 「きゅーてぃー」でもおおよそ通じると思われる
Copyright (c) 2011 Ransui Iso, All rights reserved.
8. Qtを使っているもの
● KDE
● Google Earth
● Picasa Client
● VLC Media Player
● Guitar Pro
● Mathmatica
● Skype
Copyright (c) 2011 Ransui Iso, All rights reserved.
9. Qtのモジュール群
● 総合ライブラリと呼ぶに相応しい充実度
● QtCore ● QtScriptTools
● QtGui ● QtSql
● QtMultimedia ● QtSvg
● QtNetwork ● QtWebKit
● QtOpenGL ● QtXml
● QtOpenVG ● QtXmlPatterns
● QtScript ● Phonon
ものすごく多機能で、提供されるコンポーネント量も膨大ですが、モジュール分
割の粒度と命名規則がスマートなので、使いたい機能を比較的楽に探し出せる
Copyright (c) 2011 Ransui Iso, All rights reserved.
11. 言語バインディング
● まぁ色々と大変なのでもっと気軽に使いたい
– いわゆるLL系言語を含んだ沢山の言語バインディングが
あります
Ada C# D Haskell Harbor
Java CommonLisp Lua Ocaml
Pascal Perl PHP Python R
Ruby Scheme Tcl
これで全部ではないはずです。
Copyright (c) 2011 Ransui Iso, All rights reserved.
12. PyQt
● QtライブラリのPythonバインディング
– Riverbank Computingという会社が作っている
– 最新のQtバージョンへの対応はそれなりに速い
– 主要なモジュールはほぼ全てサポートしている
– SIPというC++とPythonを繋ぐ独自の仕組みで構築
● ライセンス形態
– GPLv2 or GPLv3 (LGPLではないので注意)
– Commercial License 有り
● PySideという別のバインディングもあります
● ライセンスはLGPLv2.1で使いやすいかも!
Copyright (c) 2011 Ransui Iso, All rights reserved.
14. インストールにあたって
● Windowsへの導入について説明します
– LinuxやMacOSを使っている人は自力で出来るでしょ?
● ほとんどのディストリのパッケージに登録されているはずなの
でコマンド一発でインストールのはず
● バージョンについて
– Python Version 3.2.x for Windows
● PyQtを使う場合はPython3系が圧倒的に便利
– PyQt Version 4.8.5 for Windows
● PyQtをインストールすると自動的にQtも入る
Copyright (c) 2011 Ransui Iso, All rights reserved.
15. Pythonのインストール
● MSIパッケージでインストール
– http://www.python.org/download
– 多分これが一番楽
– x86とx86-64は別パッケージなので注意
– インストール位置とかはデフォルトのままが混乱が少な
くてよい
Copyright (c) 2011 Ransui Iso, All rights reserved.
16. 環境の確認
Ctrl-Z + Enter で終了できます
Copyright (c) 2011 Ransui Iso, All rights reserved.
17. PyQtのインストール
● Riverbankからインストーラを入手
– http://www.riverbankcomputing.co.uk/software/py
qt
– Python本体をデフォルトでインストールしていれば何
も変更ぜずにインストールしてしまえば良い。
Copyright (c) 2011 Ransui Iso, All rights reserved.
19. エディタ・IDE
● PyScripter : お勧めします
– http://code.google.com/p/pyscripter
– これもWindowsのインストーラがDLできる
– デフォルトのオプションでインストールする
– Pythonプログラミングするのに十分な機能を持ったIDE
Copyright (c) 2011 Ransui Iso, All rights reserved.
20. Off-lineドキュメント
● 別でDLする必要有り
– http://qt.nokia.com/downloads
– QtSDK Ver 1.1.2のWindows版Online installerをDL
– Customインストールを選択
– 以下のようにドキュメントだけを選択してインストール
必要なコンポーネントのみが
DLされインストールされる
Copyright (c) 2011 Ransui Iso, All rights reserved.
21. Assistantの設定
● ドキュメントブラウザにデータを設定する
–
Assistantを起動する
メニューバーから
1:【編集(E)】→【設定】でダイアログを開く
2:【ドキュメント】タブを選択する
3:【追加】ボタンを押す
4: C:QtSDKDocumentationの中にある
.qchファイルを全部選択して【開く】
ボタンを押す
Copyright (c) 2011 Ransui Iso, All rights reserved.
22. やっと環境ができました
● いよいよPyQtプログラミングの解説です
– 今まで説明してきたインストール方法で環境が構築され
ていることが前提です
– カスタムインストールした環境ではそのまま動かない例
があるかもしれませんが、自助努力で頑張ってください
– セッション中にデモすることがありますが、環境はKDE
です。ちょっと見た目とか違いますが、全く同じコード
がWindows環境でも動作しますので安心してください。
Copyright (c) 2011 Ransui Iso, All rights reserved.
24. とりあえず始めはこれ
● PyScripterに以下のソースを書きこむ
import sys
import sys
import PyQt4.QtCore as QtCore
import PyQt4.QtCore as QtCore
import PyQt4.QtGui as QtGui
import PyQt4.QtGui as QtGui
def main():
def main():
app = QtGui.QApplication(sys.argv)
app = QtGui.QApplication(sys.argv)
main_window = QtGui.QMainWindow()
main_window = QtGui.QMainWindow()
hello_button = QtGui.QPushButton("波浪ワールド")
hello_button = QtGui.QPushButton("波浪ワールド")
main_window.setCentralWidget(hello_button)
main_window.setCentralWidget(hello_button)
main_window.show()
main_window.show()
app.exec_()
app.exec_()
if __name__ == '__main__':
if __name__ == '__main__':
main()
main()
ファイル名とかはまだ付けなくても大丈夫
Copyright (c) 2011 Ransui Iso, All rights reserved.
25. 実行してみる
● PyScripterから実行してみる
– スクリプト実行ボタンを押す
– メニューの[実行(R)]→[実行(R)]を選択する
プログラムに間違いが無ければ、
左のように巨大なボタンが1個だ
け配置されたウィンドウが表示さ
れる。
ボタンはクリックできるけれど
も、押してもなにも起こらない
Copyright (c) 2011 Ransui Iso, All rights reserved.
26. 出てきたQtのコンポーネント
● QApplication
– QtのGUIプログラム全体をコントロールする
– コマンドライン引数を要求するので sys.argv を渡す
● QMainWindow
– メインウィンドウを表現する
● QPushButton
– 基本的なボタンコンポーネント
Copyright (c) 2011 Ransui Iso, All rights reserved.
27. setCentralWidget()
● QMainWindowのレイアウトに関係している
– MainWindowには、メニューバーやツールバーを配置す
ることが多いが、QMainWindowは予めこれらをどこに
配置するかが考慮されている
HelloWorldの例では
CentralWidgetしか設定していな
いので、他の領域は潰されて見え
なくなっている
Copyright (c) 2011 Ransui Iso, All rights reserved.
28. exec_() メソッド
● イベントループを開始するメソッド
● GUIシステムはイベントドリブン方
Application 式で動いている
● Qtは下位のレイヤーが発するイベン
トを拾ってアプリケーションに通知
Qt する必要がある
Eventの取得 処理呼び出し
X11 / Windows
結果の通知
OS / Device Driver
● この繰り返しをイベントループと呼
ぶ。イベントループが止まるとGUI
Keyboard Mouse Monitor Audio 全体も止まってしまう
Copyright (c) 2011 Ransui Iso, All rights reserved.
30. イベントに応答するボタン
● 前のソースに追加する
import sys
import sys
import PyQt4.QtCore as QtCore
import PyQt4.QtCore as QtCore
import PyQt4.QtGui as QtGui
import PyQt4.QtGui as QtGui
def on_click():
def on_click():
print("Hello World")
print("Hello World")
def main():
def main():
app = QtGui.QApplication(sys.argv)
app = QtGui.QApplication(sys.argv)
main_window = QtGui.QMainWindow()
main_window = QtGui.QMainWindow()
hello_button = QtGui.QPushButton("波浪ワールド")
hello_button = QtGui.QPushButton("波浪ワールド")
hello_button.clicked.connect(on_click)
hello_button.clicked.connect(on_click)
main_window.setCentralWidget(hello_button)
main_window.setCentralWidget(hello_button)
main_window.show()
main_window.show()
app.exec_()
app.exec_()
if __name__ == '__main__':
if __name__ == '__main__':
main()
main()
Copyright (c) 2011 Ransui Iso, All rights reserved.
31. SIGNAL
● イベント処理のインタフェース
– connect()を使って処理ルーチンと接続する
QMouseEvent QPushButton
button on_click()
clicked
position
globalPosition pressed
released
マウスのボタン操作に関する 内部状態に応じて適切な 処理ハンドラは普通の
イベントがEvent Loopから SIGNALがアクティブになる Python関数で良い
送られてくる
– アプリケーションはイベントの詳細を知らずとも
「何が起こったか」だけを見れば良い
Copyright (c) 2011 Ransui Iso, All rights reserved.
32. 状態を伴ったSIGNAL
● QCheckBoxで見てみる
def print_state(state):
def print_state(state):
if state == 0:
if state == 0:
print("Unchecked")
print("Unchecked")
else:
else:
print("Checked")
print("Checked")
def main():
def main():
app = QtGui.QApplication(sys.argv)
app = QtGui.QApplication(sys.argv)
main_window = QtGui.QMainWindow()
main_window = QtGui.QMainWindow()
check_box = QtGui.QCheckBox("Check box")
check_box = QtGui.QCheckBox("Check box")
check_box.stateChanged.connect(print_state)
check_box.stateChanged.connect(print_state)
main_window.setCentralWidget(check_box)
main_window.setCentralWidget(check_box)
main_window.show()
main_window.show()
app.exec_()
app.exec_()
ソースコードは抜粋です
Copyright (c) 2011 Ransui Iso, All rights reserved.
33. SIGNAL付随の状態を受け取る
● 引数部分を指定された型で取得できる
QCheckBox
stateChanged(int) print_state(state)
clicked(void)
pressed(void)
released(void)
– QCheckBox の stateChanged の場合は、チェックさ
れている場合は1、されていない場合は0がint型で引数
としてconnectしているハンドラに渡される
Copyright (c) 2011 Ransui Iso, All rights reserved.
34. コンポーネント同士を直結する
● SIGNALとSLOTを接続する
– SLOTはSIGNALの受け口関数
QPushButton QCheckBox
clicked stateChanged print_state()
pressed pressed
released toggle released
button.clicked.connect(check_box.toggle)
button.clicked.connect(check_box.toggle)
check_box.stateChanged.connect(print_state)
check_box.stateChanged.connect(print_state)
SLOTも普通の関数のようにconnectのターゲットに指定できる。
ただしSIGNALとSLOTを直結する場合は受け渡す値のC++型が一致している必要が
あるので注意する。型の調べ方は後で説明する
Copyright (c) 2011 Ransui Iso, All rights reserved.
35. わざわざconnectする理由
● 疎結合にしておくことによるご利益
– 相互作用のトリガが明示されるので理解しやすい
– コンポーネント間の相互作用がロジックに埋もれない
– インタフェースが同じなら簡単に差し替え可能
– 必要なSIGNALのみに処理をconnectすれば良い
– 1つのSIGNALに複数のハンドラを設定できる
– SIGNALとハンドラの接続は実行時に設定&解除可能
● Qtは徹底して疎結合モデル
– 大規模なシステムを設計する時の極めて強力な1つの手
法として大いに参考にできる
Copyright (c) 2011 Ransui Iso, All rights reserved.
36. ラーメンタイマー
A tiny example for design and develop GUI application
Copyright (c) 2011 Ransui Iso, All rights reserved.
37. ラーメンタイマー伝説
● 2chのスレッド
– 「Linux使ってこりゃ普及するわけないと思ったとき」
873:login:Penguin:2009/01/24(土) 23:53:21 ID:AI280zEEdownup
873:login:Penguin:2009/01/24(土) 23:53:21 ID:AI280zEEdownup
だいたいLinuxなんてラーメンタイマーさえ作れないだろ。
だいたいLinuxなんてラーメンタイマーさえ作れないだろ。
開発環境が整っているとかソフトが豊富で安定してて楽チンとか
開発環境が整っているとかソフトが豊富で安定してて楽チンとか
宣伝する割にはさ。
宣伝する割にはさ。
それ考えるとWindowsってよく出来てるんじゃない?
それ考えるとWindowsってよく出来てるんじゃない?
素人でもラーメンタイマーくらいすぐ作れるし。
素人でもラーメンタイマーくらいすぐ作れるし。
● Uncyclopediaの「Linux」の記事
主な利用者
主な利用者
エセSE
エセSE
低レベルなサーバー管理者
低レベルなサーバー管理者
ただのオタク
ただのオタク
ニコニコ動画で3Dデスクトップの動画を見たリア厨
ニコニコ動画で3Dデスクトップの動画を見たリア厨
偉そうに語るくせに、ラーメンタイマー1つまともに作ることもできないエセ開発者
偉そうに語るくせに、ラーメンタイマー1つまともに作ることもできないエセ開発者
Copyright (c) 2011 Ransui Iso, All rights reserved.
38. 練習題材としてはなかなか良い
● 実装例:RamenTimer.py
– 3分間カウントダウンするだけの単純なもの
2chのスレッドではQtは反則ということになっていたようだが
PyQtセッションなので躊躇なく使うことにする
Copyright (c) 2011 Ransui Iso, All rights reserved.
39. まずは構想&デザイン
● いきなりコードを書き始めると100%死にます
– GUIコンポーネント(Widget)の配置
● スケッチを書いてみる
● 機能に注目してUIの領域を分割してみる
– プログラムを構成するコンポーネントの階層関係
● UI上の領域と機能の両方を考慮しながらクラスの構成を考える
– コンポーネント間の相互作用
● SIGNAL, SLOT, クラスメソッドがどのように連携するかを整理する
– 「設計」ではなく「デザイン」という観点が大切
● なかなか言葉で表現しにくい
● デザイン中は対象がどんどん変化するので「形式」に拘るのは非効率
Copyright (c) 2011 Ransui Iso, All rights reserved.
40. コンセプトを決める
● コンセプトは成果物の基本方針
– GUIは「見た目」という強烈な刺激があるので「思いつき」が発生しやす
く、開発中にあらぬ方向へ進み出す危険性が高い
– 自分が「なにをしたいのか」「何を作っているのか」を確認するために適
時振り返るためにもコンセプトは重要
– 「ココが重要」「これは譲れない」という事項を列挙しただけの簡単なも
のでも十分に機能する
● ラーメンタイマーの場合
– タイマーなので数字は「見やすく」「でっかく」表示したい
– 1/100秒単位で表示するとカッコイイかも
– カウントダウン中に一時停止やカウンタ値リセットがあると便利
– 見ただけで操作が分かるようにしたい
– とりあえずは3分固定でOK
– ... 等々 ...
Copyright (c) 2011 Ransui Iso, All rights reserved.
41. GUIレイアウト
● コンセプトに基づいてスケッチしてみる
– 実際に行うときは「紙と鉛筆」を推奨
● PC上でパワポとか使って検討するのは本当に時間の無駄
● 「イケテル!」と確信できた時点で清書すればいい
– 機能単位に「まとまっている」所を探してみる
カウントダウンを実行して表
示する部分
操作を行う部分
Copyright (c) 2011 Ransui Iso, All rights reserved.
42. コンポーネントの選定と配置
● コンポーネントを積み重ねて構成する
– この段階で利用するコンポーネントの当たりをつける
QWidget:QVBoxLayout
QLCDNumberとQTimerを
配置する QLCDNumber
QLCDNumber
QPushButton QPushButton
QPushButton QPushButton QWidget:QGridLayout
QPushButtonを格子状に
QPushButton QPushButton 配置する
QPushButton QPushButton
QWidget:QVBoxLayout
QMainWindowのCentralWidgetに指定する
上に乗る2つのレイヤーを縦方向に整列して配置する
Copyright (c) 2011 Ransui Iso, All rights reserved.
43. コンポーネントの階層構造
● コンポーネントの親子関係等を図で整理する
QMainWindow QWidget attribute QTimer
main_window CountDownWidget timer
CentralWidget QVBoxLayout
QLCDNumber
QWidget instance addWidget lcd_number
panel
countdown_widget
QVBoxLayout
addWidget QPushButton
addWidget start_button
QWidget QPushButton
ButtonBoxWidget stop_button
QGridLayout
QPushButton
instance reset_button
button_box_widget QPushButton
quit_button
Copyright (c) 2011 Ransui Iso, All rights reserved.
44. コンポーネントの相互作用
● これも図を書いて整理しておくと良い
start_button timer
start_countdown start
clicked timeout
stop
stop_button stop_countdown
clicked do_countdown
reset_button reset_count
lcd_number
clicked
display
update_display
quit_button update
clicked
CountDownWidget
app
ButtonBoxWidget quit
Copyright (c) 2011 Ransui Iso, All rights reserved.
45. 新出コンポーネントのまとめ
● どれも基本的なものでよく使います
– QWidget
● UIを構成する全てのコンポーネントのスーパークラス
● 今回はパーツを束ねるコンテナ(トレイ)として使用
– QLCDNumber
● 液晶風の数値表示ウィジェット
– QTimer
● 一定時間毎に指定された処理を呼び出す
– QGridLayout, QVBoxLayout
● GUIパーツを整列させるためのレイアウトマネージャ
Copyright (c) 2011 Ransui Iso, All rights reserved.
46. ButtonBoxWidgetの実装
● まずは方針を確認する
– こんな感じに作る予定だったはず
addWidget QPushButton
start_button
QWidget QPushButton
ButtonBoxWidget stop_button
QGridLayout
QPushButton
reset_button
QPushButton
quit_button
Copyright (c) 2011 Ransui Iso, All rights reserved.
47. ButtonBoxWidgetのコード
● 素直にコードに落とす
class ButtonBoxWidget(QtGui.QWidget):
class ButtonBoxWidget(QtGui.QWidget):
def __init__(self, parent=None):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent=parent)
QtGui.QWidget.__init__(self, parent=parent)
self.setup_ui()
self.setup_ui()
def setup_ui(self):
def setup_ui(self):
self.start_button = QtGui.QPushButton("STRAT", parent=self)
self.start_button = QtGui.QPushButton("STRAT", parent=self)
self.stop_button = QtGui.QPushButton("STOP", parent=self)
self.stop_button = QtGui.QPushButton("STOP", parent=self)
self.reset_button = QtGui.QPushButton("RESET", parent=self)
self.reset_button = QtGui.QPushButton("RESET", parent=self)
self.quit_button = QtGui.QPushButton("QUIT", parent=self)
self.quit_button = QtGui.QPushButton("QUIT", parent=self)
layout = QtGui.QGridLayout()
layout = QtGui.QGridLayout()
layout.addWidget(self.start_button, 0, 0)
layout.addWidget(self.start_button, 0, 0)
layout.addWidget(self.stop_button, 0, 1)
layout.addWidget(self.stop_button, 0, 1)
layout.addWidget(self.reset_button, 1, 0)
layout.addWidget(self.reset_button, 1, 0)
layout.addWidget(self.quit_button, 1, 1)
layout.addWidget(self.quit_button, 1, 1)
self.setLayout(layout)
self.setLayout(layout)
Copyright (c) 2011 Ransui Iso, All rights reserved.
48. CountDownWidgetの実装
● 方針を確認する
– こちらは処理メソッドを実装しないといけない
QWidget attribute QTimer
CountDownWidget timer
QVBoxLayout
QLCDNumber
addWidget lcd_number
timer
start_countdown start timeout
stop
stop_countdown
do_countdown
reset_count
lcd_number
display
update_display
update
Copyright (c) 2011 Ransui Iso, All rights reserved.
49. CountDownWidget:UIコード
● サイズが自動伸長されるようにしてある
class CountDownWidget(QtGui.QWidget):
class CountDownWidget(QtGui.QWidget):
def __init__(self, parent=None):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent=parent)
QtGui.QWidget.__init__(self, parent=parent)
self.interval = 10
self.interval = 10
self.setup_ui()
self.setup_ui()
def setup_ui(self):
def setup_ui(self):
self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
self.timer = QtCore.QTimer(parent=self)
self.timer = QtCore.QTimer(parent=self)
self.timer.setInterval(self.interval)
self.timer.setInterval(self.interval)
Self.timer.timeout.connect(self.do_countdown)
Self.timer.timeout.connect(self.do_countdown)
self.lcd_number = QtGui.QLCDNumber(parent=self)
self.lcd_number = QtGui.QLCDNumber(parent=self)
self.lcd_number.setSizePolicy(QtGui.QSizePolicy.Expanding,
self.lcd_number.setSizePolicy(QtGui.QSizePolicy.Expanding,
QtGui.QSizePolicy.Expanding)
QtGui.QSizePolicy.Expanding)
self.lcd_number.setFrameStyle(QtGui.QFrame.NoFrame)
self.lcd_number.setFrameStyle(QtGui.QFrame.NoFrame)
self.lcd_number.setSegmentStyle(QtGui.QLCDNumber.Flat)
self.lcd_number.setSegmentStyle(QtGui.QLCDNumber.Flat)
self.lcd_number.setDigitCount(6)
self.lcd_number.setDigitCount(6)
layout = QtGui.QVBoxLayout()
layout = QtGui.QVBoxLayout()
layout.addWidget(self.lcd_number)
layout.addWidget(self.lcd_number)
self.setLayout(layout)
self.setLayout(layout)
self.reset_count()
self.reset_count()
Copyright (c) 2011 Ransui Iso, All rights reserved.
50. CountDownWidget:処理部分
● 以下のようになる
def update_display(self):
def update_display(self):
self.lcd_number.display("%6.2f" % (self.count / 100))
self.lcd_number.display("%6.2f" % (self.count / 100))
self.lcd_number.update()
self.lcd_number.update()
def do_countdown(self):
def do_countdown(self):
self.count = 1
self.count = 1
self.update_display()
self.update_display()
if self.count <= 0:
if self.count <= 0:
self.stop_countdown()
self.stop_countdown()
def start_countdown(self):
def start_countdown(self):
if self.count > 0:
if self.count > 0:
self.timer.start()
self.timer.start()
def stop_countdown(self):
def stop_countdown(self):
self.timer.stop()
self.timer.stop()
def reset_count(self):
def reset_count(self):
self.count = 18000
self.count = 18000
self.update_display()
self.update_display()
Copyright (c) 2011 Ransui Iso, All rights reserved.
51. MainWindowとかの構成
● 方針の確認
QMainWindow QWidget
main_window CountDownWidget
QVBoxLayout
CentralWidget
instance
QWidget
addWidget
panel countdown_widget
QVBoxLayout
QWidget
ButtonBoxWidget
QGridLayout
instance
button_box_widget
Copyright (c) 2011 Ransui Iso, All rights reserved.
52. 全体の構成コード
● main関数として実装してみる
def main():
def main():
app = QtGui.QApplication(sys.argv)
app = QtGui.QApplication(sys.argv)
panel = QtGui.QWidget()
panel = QtGui.QWidget()
countdown_widget = CountDownWidget(parent=panel)
countdown_widget = CountDownWidget(parent=panel)
button_box_widget = ButtonBoxWidget(parent=panel)
button_box_widget = ButtonBoxWidget(parent=panel)
panel_layout = QtGui.QVBoxLayout()
panel_layout = QtGui.QVBoxLayout()
panel_layout.addWidget(countdown_widget)
panel_layout.addWidget(countdown_widget)
panel_layout.addWidget(button_box_widget)
panel_layout.addWidget(button_box_widget)
panel.setLayout(panel_layout)
panel.setLayout(panel_layout)
panel.setFixedSize(320, 200)
panel.setFixedSize(320, 200)
main_window = QtGui.QMainWindow()
main_window = QtGui.QMainWindow()
main_window.setWindowTitle("Ramen Timer")
main_window.setWindowTitle("Ramen Timer")
main_window.setCentralWidget(panel)
main_window.setCentralWidget(panel)
main_window.show()
main_window.show()
Copyright (c) 2011 Ransui Iso, All rights reserved.
53. SIGNALの接続
● main関数内で行う
def main():
def main():
app = QtGui.QApplication(sys.argv)
app = QtGui.QApplication(sys.argv)
... 中略 ...
... 中略 ...
button_box_widget.start_button.clicked.connect(
button_box_widget.start_button.clicked.connect(
countdown_widget.start_countdown)
countdown_widget.start_countdown)
button_box_widget.stop_button.clicked.connect(
button_box_widget.stop_button.clicked.connect(
countdown_widget.stop_countdown)
countdown_widget.stop_countdown)
button_box_widget.reset_button.clicked.connect(
button_box_widget.reset_button.clicked.connect(
countdown_widget.reset_count)
countdown_widget.reset_count)
button_box_widget.quit_button.clicked.connect(
button_box_widget.quit_button.clicked.connect(
app.quit)
app.quit)
app.exec_()
app.exec_()
Copyright (c) 2011 Ransui Iso, All rights reserved.
54. ラーメンタイマー完成
● 実際に動かしてみる
● デザインワークは超大切
● よりよいラーメンタイマーを目指して
– ラーメン出来上がりまで画面注視とかどんだけ
– 5分のラーメンもあるんですけど
– 2杯同時に作るときお湯入れるの時間差あるんですが
– ワシは「麺硬め」だが奴は「普通」が好みらしい
– 後入れスープやってる間に麺が伸びちゃうのはNG
明日のSprintのネタとしてやってみても楽しいかと
Copyright (c) 2011 Ransui Iso, All rights reserved.
56. assistant
● Qtのドキュメントビュアー
– Webでも参照できるが専用ビュアーは色々と便利
– これを使いこなせるようになれば脱初心者
– 基本英語だけどがんばって!
Copyright (c) 2011 Ransui Iso, All rights reserved.
57. designer
● GUIレイアウト作成ツール
– このツールから入門したくなるという誘引力は強烈
Copyright (c) 2011 Ransui Iso, All rights reserved.
58. designerの暗黒面
注意・警告
一見初心者に優しげなこのツールは実は
上級者向け。QtのGUI構成の基本が分
かっていない時点で使うと自動生成され
るコードに振り回される等の「害あって
も益無し」状態になる可能性が高い
まずは基本をしっかりマスターしてから
「手間を減らす」ツールとして使うこと
を推奨
Copyright (c) 2011 Ransui Iso, All rights reserved.
60. Graphicsの取り扱い
● 実装例:アナログ時計
QtにはOpenGLサポート等の高度なグラフィックス
処理機能があるが、今回は最も基本的な機能を使って
作ってみる
Copyright (c) 2011 Ransui Iso, All rights reserved.
61. コンポーネント構成
● 操作する部分が無いのでシンプル
QMainWindow QWidget attribute QTimer
main_window ClockWidget timer
instance
CentralWidget
QPixmap
clock_widget offscreen
setup_ui timer
offscreen
start timeout
draw_scales
drawing 20 times/sec
QPaintEvent
draw_needles
redraw_clock
use
paintEvent
ClockWidget
Copyright (c) 2011 Ransui Iso, All rights reserved.
62. Graphics描画機能
● QPainterというコンポーネントを使う
painter = QtGui.QPainter()
painter = QtGui.QPainter()
描画対象を指定 painter.begin(self.pixmap)
painter.begin(self.pixmap)
描画方法の設定 painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setPen(QtGui.QPen(QtCore.Qt.white, 4)
painter.setPen(QtGui.QPen(QtCore.Qt.white, 4)
描画処理 painter.drawLine(10, 10, 100, 100)
painter.drawLine(10, 10, 100, 100)
painter.drawPixmap(30, 10, foo_pixmap)
painter.drawPixmap(30, 10, foo_pixmap)
描画終了 painter.end()
painter.end()
Copyright (c) 2011 Ransui Iso, All rights reserved.
63. Tiny Media Player
How to playback sound and video data with Qt
Copyright (c) 2011 Ransui Iso, All rights reserved.
64. メディアデータの再生
● 超単純なメディアプレーヤー
– Phononというメディア再生フレームワークを使う
Copyright (c) 2011 Ransui Iso, All rights reserved.
65. コンポーネント構成
● 1個のwidgetに全部載せる構成にしている
QMainWindow QWidget Phonon.VideoPlayer
main_window VideoPlayerWidget player
QVBoxLayout
instance addWidget
CentralWidget instance QHBoxLayout
video_player_widget QPushButton
play_pause_button
addWidget
Phonon.SeekSlider
slider
Layoutの上に直接Layoutを追加することができる
● これをうまく使うと相当複雑なレイアウトも実現できる
Copyright (c) 2011 Ransui Iso, All rights reserved.
66. Web Browser
Qt has WebKit based browser component
Copyright (c) 2011 Ransui Iso, All rights reserved.
67. QWebKitを使ってみる
● 簡易ブラウザなら簡単に作成可能
● 自作アプリの一部にHTMLVewer
として組み込む
● HTMLパーサとして利用(DOMに
アクセスできる)
● ユーザのアクション(リンクク
リック等)を全てHookできるの
で挙動のカスタマイズはやり放
題
● 本当にHTML+CSS+Javascript
レンダリングの機能しかないの
で、それなりに使えるブラウザ
にするためには結構コードを書
かなくちゃいけない
Copyright (c) 2011 Ransui Iso, All rights reserved.
68. GUIはdesignerを使ってみた
● レイアウトの階層構造を意識しながら作る
今回はQMainWindowにメニューバーとステータスバー
も追加している
Copyright (c) 2011 Ransui Iso, All rights reserved.
69. Pythonモジュールの生成
● pyuic4 コマンドを使う
– designerは .ui というXMLファイルを吐き出す
– pyuic4 help でオプションを確認できる
– 基本は以下のように使う
● pyuic4 o foo_ui.py foo.ui
– 出力されるモジュールファイルは手で編集しない
– 利用の例はソースコードを参照
● この例では TinyBrowser.py がメインで MainWindowUI.py
が pyuic4 で生成されたGUIコンポーネントのモジュール
● designerを使う場合は、GUIとLogicはモジュール単位で分割され
ることになる
Copyright (c) 2011 Ransui Iso, All rights reserved.
71. Qtプログラミングの情報源
● Qt/PyQtはドキュメントが超充実
– ふんだんに用意されているプログラミング例はC++で書
かれているが、Pythonプログラミングの練習にもなる
ので、どんどん移植してみよう
● 書籍
– 日本語で読めるものは少ない!
– O'REILLY「入門 Qt4 プログラミング」
● すごく高度な技が載っているわけではない
が、基本的なGUIコンポーネントをまんべん
なく取り扱っている。Qtドキュメントとの
併読がお勧め
Copyright (c) 2011 Ransui Iso, All rights reserved.
72. Thank you for listening
Happy Hacking with PyQt
Copyright (c) 2011 Ransui Iso, All rights reserved.