PyQt

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

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

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

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

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のサイズは変わりません。

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

上記のように固定サイズで決めてしまうと、画面の拡大・縮小に対する柔軟性に欠けてしまいます。もう一つの方法として、画面サイズの最小値と最大値を設定する方法があります。以下のように最小値はsetMinimumSizeで、最大値はsetMaximumSizeで(幅, 高さ)を指定します。

        # ウィジェットの用意
        textedit = qtw.QTextEdit(self)
        # ===== 最小・最大を設定
        textedit.setMinimumSize(320, 180)
        textedit.setMaximumSize(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とすると、サイズヒントの画面サイズに固定がされます。以下でいくつか実際の設定例を見てみましょう。

【幅はサイズヒントに固定、高さはデフォルト】

幅はサイズヒントに固定(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ピクセルで固定されますが、高さについては、画面にあわせて拡大・縮小します。

【可能な限り広いスペースを確保するが、サイズヒントより小さくならないようにする】

可能な限り広いスペースを確保するが、サイズヒントより小さくならないようにすることを考えてみます。

        # ウィジェットの用意
        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)

まとめ

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

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

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