【PyQt】ウィジェットのレイアウト方法

PythonのGUIツールキットであるPyQtで画面要素であるウィジェットをレイアウトする方法について紹介します。
目次
ウィジェット(Widgets)のレイアウト方法
PyQtでは部品となる様々なウィジェットを使って画面開発を行うことができます。
PyQtで開発する際のサンプルテンプレートプログラムは「QWidgetを継承した画面開発のテンプレート」や「QMainWindowを継承した画面開発のテンプレート」でまとめています。また、ウィジェットとしてどのようなものがあるかは「画面開発で使えるQtWidgetsを紹介」でまとめていますので興味があれば参考にしてください。
さて、画面開発では、各ウィジェットをきれいに画面上に配置していきたいわけですが、PyQtではレイアウト用のクラスが用意されているので、非常に簡単にウィジェットを配置することができます。
レイアウトの使い方の手順は以下のような形になります。
- レイアウトオブジェクトを生成する。
- setLayoutメソッドを使って、親ウィジェットにレイアウトを設定する。
- addWidgetメソッドを使って、画面要素となるウィジェットをレイアウトに追加する。
本記事では、PyQtで提供されているレイアウトクラスと使用例について紹介します。なお、以降の説明では「QWidgetを継承した画面開発のテンプレート」をベースに部品となるウィジェットを配置する方法で説明していきます。
垂直方向にレイアウト:QVBoxLayout
ウィジェットを以下のように垂直にレイアウトしたい場合には、QVBoxLayoutを使用します。Vというのは垂直方向のverticalを意味しています。

QVBoxLayoutを使用してウィジェットを配置する場合には以下のように使用します。
import sys
from PyQt6 import QtCore as qtc
from PyQt6 import QtGui as qtg
from PyQt6 import QtWidgets as qtw
class MainWindow(qtw.QWidget):
"""メインウィンドウ"""
def __init__(self):
super().__init__()
self.setWindowTitle("QVBoxLayout")
self.resize(320, 240)
# 垂直(vertical)レイアウトの作成とメインウィンドウへの設定
vertical_layout = qtw.QVBoxLayout()
self.setLayout(vertical_layout)
# ウィジェットを用意
self.label = qtw.QLabel("ラベル", self)
self.line_edit = qtw.QLineEdit(self, placeholderText="入力してください。")
self.check = qtw.QCheckBox("チェック", self)
self.button = qtw.QPushButton("ボタン", self)
# レイアウトにウィジェットを追加
vertical_layout.addWidget(self.label)
vertical_layout.addWidget(self.line_edit)
vertical_layout.addWidget(self.check)
vertical_layout.addWidget(self.button)
vertical_layout.addStretch()
# 画面表示
self.show()
def main():
"""メイン関数"""
app = qtw.QApplication(sys.argv)
mv = MainWindow()
sys.exit(app.exec())
if __name__ == "__main__":
main()【表示結果】

【詳細説明】
# 垂直(vertical)レイアウトの作成とメインウィンドウへの設定
vertical_layout = qtw.QVBoxLayout()
self.setLayout(vertical_layout)まずは、QVBoxLayoutを生成し、メインウィンドウへ設定します。
# ウィジェットを用意
self.label = qtw.QLabel("ラベル", self)
self.line_edit = qtw.QLineEdit(self, placeholderText="入力してください。")
self.check = qtw.QCheckBox("チェック", self)
self.button = qtw.QPushButton("ボタン", self)
# レイアウトにウィジェットを追加
vertical_layout.addWidget(self.label)
vertical_layout.addWidget(self.line_edit)
vertical_layout.addWidget(self.check)
vertical_layout.addWidget(self.button)
vertical_layout.addStretch()後は、上記のように必要なウィジェットを用意してレイアウトのaddWidgetメソッドを使用してウィジェットを指定することで順番に垂直方向に配置されていきます。
addStretchメソッドは、各ウィジェットを上詰めで表示したいために追加しています。この部分を除くと画面サイズいっぱい使うように配置がされます。
水平方向にレイアウト:QHBoxLayout
ウィジェットを以下のように水平にレイアウトしたい場合には、QHBoxLayoutを使用します。Hというのは水平方向のhorizontalを意味しています。

QHBoxLayoutを使用してウィジェットを配置する場合には以下のように使用します。
import sys
from PyQt6 import QtCore as qtc
from PyQt6 import QtGui as qtg
from PyQt6 import QtWidgets as qtw
class MainWindow(qtw.QWidget):
"""メインウィンドウ"""
def __init__(self):
super().__init__()
self.setWindowTitle("QHBoxLayout")
self.resize(640, 240)
# 水平(horizontal)レイアウトの作成とメインウィンドウへの設定
horizontal_layout = qtw.QHBoxLayout()
self.setLayout(horizontal_layout)
# ウィジェットを用意
self.label = qtw.QLabel("ラベル", self)
self.line_edit = qtw.QLineEdit(self, placeholderText="入力してください。")
self.check = qtw.QCheckBox("チェック", self)
self.button = qtw.QPushButton("ボタン", self)
# レイアウトにウィジェットを追加
horizontal_layout.addWidget(self.label)
horizontal_layout.addWidget(self.line_edit)
horizontal_layout.addWidget(self.check)
horizontal_layout.addWidget(self.button)
horizontal_layout.addStretch()
# 画面表示
self.show()
def main():
"""メイン関数"""
app = qtw.QApplication(sys.argv)
mv = MainWindow()
sys.exit(app.exec())
if __name__ == "__main__":
main()【表示結果】

【詳細説明】
# 水平(horizontal)レイアウトの作成とメインウィンドウへの設定
horizontal_layout = qtw.QHBoxLayout()
self.setLayout(horizontal_layout)まずは、QHBoxLayoutを生成し、メインウィンドウへ設定します。
# ウィジェットを用意
self.label = qtw.QLabel("ラベル", self)
self.line_edit = qtw.QLineEdit(self, placeholderText="入力してください。")
self.check = qtw.QCheckBox("チェック", self)
self.button = qtw.QPushButton("ボタン", self)
# レイアウトにウィジェットを追加
horizontal_layout.addWidget(self.label)
horizontal_layout.addWidget(self.line_edit)
horizontal_layout.addWidget(self.check)
horizontal_layout.addWidget(self.button)
horizontal_layout.addStretch()後は、上記のように必要なウィジェットを用意してレイアウトのaddWidgetメソッドを使用してウィジェットを指定することで順番に水平方向に配置されていきます。
addStretchメソッドは、各ウィジェットを左詰めで表示したいために追加しています。この部分を除くと画面サイズいっぱい使うように配置がされます。
グリッド状にレイアウト:QGridLayout
ウィジェットを以下のようにグリッド状にレイアウトしたい場合には、QGridLayoutを使用します。以下の図で(0, 0)というように書いているのは(行, 列)の位置を示しています。

QGridLayoutを使用してウィジェットを配置する場合には以下のように使用します。
import sys
from PyQt6 import QtCore as qtc
from PyQt6 import QtGui as qtg
from PyQt6 import QtWidgets as qtw
class MainWindow(qtw.QWidget):
"""メインウィンドウ"""
def __init__(self):
super().__init__()
self.setWindowTitle("QGridLayout")
self.resize(320, 240)
# グリッド(Grid)レイアウトの作成とメインウィンドウへの設定
grid_layout = qtw.QGridLayout()
self.setLayout(grid_layout)
# ウィジェットを用意
self.label = qtw.QLabel("ラベル", self)
self.line_edit = qtw.QLineEdit(self, placeholderText="入力してください。")
self.check = qtw.QCheckBox("チェック", self)
self.button = qtw.QPushButton("ボタン", self)
# レイアウトにウィジェットを追加
grid_layout.addWidget(self.label, 0, 0)
grid_layout.addWidget(self.line_edit, 0, 1)
grid_layout.addWidget(self.check, 1, 0)
grid_layout.addWidget(self.button, 1, 1)
# 画面表示
self.show()
def main():
"""メイン関数"""
app = qtw.QApplication(sys.argv)
mv = MainWindow()
sys.exit(app.exec())
if __name__ == "__main__":
main()【表示結果】

【詳細説明】
# グリッド(Grid)レイアウトの作成とメインウィンドウへの設定
grid_layout = qtw.QGridLayout()
self.setLayout(grid_layout)まずは、QGridLayoutを生成し、メインウィンドウへ設定します。
# ウィジェットを用意
self.label = qtw.QLabel("ラベル", self)
self.line_edit = qtw.QLineEdit(self, placeholderText="入力してください。")
self.check = qtw.QCheckBox("チェック", self)
self.button = qtw.QPushButton("ボタン", self)
# レイアウトにウィジェットを追加
grid_layout.addWidget(self.label, 0, 0)
grid_layout.addWidget(self.line_edit, 0, 1)
grid_layout.addWidget(self.check, 1, 0)
grid_layout.addWidget(self.button, 1, 1)後は、上記のように必要なウィジェットを用意してレイアウトのaddWidgetメソッドを使用してウィジェットを指定します。この時、ウィジェットを配置したい行と列の数字も指定します。
なお、あらかじめ画面が何行何列かということは定義しておく必要はなく、指定された数字に配置されるように自動的に配置してくれます。
フォーム形式のレイアウト:QFormLayout
よくプログラムでは以下のような入力フォームの画面があると思いますが、このようなレイアウトを作りたい場合には、QFormLayoutを使用します。

QFormLayoutを使用してウィジェットを配置する場合には以下のように使用します。
import sys
from PyQt6 import QtCore as qtc
from PyQt6 import QtGui as qtg
from PyQt6 import QtWidgets as qtw
class MainWindow(qtw.QWidget):
"""メインウィンドウ"""
def __init__(self):
super().__init__()
self.setWindowTitle("QFormLayout")
self.resize(320, 240)
# フォーム(form)レイアウトの作成とメインウィンドウへの設定
form_layout = qtw.QFormLayout()
self.setLayout(form_layout)
# ウィジェットを用意
self.label = qtw.QLabel("入力フォーム", self)
self.line_edit1 = qtw.QLineEdit(self)
self.line_edit2 = qtw.QLineEdit(self)
self.button = qtw.QPushButton("ボタン", self)
# フォームレイアウトへの配置
form_layout.addRow(self.label)
form_layout.addRow("入力 1", self.line_edit1)
form_layout.addRow("入力 2", self.line_edit2)
form_layout.addRow(self.button)
# 画面表示
self.show()
def main():
"""メイン関数"""
app = qtw.QApplication(sys.argv)
mv = MainWindow()
sys.exit(app.exec())
if __name__ == "__main__":
main()【表示結果】

【詳細説明】
# フォーム(form)レイアウトの作成とメインウィンドウへの設定
form_layout = qtw.QFormLayout()
self.setLayout(form_layout)まずは、QGridLayoutを生成し、メインウィンドウへ設定します。
# ウィジェットを用意
self.label = qtw.QLabel("入力フォーム", self)
self.line_edit1 = qtw.QLineEdit(self)
self.line_edit2 = qtw.QLineEdit(self)
self.button = qtw.QPushButton("ボタン", self)
# フォームレイアウトへの配置
form_layout.addRow(self.label)
form_layout.addRow("入力 1", self.line_edit1)
form_layout.addRow("入力 2", self.line_edit2)
form_layout.addRow(self.button)後は、上記のように必要なウィジェットを用意してレイアウトに追加します。フォームレイアウトではaddRowメソッドを使用しています。第1引数にフォームの項目名を入力し、第2引数を指定することでフォームの形式で表示してくれます。
また、QLabelやQPushButton等についても上記のように配置していくことできるので、簡単に入力フォームのレイアウトを作ることが可能です。
レイアウトの組み合わせ
上記で各種レイアウトの使い方を紹介してきましたが、レイアウトは以下のように組み合わせで使用することも可能です。

上記図のようにQVBoxLayoutの中にQHBoxLayoutの行があるような例で使い方を見てみましょう。
import sys
from PyQt6 import QtCore as qtc
from PyQt6 import QtGui as qtg
from PyQt6 import QtWidgets as qtw
class MainWindow(qtw.QWidget):
"""メインウィンドウ"""
def __init__(self):
super().__init__()
self.setWindowTitle("Layoutの組み合わせ")
self.resize(320, 240)
# レイアウトの作成とメインウィンドウへの設定
layout = qtw.QVBoxLayout()
self.setLayout(layout)
# ウィジェットを用意
self.label1 = qtw.QLabel("ラベル", self)
self.label2 = qtw.QLabel("入力欄: ", self)
self.line_edit = qtw.QLineEdit(self, placeholderText="入力してください。")
self.button = qtw.QPushButton("ボタン", self)
# レイアウトにウィジェットを追加
layout.addWidget(self.label1)
# サブレイアウトを作成して追加
sublayout = qtw.QHBoxLayout()
sublayout.addWidget(self.label2)
sublayout.addWidget(self.line_edit)
layout.addLayout(sublayout)
# 他のウィジェットを追加
layout.addWidget(self.button)
layout.addStretch()
# 画面表示
self.show()
def main():
"""メイン関数"""
app = qtw.QApplication(sys.argv)
mv = MainWindow()
sys.exit(app.exec())
if __name__ == "__main__":
main()【表示結果】

【詳細説明】
メインのQVBoxLayoutを作っている部分は上記で説明してきた各レイアウトの使い方と同じなので省略します。
# レイアウトにウィジェットを追加
layout.addWidget(self.label1)
# サブレイアウトを作成して追加
sublayout = qtw.QHBoxLayout()
sublayout.addWidget(self.label2)
sublayout.addWidget(self.line_edit)
layout.addLayout(sublayout)
# 他のウィジェットを追加
layout.addWidget(self.button)
layout.addStretch()レイアウトを組み合わせる場合には、上記のようにQHBoxLayoutのサブレイアウトを作成して、サブレイアウトにaddWidgetメソッドでウィジェットを配置します。
その後、メインレイアウトであるQVBoxLayoutのaddLayoutメソッドでサブレイアウトを配置することでレイアウトを組み合わせることが可能になります。
まとめ
PythonのGUIツールキットであるPyQtで画面要素であるウィジェットをレイアウトする方法について紹介しました。
PyQtで使用できる主なレイアウトとしては以下のようなものがあります。
- QVBoxLayout:垂直方向レイアウト
- QHBoxLayout:水平方向レイアウト
- QGridLayout:グリッド状レイアウト
- QFormLayout:フォーム形式レイアウト
また、レイアウトを組み合わせることも可能なため柔軟に画面構成を作成することが可能になります。是非使い方を覚えて色々な画面レイアウトを作ってみてください。
上記で紹介しているソースコードについてはgithubにて公開しています。参考にしていただければと思います。







