QT を Python から利用するためのライブラリには PyQt や PySide 等が存在します。PySide は元々 QT4 向けのライブラリでしたが、QT5 に対応するために新たに PySide2 が開発されました。PySide2 は Qt for Python ともよばれています。
Q: PySide? Qt for Python? what is the name?
A: The name of the project is Qt for Python and the name of the module is PySide2.
Q: Why PySide2 and not just PySide?
A: Since PySide was developed for Qt4, when the port was made to support Qt5, the name was changed to PySide2 to infer that is was a newer version.
https://wiki.qt.io/Qt_for_Python
PySide2 について基本的な使い方を記載します。今回は VirtualBox と Vagrant で Debian9/stretch をインストールして利用することにします。
以下のコマンドで同期しつつ、X11 で動作確認します。
vagrant rsync
vagrant rsync-auto
インストール
必要なバージョンの python や qt5 等をインストールする必要があります。
python
pyenv を利用すると簡単です。今回は 2.7.15 を利用することにします。
pyenv global 2.7.15
PySide2 自体は pip でインストールできます。
pip install --index-url=http://download.qt.io/snapshots/ci/pyside/5.9/latest/ pyside2 --trusted-host download.qt.io
qt5 および依存ライブラリ
OpenGL 等が依存先としてまとめてインストールされます。環境によっては pyside2/wiki/Dependencies を参照して、不足しているパッケージを適宜追加でインストールします。
sudo apt-get install qt5-default
sudo apt-get install qttools5-dev-tools
動作検証
DISPLAY が利用できない場合は仮想ディスプレイを作成して利用します。
sample.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import exit, argv
from PySide2.QtWidgets import QApplication, QLabel
def Main():
app = QApplication(argv)
label = QLabel("Hello World")
label.show()
exit(app.exec_())
if __name__ == '__main__':
Main()

古い環境の場合は必要な共有ライブラリが存在せずにエラーになります。新しい OS を利用するか、必要に応じてソースコードからビルドします。例えば以下のような libc のバージョンエラーが発生します。
$ python sample.py
Traceback (most recent call last):
File "sample.py", line 2, in <module>
from PySide2.QtWidgets import QApplication, QLabel
ImportError: /lib64/libc.so.6: version `GLIBC_2.14' not found (required by /home/username/.pyenv/versions/2.7.15/lib/python2.7/site-packages/PySide2/QtWidgets.so)
以下のようにソースコードからビルドして対応できます。
mkdir glibc_install
cd glibc_install/
wget http://ftp.gnu.org/gnu/glibc/glibc-2.14.1.tar.gz
tar zxvf glibc-2.14.1.tar.gz
cd glibc-2.14.1/
mkdir build
cd build/
../configure --prefix=/opt/glibc-2.14
make -j4
sudo make install
export LD_LIBRARY_PATH=/opt/glibc-2.14/lib
その他の検証用のコード
HTML の利用
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import exit, argv
from PySide2.QtWidgets import QApplication, QLabel
def Main():
app = QApplication(argv)
label = QLabel("<font color=red size=40>Hello World!</font>")
label.show()
exit(app.exec_())
if __name__ == '__main__':
Main()

QML の利用
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import exit, argv
from PySide2.QtWidgets import QApplication
from PySide2.QtQuick import QQuickView
from PySide2.QtCore import QUrl
def Main():
app = QApplication(argv)
view = QQuickView()
url = QUrl("view.qml")
view.setSource(url)
view.show()
exit(app.exec_())
if __name__ == '__main__':
Main()
view.qml
import QtQuick 2.0
Rectangle {
width: 200
height: 200
color: "green"
Text {
text: "Hello World"
anchors.centerIn: parent
}
}

メッセージボックスの利用
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import exit, argv
from PySide2.QtWidgets import QApplication, QMessageBox
def Main():
app = QApplication(argv)
msg_box = QMessageBox()
msg_box.setText("Hello World!")
msg_box.show()
exit(app.exec_())
if __name__ == '__main__':
Main()

関数を実行する push ボタンの利用
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import exit, argv
from PySide2.QtWidgets import QApplication, QPushButton
def f():
print("clicked!")
def Main():
app = QApplication(argv)
# push 時に関数を実行
button = QPushButton("Click me")
button.clicked.connect(f)
buttonExit = QPushButton("Exit")
buttonExit.clicked.connect(app.exit)
button.show()
buttonExit.show()
exit(app.exec_())
if __name__ == '__main__':
Main()

QWidget を継承したクラスを作成
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import exit, argv
from random import choice
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QWidget, QLabel, QPushButton, QVBoxLayout, QApplication
class MyWidget(QWidget):
def __init__(self):
super(MyWidget, self).__init__()
self.nums = ["1", "2", "3"]
# ラベルの作成
self.text = QLabel("0")
self.text.setAlignment(Qt.AlignCenter)
# push ボタンの作成
self.button = QPushButton("Click me!")
self.button.clicked.connect(self.f)
# ラベルとボタンを格納する箱となるレイアウトの作成
self.layout = QVBoxLayout()
self.layout.addWidget(self.text)
self.layout.addWidget(self.button)
self.setLayout(self.layout)
def f(self):
self.text.setText(choice(self.nums))
def Main():
app = QApplication(argv)
widget = MyWidget()
widget.show()
exit(app.exec_())
if __name__ == '__main__':
Main()

QDialog を継承したクラスを作成
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import exit, argv
from PySide2.QtWidgets import QLineEdit, QPushButton, QApplication, QVBoxLayout, QDialog
class MyForm(QDialog):
def __init__(self):
super(MyForm, self).__init__()
self.setWindowTitle("My Form")
# 入力フォーム、ボタン
self.edit = QLineEdit("xxxx")
self.button = QPushButton("yyyy")
self.button.clicked.connect(self.f)
# レイアウト
layout = QVBoxLayout()
layout.addWidget(self.edit)
layout.addWidget(self.button)
self.setLayout(layout)
def f(self):
print ("Hello %s" % self.edit.text())
def Main():
app = QApplication(argv)
form = MyForm()
form.show()
exit(app.exec_())
if __name__ == '__main__':
Main()

シグナルとスロット
コールバックの代替としてシグナルとスロットが利用できます。QPushButton も内部的にはシグナルとスロットを利用しています。
sample.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
from PySide2.QtCore import QObject, Signal, Slot
from time import sleep
class MyQObject(QObject): # QObject を継承すること
clicked = Signal()
changed = Signal(int)
def __init__(self):
super(MyQObject, self).__init__()
self.clicked.connect(self.onClicked)
self.changed.connect(self.onChanged)
@Slot()
def onClicked(self):
print("clicked")
@Slot(int)
def onChanged(self, num):
print("changed: %d" % num)
def f(self):
self.clicked.emit()
self.changed.emit(123)
def Main():
obj = MyQObject()
while True:
obj.f()
sleep(1.0)
if __name__ == '__main__':
Main()
実行例
$ python sample.py
clicked
changed: 123
clicked
changed: 123
...
プロパティ
Property() を利用すると、Qt オブジェクトと Python オブジェクトのプロパティをバインディングできます。
sample.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
from sys import exit, argv
from PySide2.QtCore import Property
from PySide2.QtWidgets import QWidget, QLabel, QVBoxLayout, QApplication
class MyWidget(QWidget):
def __init__(self):
super(MyWidget, self).__init__()
self.label = QLabel("xxxx")
self.layout = QVBoxLayout()
self.layout.addWidget(self.label)
self.setLayout(self.layout)
def GetLabelText(self):
return self.label.text()
def SetLabelText(self, text):
self.label.setText(text)
labelText = Property(str, GetLabelText, SetLabelText)
def Main():
app = QApplication(argv)
widget = MyWidget()
widget.show()
print(widget.labelText)
widget.labelText = "yyyy"
exit(app.exec_())
if __name__ == '__main__':
Main()
QLabel の初期値が widget.labelText で取得できています。逆に widget.labelText に値を設定すると QLabel に反映されます。
$ python sample.py
xxxx

バージョン情報の確認
ipython 等で以下のコマンドを実行して PySide および Qt のバージョンを確認できます。
PySide バージョン
import PySide2.QtCore
print(PySide2.__version__)
→ e.g. 2.0.0
PySide をコンパイルした Qt バージョン
print(PySide2.QtCore.__version__)
→ e.g. 5.11.0
実行時に利用されている Qt バージョン
print(PySide2.QtCore.qVersion())
→ e.g. 5.11.0
都度参照するためのドキュメント
インフラ構築と自動化が得意。TerraformとAnsibleでインフラを自動構築するお仕事が多め
記事の執筆者にステッカーを贈る
有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。
さらに詳しく →Feedbacks
ログインするとコメントを投稿できます。



