QtPyについて
QtPyはQtのPythonバインディングであるPyQtやPySideを抽象化したパッケージ.過去の記事で情報をまとめてある.
反面教師あり学習 - QtPyでウインドウを表示する最小のコード
使用例
単純な例
例として,次のようなテキストとボタンが配置されたウインドウについて考える:
このウインドウは以下のコードによって表示できる.
ここで,utils
というモジュールに入っているhbox
とvbox
が今回作った関数である:
# -*- coding: utf-8 -*-
import sys
from qtpy.QtWidgets import (
QMainWindow, QApplication, QPushButton, QLabel, QWidget,
)
from qtpy.QtCore import QObject, Qt
from utils import hbox, vbox
class MainWindow(QMainWindow):
def __init__(self, **kwargs):
super(MainWindow, self).__init__(**kwargs)
self.main_widget = QWidget(self)
# --- レイアウトの構築(自作の関数を利用) ---
layout = vbox([
QLabel('Hello, World!'),
hbox([QPushButton('OK'), QPushButton('Cancel')])
])
# --------------------------------------------
self.main_widget.setLayout(layout)
self.setCentralWidget(self.main_widget)
self.resize(160, 120)
def main():
app = QApplication(sys.argv)
view = MainWindow()
view.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
同じ結果をQVBoxLayout
とQHBoxLayout
クラスを用いて得るためのコードは下記のようになる(レイアウトの構築部分以外はほぼ同じなので省略):
from qtpy.QtWidgets import QVBoxLayout, QHBoxLayout
# --- レイアウトの構築(Qtのレイアウトのクラスを利用) ---
btn_layout = QHBoxLayout()
btn_layout.addWidget(QPushButton('OK'))
btn_layout.addWidget(QPushButton('Cancel'))
layout = QVBoxLayout()
layout.addWidget(QLabel('Hello, World!'))
layout.addLayout(btn_layout)
# --------------------------------------------------------
hbox
,vbox
を使った場合の方がコーディング量が少ない上にコード上でどのようなレイアウトを組んでいるのかが分かりやすくなっていると思う.
また,QtでQLayout
に子要素を追加する際,
- 子要素が
QWidget
の場合はaddWidget
- 子要素が
QLayout
の場合はaddLayout
のように関数を使い分ける必要があるのだが,そのあたりもhbox
,vbox
の中でうまくラップしてある.
Widgetのサイズの比率を変える例
次のようにレイアウト内のWidgetのサイズの比率が異なるレイアウトを作る場合(QLabel : QSpinBox = 1 : 2),
要素の代わりに(要素, 比率)
のタプルを渡すことで表現できるようにした.
ちなみに,比率を指定しなかったWidget w
は(w, 0)
として扱われる.
# -*- coding: utf-8 -*-
import sys
from qtpy.QtWidgets import (
QMainWindow, QApplication, QPushButton, QLabel, QSpinBox, QWidget,
)
from qtpy.QtCore import QObject, Qt
from utils import hbox, vbox
class MainWindow(QMainWindow):
def __init__(self, **kwargs):
super(MainWindow, self).__init__(**kwargs)
self.main_widget = QWidget(self)
value_layout = vbox([
hbox([(QLabel('Value 1'), 1), (QSpinBox(), 2)]),
hbox([(QLabel('Value 2'), 1), (QSpinBox(), 2)]),
hbox([(QLabel('Value 3'), 1), (QSpinBox(), 2)]),
hbox([(QLabel('Value 4'), 1), (QSpinBox(), 2)]),
hbox([(QLabel('Value 5'), 1), (QSpinBox(), 2)]),
])
btn_layout = hbox([QPushButton('OK'), QPushButton('Cancel')])
layout = vbox([
value_layout,
btn_layout
])
self.main_widget.setLayout(layout)
self.setCentralWidget(self.main_widget)
self.setMinimumSize(320, 240)
def main():
app = QApplication(sys.argv)
view = MainWindow()
view.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
コード
ここまで関数の機能を紹介してきたが,utils.py
の実装は以下のようになってる:
# -*- coding: utf-8 -*-
from qtpy.QtWidgets import (
QWidget, QLayout, QHBoxLayout, QVBoxLayout
)
def _generate_box_layout(t: str, widgets):
if t == 'h':
layout = QHBoxLayout()
elif t == 'v':
layout = QVBoxLayout()
else:
raise RuntimeError()
for widget in widgets:
if isinstance(widget, tuple):
w, stretch = widget
assert isinstance(stretch, int)
else:
w = widget
stretch = 0
if isinstance(w, QWidget):
layout.addWidget(w, stretch)
elif isinstance(w, QLayout):
layout.addLayout(w, stretch)
else:
raise TypeError(f'Illegal type of widget is detected: {widget}')
return layout
def hbox(widgets):
return _generate_box_layout('h', widgets)
def vbox(widgets):
return _generate_box_layout('v', widgets)
さいごに
大規模なアプリケーションを作る場合はちゃんとQt Designerを使いこなした方がいいと思うけど, ウインドウひとつだけの簡易のツール作る程度だったらコード上でGUI組む方がファイル数増えないしお手軽でいいと思う.