at backyard

Color my life with the chaos of trouble.

tkinterのgirdを使ったウィジェットの配置について

あけましておめでとうございます。2022年もよろしくおねがいします。

昨晩は妻からプレゼントしてもらったニューマットレスを敷いて寝たのですが、マットレスが変わるとこうも寝心地が変わるものかと驚いています。 まるでホテルの良質なベッドで寝たかのような寝心地でした。

元旦から良い睡眠させてもらいました。

肩のこりも幾分楽になっているような気がする。

tkinterのgirdを用いたウィジェットの配置について

さて、本題。

tkinterではウィジェットの配置にはpack, grid, placeを用いるが、今回はこのgridについて調べたのでそちらについて書いていく。

tkinterのgridはrow(行番号)とcolumn(列番号)を指定しながらウィジェットを配置する。
イメージ的にはExcelのセルに配置するようなイメージとなる。

例えば下記のようなコードを実行した場合、

import tkinter as tk

class App:

    def __init__(self, root):
        self.root = root
        root.title("grid サンプル")
        root.geometry("600x400")
        tk.Label(root, text="ラベル1").grid(row=0, column=0)
        tk.Label(root, text="ラベル2").grid(row=0, column=1)
        tk.Label(root, text="ラベル3").grid(row=1, column=0)
        tk.Label(root, text="ラベル4").grid(row=2, column=1)


def main():
    root = tk.Tk()
    app = App(root)
    app.root.mainloop()


if __name__ == "__main__":
    main()

下記のようなウィジェットの配置となる。

f:id:shinshin86:20220101071754p:plain

[余談] gridの公式ドキュメントについて

なお、ここでtkinterのgridに関する公式ドキュメントを探してみた。

というのも tkinter grid python などで検索すると公式ドキュメントは検索結果の1ページめには出てこない。
なぜ公式ドキュメントが一枚目に出てこないのか?と思いつつ、公式ページからたどってたどり着いたgridの公式ドキュメントがこちらとなる。

grid manual page - Tk Built-In Commands

なかなか無骨なデザインになっているので、これを読んでいくよりはどこかのブログで分かりやすく解説されている記事でも読んだほうが精神的には楽かもしれない...

ただ、公式ドキュメントであるし、網羅的にはまとまっているので困ったらこちらを見るのはありかもしれない。

gridでExcelのセル結合的なことを実現させる

さきほどgridはExcelのセル的な考えに近いと書いたが、例えばセル結合的なことを行ったウィジェット配置も可能で、その場合には columnspan という属性を渡す。

import tkinter as tk

class App:

    def __init__(self, root):
        self.root = root
        root.title("grid サンプル")
        root.geometry("600x400")
        tk.Label(root, text="ラベル1").grid(row=0, column=0)
        tk.Label(root, text="ラベル2").grid(row=0, column=1)
        tk.Label(root, text="ラベル3").grid(row=1, column=0, columnspan=2) # columnspanを追加
        tk.Label(root, text="ラベル4").grid(row=2, column=1)


def main():
    root = tk.Tk()
    app = App(root)
    app.root.mainloop()


if __name__ == "__main__":
    main()

columnspanはデフォルトでは1が渡されているがこれを2にすることで2つ分のセルを結合したような状態となる。

実行結果は下記の通り。

f:id:shinshin86:20220101072845p:plain

ラベル3の位置が変わっていることが分かる。

同じように rowspan という属性もあり、これは縦方向のセルを結合するために利用される。

gridのオプションについて

  • column - ウィジェットを配置する列番号(0はじまり)
  • row - ウィジェットを配置する行番号(0はじまり)
  • columnspan - 横方向に結合する数(デフォルトは1)
  • rowspan - 縦方向に結合する数(デフォルトは1)

またgridのオプションには隙間(空白)を設定できるものもある。
(webで言うmarginとpadding)

ipadx, ipadyがpadding的なものとなり、padx, padyがmargin的な使い方となる。

実際にipadyでlabelの内側の隙間(padding)を広げたサンプル

import tkinter as tk

class App:

    def __init__(self, root):
        self.root = root
        root.title("grid サンプル")
        root.geometry("600x400")
        tk.Label(root, text="ラベル1", bg="green").grid(row=0, column=0, ipady=50) # 分かりやすく50に設定
        tk.Label(root, text="ラベル2", bg="coral").grid(row=0, column=1)
        tk.Label(root, text="ラベル3", bg="blue").grid(row=1, column=0)
        tk.Label(root, text="ラベル4", bg="red").grid(row=2, column=1)


def main():
    root = tk.Tk()
    app = App(root)
    app.root.mainloop()


if __name__ == "__main__":
    main()

すると下記のようになる。

f:id:shinshin86:20220101074506p:plain

gridのstickyオプションについて

stickyオプションを利用すると、指定した方向に対してグリッド内いっぱいにウィジェットが広がる。

具体的にどのような挙動をするか、まずはサンプルを用意する。

import tkinter as tk

class App:

    def __init__(self, root):
        self.root = root
        root.title("grid サンプル")
        root.geometry("600x400")
        tk.Label(root, text="ラベル1", bg="green").grid(row=0, column=0)
        tk.Label(root, text="ラベル2", bg="coral", width=50).grid(row=0, column=1)
        tk.Label(root, text="ラベル3", bg="blue").grid(row=1, column=0)
        tk.Label(root, text="ラベル4", bg="red").grid(row=2, column=1)


def main():
    root = tk.Tk()
    app = App(root)
    app.root.mainloop()


if __name__ == "__main__":
    main()

このコードを実行すると、下記のようなウィジェット配置となる。

f:id:shinshin86:20220101075048p:plain

次にこのサンプルの ラベル4 にstickyオプションを指定する。
指定内容は左右いっぱいに広げるという内容となる。

import tkinter as tk

class App:

    def __init__(self, root):
        self.root = root
        root.title("grid サンプル")
        root.geometry("600x400")
        tk.Label(root, text="ラベル1", bg="green").grid(row=0, column=0)
        tk.Label(root, text="ラベル2", bg="coral", width=50).grid(row=0, column=1)
        tk.Label(root, text="ラベル3", bg="blue").grid(row=1, column=0)
        tk.Label(root, text="ラベル4", bg="red").grid(row=2, column=1, sticky=tk.EW) # stickyを指定


def main():
    root = tk.Tk()
    app = App(root)
    app.root.mainloop()


if __name__ == "__main__":
    main()

すると、下記の通りの配置となる。

f:id:shinshin86:20220101075138p:plain

本来widthを指定していないラベル4がセルの左右いっぱいに広げられているのはstickyによるものだ。

例えば右に寄せて配置したければ sticky=tk.E と指定すれば良い。

このstickyで方向を指定する際のEとかWとかは東西南北から取られていると思われる。

  • E - 東、つまり右方向
  • W - 西、つまり左方向
  • N - 北、つまり上方向
  • S - 南、つまり下方向

上のサンプルに書いたように tk.EW と組み合わせでも記載できる。