PyQt

【PyQt】ウィジェットのサイズを制御する方法

【PyQt】ウィジェットのサイズを制御する方法

PythonのGUIツールキットであるPyQtで画面要素であるウィジェットのサイズを制御する方法について紹介します。

ウィジェットのサイズを制御する方法

PyQtでは、画面作成のために様々なウィジェットを使用することができます。PyQtには、画面を拡大・縮小した時にウィジェットのサイズがどのように変わるのかを制御する仕組みがあります。

本記事では、以下の簡単なサンプル画面を使ってウィジェットサイズを制御する方法を紹介していきます。サンプルはQTextEditとQPushButtonが垂直に配置されているとてもシンプルな画面です。

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("ウィジェットのサイズを制御する")
        # 画面サイズの設定
        self.resize(640, 360)

        # レイアウトの作成
        layout = qtw.QVBoxLayout()
        self.setLayout(layout)

        # ウィジェットの用意
        textedit = qtw.QTextEdit(self)
        button = qtw.QPushButton("登録", self)

        # レイアウトにウィジェットを設定
        layout.addWidget(textedit)
        layout.addWidget(button)

        # 画面表示
        self.show()


def main():
    """メイン関数"""
    app = qtw.QApplication(sys.argv)
    mv = MainWindow()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()

【表示結果】

PyQt ウィジェットのサイズを制御する sizeHint sizePolicy

この画面で、QTextEditのサイズを変更する例を以降で見ていきます。以降の説明では、上記プログラムの一部変更箇所のみプログラムコードを示しますので、上記プログラムを書き換えつつ動かしてみてもらえるとよいかと思います。

上記サンプルプログラムは以下で紹介しているような内容をベースにしています。興味があれば参考にしてください。

固定サイズを設定する

最もシンプルな方法としてサイズを固定する方法について説明します。サイズを固定する場合には、以下のようにsetFixedSizeで(幅, 高さ)を指定します。

        # ウィジェットの用意
        textedit = qtw.QTextEdit(self)
        # ===== サイズを固定する
        textedit.setFixedSize(320, 180)
        # =====

この場合、画面を大きくしたり小さくしたりしても上記のQTextEditのサイズは変わりません。画面としての柔軟性は欠けますが、固定で出す方が適切な画面であればsetFixedSizeで決めてしまうのもよいでしょう。

最小サイズと最大サイズを設定する

上記で説明したsetFixedSizeで固定サイズに決めてしまうと、画面の拡大・縮小に対する柔軟性に欠けてしまいます。

画面サイズを設定するもう一つの方法として、画面サイズの最小値と最大値を設定する方法があります。この場合、最小値はsetMinimumSize、最大値はsetMaximumSizeで(幅, 高さ)を指定します。

        # ウィジェットの用意
        textedit = qtw.QTextEdit(self)
        # ===== 最小・最大を設定
        textedit.setMinimumSize(320, 180)
        textedit.setMaximumSize(640, 360)
        # =====

上記のようにすると画面を小さくしようとしてもQTextEditは(幅, 高さ)=(320, 180)よりも小さくはなりません。また、画面を大きく広げていくとQTextEditは最大でも(幅, 高さ)=(640, 360)までしか拡大しません。

サイズヒント(sizeHint)とサイズポリシー(sizePolicy)を使用する

基本的な考え方

上記で紹介してきた画面サイズを指定する方法の場合、どれだけのピクセル数が適切かを決めるのに悩む場合が多いかと思います。PyQtではサイズヒント(sizeHint)サイズポリシー(sizePolicy)というものを利用することでサイズに関する制御ができるようになっています。

サイズヒント(sizeHint)は、ウィジェットサイズをプログラム側が提案してくれるもので、sizeHint()メソッドで取得することができます。一方、サイズポリシー(sizePolicy)は、サイズヒントに対してどのようなポリシーで画面サイズを制御するかというものを決めるです。

PyQtでは、画面サイズ変更に関して以下のようなポリシーフラグというものを持っています。

ポリシーフラグ
フラグ説明
GrowFlag必要に応じサイズのヒントを超えて大きくなる可能性がある。
ExpandFlagウィジェットは可能な限り広いスペースをとる。
ShrinkFlagウィジェットは必要に応じてサイズヒントよりも縮小できる。
IgnoreFlagウィジェットのサイズヒントは無視される。ウィジェットは可能な限り多くのスペースをとる。

また、サイズポリシーとしては以下のようなものがあり、上記のポリシーフラグの組み合わせで表現できます。

サイズポリシー
サイズポリシーフラグの組み合わせ説明
Fixed0サイズヒントに固定する。
MinimumGrowFlagサイズヒントより小さくならない。ウィジェットは拡張可能。
MaximumShrinkFlagサイズヒントより大きくならない。必要に応じて縮小可能。
PreferredGrowFlag | ShrinkFlagサイズヒントが適切だが、縮小・拡張できる。デフォルトのポリシー。
ExpandingGrowFlag | ShrinkFlag | ExpandingFlagサイズヒントが適切なサイズだが、縮小・拡張はでき、可能な限り広いスペースをとる。
MinimumExpandingGrowFlag | ExpandingFlagサイズヒントより小さくならない。ウィジェットは拡張でき、可能な限り広いスペースをとる。
IgnoreShrinkFlag | GrowFlag | IgnoreFlagサイズヒントは無視される。ウィジェットは可能な限り多くのスペースを確保する。また必要に応じて縮小可能

例えばFixedというサイズポリシーを設定するとサイズヒントの画面サイズに固定がされるという感じです。バリエーションは色々あるので全て網羅はできませんが、以下でいくつか実際の設定例を見てみましょう。

設定例1:幅はサイズヒントに固定、高さはデフォルト

幅はサイズヒントに固定(Fixed)、高さはデフォルト(Preferred)というようにしたい場合は、以下のようにします。

        # ウィジェットの用意
        textedit = qtw.QTextEdit(self)
        print(textedit.sizeHint())

        # サイズヒントで固定
        textedit.setSizePolicy(
            qtw.QSizePolicy.Policy.Fixed, qtw.QSizePolicy.Policy.Preferred
        )
【実行結果】
PyQt6.QtCore.QSize(256, 192)

サイズヒントをsizeHintメソッドで取得していますが、今回の例だとサイズヒントは(幅, 高さ)=(256, 192)となっています。

サイズポリシーを設定するには対象ウィジェットのsetSizePolicyでポリシーを指定します。ポリシーは、qtw.QSizePolicy.Policyの中に上記表に記載された値(Fixedなど)が定義されています。なお、幅(水平)方向のポリシーと高さ(垂直)方向のポリシーをそれぞれ指定する必要があります。

上記例では、幅(水平)方向はFixed、高さ(垂直)方向はデフォルトのPreferredに設定しています。そのため、画面表示してみると幅についてはサイズヒントで返ってきた192ピクセルで固定されますが、高さについては、画面にあわせて拡大・縮小します。

設定例2:可能な限り広いスペースを確保するがサイズヒントよりは小さくならない

次に、可能な限り広いスペースを確保するがサイズヒントよりは小さくならないようにしたい場合は、以下のようにします。

        # ウィジェットの用意
        textedit = qtw.QTextEdit(self)
        print(textedit.sizeHint())

        # サイズヒントよりも小さくならないが拡張する
        textedit.setSizePolicy(
            qtw.QSizePolicy.Policy.MinimumExpanding,
            qtw.QSizePolicy.Policy.MinimumExpanding,
        )
【実行結果】
PyQt6.QtCore.QSize(256, 192)

この例では、幅と高さ両方にMinimumExpandingのポリシーを設定しています。この場合は、画面を広げるとウィジェットも拡大しますが、小さくしようとしてもサイズヒントの(幅, 高さ)=(256, 192)よりは小さくなりません。

サイズヒントを上書きする方法

場合によってはサイズヒントをプログラム上で上書きしたくなるケースがあるかもしれません。その場合には、以下のように設定することも可能です。

        # ウィジェットの用意
        textedit = qtw.QTextEdit(self)
        print(textedit.sizeHint())
        
        # サイズヒントを上書きする
        textedit.sizeHint = lambda: qtc.QSize(300, 300)
        print(textedit.sizeHint())
【実行結果】
PyQt6.QtCore.QSize(256, 192)
PyQt6.QtCore.QSize(300, 300)

このようにサイズヒントを上書きした場合、setSizePolicyにより設定した場合には上書きされたサイズヒントに従います。

まとめ

PythonのGUIツールキットであるPyQtで画面要素であるウィジェットのサイズを制御する方法について紹介しました。

PyQtでは、固定サイズや最小・最大サイズを指定する基本的な方法の他にも、サイズヒント(sizeHint)サイズポリシー(sizePolicy)という考え方があり、各ウィジェットに設定することで、サイズヒントをもとにそれぞれのポリシーに従ってウィジェットサイズが変わるように設定することができます。

ポリシー設定は実際画面を動かしてみると思った通りに動かないというのが結構あるかと思います。自身の画面で色々と設定を変更しながら適切な挙動をするポリシーをうまく設定してもらうとよいのかなと思います。