2021-04-06

【備忘録】QGridLayout でレイアウトに悩む (PySide6)

Qt for Python (PySide6) は、Qt 6 の Python バインディングで、Qt 6と同じく、LGPLv3 / GPLv3Qt commercial license(商用ライセンス)が適用されています。

本ブログ記事では下記の OS 環境で動作確認をしています。

Fedora 34 (Workstation Edition Prerelease) x86_64
- python3-3.9.2-1
- venv/pip: PySide6 (6.0.2)

昨年 12 月に Qt 6 がリリースされ、Qt for Python も PySide6 と番号を揃えて利用できるようになりました。Fedora 34(ベータ版)では Qt 6 が利用できるようになっているものの、PySide6 は RPM パッケージとしては利用できるようになっていませんでした。そこで venv で仮想環境を作り pip で PySide6 をインストールして使いはじめました。今のところの使い方では、GUI の見映えに多少の違いが認められるものの、Python スクリプトでは PySide2 と PySide6 で使い方の違いが判らず、同じように記述しています。

手がけている PySide2 プロジェクトすべてを PySide6 へ移行させてはいませんが、本ブログ記事では本記事から PySide6 へ切り替えることにします。

QGridLayout でレイアウトに悩む

グリッド状にウィジェットをレイアウトしたい時に QGridLayout を使いますが、ウィンドウのサイズを変更した時に、グリッドのセルの比率が好ましくない変化をして悩むことがあります。

下記のサンプルフォームで説明します。

qt_layout_study_1_1.py
#!/usr/bin/env python
# coding: utf-8

import sys
from PySide6.QtWidgets import (
    QApplication,
    QGridLayout,
    QLabel,
    QLineEdit,
    QWidget,
)


class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.setWindowTitle('Layout 1.1')

    def initUI(self):
        grid = QGridLayout()
        self.setLayout(grid)

        row = 0
        lab0 = QLabel('個人情報記入欄')
        grid.addWidget(lab0, row, 0, 1, 2)
        row += 1

        lab1 = QLabel('氏名')
        grid.addWidget(lab1, row, 0)
        ent1 = QLineEdit()
        grid.addWidget(ent1, row, 1)
        row += 1

        lab2 = QLabel('住所')
        grid.addWidget(lab2, row, 0)
        ent2 = QLineEdit()
        grid.addWidget(ent2, row, 1)
        row += 1



def main():
    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec())


if __name__ == '__main__':
    main()

実行例を以下に示しました。

 

ウィンドウのサイズを大きくすると、0 行に配置した「個人情報記入欄」と表示するラベル (QLabel) のみ、行の幅(高さ)が変化してしまいます。どうやら QGridLayout は、ベースになるウィジェット、この例では Example クラスが継承している QWidget のウィンドウサイズに対してグリッドをレイアウトするため、ウィンドウが大きくなった分、どこかの行、列(複数の場合もあり)が、ウィンドウのサイズに応じて変化してしまうようです。

自分の持っている知識と経験では QGridLayout をうまく操って、自分が期待するレイアウト、この場合は、どの行の幅(高さ)も変化させないようにする、ということができませんでした。そこで、ベースとした QWidget の高さが変化せずに、幅のみ変化するようにして、それを QScrollArea のウィジェット上に表示するようにしました。つまり Example クラスは QScrollArea を継承するようにして、そこにあらためて横(幅)しか変化しないようにした QWidget と、その上にラベル (QLabel) とエントリ (QLineEdit) で構成したフォームを QGridLayout で配置しました。

改良したサンプルです。

qt_layout_study_1_2.py
#!/usr/bin/env python
# coding: utf-8

import sys
from PySide6.QtWidgets import (
    QApplication,
    QGridLayout,
    QLabel,
    QLineEdit,
    QScrollArea,
    QSizePolicy,
    QWidget,
)


class Example(QScrollArea):
    def __init__(self):
        super().__init__()
        self.setWidgetResizable(True)

        self.initUI()
        self.setWindowTitle('Layout 1.2')

    def initUI(self):
        base = QWidget()
        base.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.setWidget(base)

        grid = QGridLayout()
        base.setLayout(grid)

        row = 0
        lab0 = QLabel('個人情報記入欄')
        grid.addWidget(lab0, row, 0, 1, 2)
        row += 1

        lab1 = QLabel('氏名')
        grid.addWidget(lab1, row, 0)
        ent1 = QLineEdit()
        grid.addWidget(ent1, row, 1)
        row += 1

        lab2 = QLabel('住所')
        grid.addWidget(lab2, row, 0)
        ent2 = QLineEdit()
        grid.addWidget(ent2, row, 1)
        row += 1


def main():
    app = QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec())


if __name__ == '__main__':
    main()

実行例を以下に示しました。

 

これが、自分が期待した、ウィンドウサイズを変更した時のレイアウトです。ちょっと面倒くさいです。すぐ忘れてしまいますので、備忘録にしました。

参考サイト

  1. QGridLayout — Qt for Python
  2. QScrollArea — Qt for Python

 

ブログランキング・にほんブログ村へ bitWalk's - にほんブログ村 にほんブログ村 IT技術ブログ Linuxへ
にほんブログ村

0 件のコメント: