自分用のメモです。
自分の動作環境は
- PyCharm 無料版
- Python 3.7.4 or 3.6(仮想で両方で試した)
- Pycharm内臓のVenvでパッケージ管理
導入
PythonでQtを扱うにはPyQtとPysideの2つがある。
verごとに書くとこんな感じ
- Qt4 : PyQt4,PyiSde
- Qt5 : PyQt5,PySide2
という感じになる。
現行で使うならQt5系を利用するほうがいいだろうということでPyQt5,PySide2を選ぶことになる。
選定にあたりいろいろググった結果PySide2ってのがQt公式がサポートするQt5バインディング(Qt for Pythonと呼ばれてるのがこれにあたる)らしいのでこれを使うことにする。
ui -> pyをする使い方
QtDesignerというソフトを使ってグラフィカルにUIをデザインしてそのファイル(.ui)をpyside2-uic.exeを使って.py拡張子ファイルに変換してインポートする手法のこと。
この方法が一番情報も多くて使いやすいと思う。
いろんな人が情報を公開しているのでその辺を読むといいだろう
note.mu
UIファイルの更新のたびにPyCharm側でpyside2-uic.exeを自動で呼び出すこともできる。
qiita.com
QtQuickについて
これについては公式の説明が分かりやすい
Qt Quick 入門 第1回: Qt Quick とは - Qt Japanese Blog
ようはuiファイルではなくqmlというjson風の表記を使ってUIをデザインしていく手法であり比較的楽にかっこいいUIを作成できる。
画像は私が作成したUIであるが5分ぐらいでこのぐらいのGUIを作成できる。
PySide2導入とテスト
早速本題に入る。
まずはPyCharmで新規プロジェクトをつくる。
上部メニューのFile/settingでこの画面が開くのでproject interpreterの画面まで持ってくる。
+ボタンをクリックして必要なモジュールをインストールしてくる。
ここではPySide2を検索して持ってくる。
インストールができたら.pyファイルを作成して以下のリンクのコードを実行してみる。
https://doc.qt.io/qtforpython/tutorials/basictutorial/dialog.html
このような画面が出ていればPySide2の導入はOK
PySide2でQtQuick(qml)ファイルを扱う
次にqmlファイルを作るためにQt Creatorをダウンロードしてくる。
Download Qt: Choose commercial or open source
アカウントとか聞かれますけどスキップで何とかなります。
適当に自分の欲しいものをインストールしてください
CreatorとQt5.x系があればOKです。
Qt Creatorをインストール出来たら開いて新規プロジェクトでUI Prototypeを選ぶ
キットに関しては適当に選ぶといいです。
するとこのような画面になるのでデザインをクリックします。
UIデザイン画面が開くので次にインポートタブを開きます。
QtQuick.ControlsとQtQuick.Controls.Materialsを追加
そうするとエレメントタブにボタン類が入ってくる
ボタンをドラッグしてUIに追加する。
次にできたqmlをPycharmプロジェクトに入れてあげる
そしたらこのコードを実行
import sys from PySide2.QtGui import QGuiApplication from PySide2.QtQml import QQmlApplicationEngine from PySide2.QtCore import QUrl if __name__ == '__main__': app = QGuiApplication(sys.argv) engine = QQmlApplicationEngine() engine.load(QUrl("mypyside2.qml")) if not engine.rootObjects(): sys.exit(-1) sys.exit(app.exec_())
先ほど作ったUIが表示できていればOK
ボタンへのイベント追加
次に配置したウィジェットとPythonコードをつないでいく。
python code
import sys import os from PySide2 import QtCore, QtWidgets, QtQml class Connect(QtCore.QObject): def __init__(self, parent=None): super(Connect, self).__init__(parent) @QtCore.Slot() def button_clicked(self): print("button clicked") if __name__ == "__main__": os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material" app = QtWidgets.QApplication(sys.argv) myconnect = Connect() engine = QtQml.QQmlApplicationEngine() ctx = engine.rootContext() ctx.setContextProperty("Connect", myconnect) engine.load('mypyside2.qml') if not engine.rootObjects(): sys.exit(-1) sys.exit(app.exec_())
qml code
import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.3 import QtQuick.Controls.Material 2.0 Window { visible: true width: 640 height: 480 title: qsTr("Hello World") Button { id: button x: 270 y: 220 text: qsTr("Button") onClicked:Connect.button_clicked()//python側のクラスとつなぐ } }
実行してボタンをクリックするとprint文が実行されるはず。
説明(Python側)
まずQtCore.QObjectを継承したクラス(Connect)を作成する。
@QtCore.Slotでqml側にメソッドの存在を通知する。これによりqml側でdef以下を呼べるようになる。
@表記はPythont的に言うとデコレータってやつらしい。
os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
Materialテーマを有効にする記述で特に必要はない
有効にしていると以下のリンクのようなことができる。
https://doc.qt.io/qt-5/qtquickcontrols2-material.html
ctx = engine.rootContext()
ctx.setContextProperty("Connect", myconnect)
engine内部の子ウィジェットを検索可能にする記述(あってるかわかんないけど
QMLにアクセスしたりQMLからアクセスしたりする場合はこの二行が必須っぽい
参考文献
rootcontextの記述はC++も同様なので参考になった。
その他qmlとの相互イベント通知の参考になる。
QMLとC++のバインディング - Qiita
今回の記事はこの問題の劣化版みたいなものなのでこっちのスレッド読むともっといいのがつくれると思う
Connect python signal to QML ui slot with PySide2 - Stack Overflow
公式のサンプルとチュートリアル
QMLの例が2個しかないけど書き方の参考にはなる。
https://doc.qt.io/qtforpython/tutorials/index.html