はじめに
今回は、tkinterモジュールの.pack()について掘り下げて解説します。
Python+tkinterの導入が済んでおり、基本操作が行える事を前提で進めます。
↓前提知識はこちらをご覧ください↓
.pack()は素早く、最低限のモノを作るのに向いてますが…
細かな配置を調整してUIを作り込むには向いてません。
ちゃんと作り込みたいなら「.place()」の方がおすすめです。
.pack()を使ったウィジェット配置
tkinter.Labelなどで、テキストなどのウィジットを作成。
そして「.pack()」はコレを書くだけで勝手に要素が並びます。
初期設定のままだと、画面中央上部に上から順に要素が並んでいきます。
要素の並び方を変える(side =)
要素の並び方は「side = tkinter.○○」で変えれます。
試しに「LEFT」に設定。
すると、要素の整列先が左側になります。
配置方法は下記の4通りに設定できます。
【.pack()の配置調整】
・上側に配置 → side = tkinter.TOP (もしくは、指定なし)
・下側に配置 → side = tkinter.BOTTOM
・左側に配置 → side = tkinter.LEFT
・右側に配置 → side = tkinter.RIGHT
※上側への配置は、初期状態で「TOP」揃えになります。
なので「TOP」と書かずに、指定なしでも動きます。
また、それぞれの配置は「””」で囲った小文字表記でも表せます。
side = tkinter.LEFTは「side = “left”」と省略可能。
この2つは同じ意味なので、左に文字が2つ並びます。
【.pack()配置の省略表記】
・上側に配置 → side = "top" (もしくは、指定なし)
・下側に配置 → side = "bottom"
・左側に配置 → side = "left"
・右側に配置 → side = "right"
※上側への配置は、初期状態で「TOP」揃えになります。
なので「TOP」と書かずに、指定なしでも動きます。
この記事の執筆段階では “top” のような省略表記に気づいてませんでした。
なので、記事の画像は「side = tkinter.LEFT」の書き方をしてますが…
皆さまは「side = “left”」と書いて大丈夫です。
以上がside = で並べる方法の基本です。
そして、このside =は「複数の要素を並べた際」に別の問題が発生します。
複数要素を並べる際の注意点
処理の順番は、上から下です。
なので下図のようなプログラムだと下記のように動きます。
① → 左側に文字を配置
② → 上側に文字を配置
そして… ②の上側の文字は微妙に中央からズレます。
これは、①番目の処理の後で “残った” スペースを判定して中央を決めてるのが原因です。
そしたら、プログラムの順番を変えます。
① → 上側に文字を配置
② → 左側に文字を配置
…のように設定。
すると、上側に配置したテキストが綺麗に画面中央に行きます。
が… ②の左側に配置したテキストは①の処理で “残った” スペースの中央に配置されます。
なので、②が中央からズレます。
このズレは.pack()の仕様上、仕方ないです。
諦めてください。
なので.pack()は細かなUIの作り込みには向かず…
ちゃんと作り込みたいなら「.place()」の方がおすすめ。
領域を最大まで広げる(expand)
「expand = Ture」を設定すると、ウィジットの占有する領域が限界まで伸びます。
ここでは「①」のTOP配置に「expand = Ture」を設定。
2つ目の設定は「,」で区切る形で書きます。
すると、①の領域が限界まで伸びます。
そして、②の左側に文字が入る処理が入ります。
結果、左下に文字が表示されます。
expand = Trueを使うと…
side = の設定に関係無く、文字が “中央” に配置されます。
そしたら、②の「LEFT」配置にもexpand = Trueを設定。
すると… ②に許された空間内で文字が “中央” に配置されます。
「expand = True」は同じ方向に配置したモノを並べると、また別の挙動になります。
「side = tkinter.TOP」に設定したモノを3つ用意し「expand = True」を設定。
すると3つの要素は、すべて限界まで拡張されます。
→ 3つの要素が “3等分” された場所に配置されます。
TOPとLEFTという、向きが違うモノの場合は先の処理が優先。
TOPとTOPの場合は設定した数で割られた位置に配置。
…という、微妙な挙動の違いがあります。
以上が、expand = を使う事の解説です。
領域内での配置を調整(anchor = )
下記のような「expand = True」を4つ使った処理を作成。
すると、下図のような領域が設定されます。
そして、文字はデフォルトで “中央” に配置されます。
この中央の配置を変えるのが「anchor =」です。
anchor = tkinter.N / S / W / Eのような形で設定。
すると、文字の配置が下記のように変わります。
・anchor = tkinter. N → 領域内の"上"に移動
・anchor = tkinter. S → 領域内の"下"に移動
・anchor = tkinter. W → 領域内の"左"に移動
・anchor = tkinter. E → 領域内の"右"に移動
この文字は “方角” に由来します。
そして、この方角は組み合わせて書く事もできます。
組み合わせの書き方は決まってるので注意。
「.NW」だと動きますが、「.WN」だと動かないです。
それぞれのパターンと意味は下記。
・anchor = tkinter. NW → 領域内の"左上"に移動
・anchor = tkinter. NE → 領域内の"右上"に移動
・anchor = tkinter. SW → 領域内の"左下"に移動
・anchor = tkinter. SE → 領域内の"右下"に移動
また「anchor = tkinter.NW」などは「anchor = “nw”」のような表記も可能です。
方角を「小文字」で書いて “” で囲んで文字型として入力する形です。
これを使うと「tkinter.」の表記を省略できます。
「tkinter.NW」と「”nw”」は同じ意味になります。
【省略表記を使った書き方】
・anchor = "n" → 領域内の"上"に移動
・anchor = "s" → 領域内の"下"に移動
・anchor = "w" → 領域内の"左"に移動
・anchor = "e" → 領域内の"右"に移動
・anchor = "nw" → 領域内の"左上"に移動
・anchor = "ne" → 領域内の"右上"に移動
・anchor = "sw" → 領域内の"左下"に移動
・anchor = "se" → 領域内の"右下"に移動
こちらも、執筆時点で気づいてなかったので
記事の画像は「anchor = tkinter.N」と書いてますが…
皆さまは「anchor = “n”」のような省略表記で大丈夫です。
以上が「anchor = 」の使い方です。
要素の間に余白を作る(padx/y)
まず、普通に要素を配置します。
3つのLabelウィジットを用意。
.pack(side = tkinter.TOP)を設定。
そして、並べます。
並べたら、pady = 10の設定を追加。
すると、テキストの上下に10pxの余白ができます。
設定値は上下合わせて10pxの設定なので…
上と下それぞれで5pxの余白になります。(設定値÷2の値)
次は、全てのウィジェットでsideの設定を「tkinter.LEFT」に設定。
すると、全ての要素が左揃えになります。
padyは縦方向の余白設定です。
なので、左揃えの場合は余白の設定が無しの状態になります。
横方向に余白を作りたい場合は「padx = 」で設定します。
ここでは「40」を設定。
すると、左右に「40px」の余白ができます。
設定値は左右合わせて20pxの設定なので…
上と下それぞれで20pxの余白になります。(設定値÷2の値)
以上が、要素の間に余白を作る(padx/y)設定です。
処理順の調整(after/before)
下図のような処理を作成。
・1~3 → 左側に並べる(余白x=40)
・4 → 上方向に並べる(余白y=20)
すると… 下図のように並びます。
そしたら④の処理に「after = Label_1」を追加。
すると、①の後(after)に実行されるので、処理が2番目になります。
そして、下図のような挙動になります。
after/before処理は “先に” 呼び出した処理じゃないと使えないので注意。
呼び出されてないモノで処理を実行すると、対象が未定義の状態となりエラーが出ます。
【after/beforeが使える条件】
・Label_1は一番上なので、全てに対して使えません(すべて読み込み前)
・Label_2は、Label_1の後なので "Label_1" にだけ使えます
・Label_3は、2と3の後なので、 "2と3"にだけ使えます
・Label_4は一番下にあるので、全てで使えます(すべて読み込み済み)
なので、after/before系の処理は基本…
下に書いた処理の順番を上に移動する際に使います。
beforeも同じような処理です。
事前に設定した処理の “前” に処理を持っていきます。
Label_4を3の前に設定したので…
下図のような処理順になります。
以上が、処理順の調整(after/before)の解説です。
要素の背景を限界まで引き延ばす(fill = )
これ以降の処理を分かりやすくするために、Labelウィジェットに背景色を設定します。
Labelの方に「bg = “lightblue”」などの色設定を打ち込み。
さらに、.packの方で「expand = True」を設定。
すると、文字の背景が青色で表示されます。
すべて左揃えかつ「expand = True」が設定されてるので…
3つの要素の領域は均等に3等分になります。
そして、文字要素の背景は引き伸ばされず、文字の周囲にあるだけです。
そしたら、この3つの要素に「fill = thinter.X / .Y / BOTH」を追加。
もしくは「fill = “x” / “y” / “both”」という形で処理を書き込み。
すると、下記のような処理が発生します。
fill = "x" → 枠の最大横幅まで塗りを伸ばす
fill = "y" → 枠の最大縦幅まで塗りを伸ばす
fill = "both" → 枠の最大値まで塗りを伸ばす(x + y)
そして、下図のように色が塗られます。
以上が、要素の背景を限界まで引き延ばす処理の解説です。(fill = )
領域を指定したpxまで伸ばす(ipadx / y)
領域は「fill」以外の方法でも伸ばすことができます。
その設定が「ipadx」と「ipady」です。
こちらを設定すると、指定した数値のピクセルまで塗りが伸びます。
fillとの併用も可能。
expand = Trueで作った領域を超えた場合は、そこで塗りが止まります。
また、この処理は「Label」以外のモノにも使えます。
ここでは試しに「Button」を設定。
すると、ボタンの大きさが変わります。
このようにして、作りたい大きさのボタンなどを作る流れになります。
以上が、枠を指定したpxまで伸ばす(ipadx / y)の解説です。
文字と背景色の変え方補足(16進数表記)
ちなみに、文字の色は「fg」で変えれます。
あと、色の表記は”#000000″のような形で16進数表記も可能です。
これで、下図のような画面になります。
色の考え方、16進数表記についての詳細はこちらをご覧ください。
親ウィジット指定(in_)
in_を使うと、親ウィジットを指定できます。
親ウィジットの代表的な例は「Frame」です。
まず、こちらを作成。
Frame = 空白のスペースを作る処理。
テキストなしのLabelのようなモノです。
Frameに入れた処理はこちら。
・bg = “#000000″ 背景色設定
・width = 横幅を指定(px)
・height = 縦幅を指定(px)
これで、下図のような “枠” を作ることができます。
そしたら、Label_3の.pack内に「in_ = 親ウィジット名」を設定。
python標準機能の「in」と区別するために、
tkinterでは「in_」と “_” を書く必要があります。
もしくは、Labelの “ウィジット” 側に「ウィジット名」を打ち込み。
すると、Frame_1を親ウィジットとして設定した位置に「Label_3」が配置されます。
位置的には下図の通り。
expand = Ture設定を入れてますが… なぜか微妙に位置がズレてます。
Label_3が消えた影響で、Frame_1の中央から少しズレたと考えられますが…
詳細は不明。
このあたりの、微妙な挙動をコントロールしづらいので.pack()は使いづらいです。
UIを作り込みたいなら「.place()」がおすすめ。
以上が、親ウィジット指定(in_)の使い方です。
親ウィジット内で配置調整(in_ + anchor)
先ほど、in_ = Frame_1でLabel_3の親ウィジットを設定しました。
そのLabel_3に「anchor = “nw”」を追加。
すると… 親ウィジットのフレームサイズの影響は受けません。
Label_3のフレームサイズで処理が作られるようです。
別の言い方をすると「Frame_1」のフレームサイズが
Label_3の背景で置き換わった形になります。
この、子ウィジットによるフレームサイズ変更を無効化する処理を入れます。
Frabe_1の所に.packを追加し「_propagate(False)」と打ち込み。
すると、子ウィジットの「anchor = “nw”」の処理が親ウィジットのFrame_1を参照します。
そして、Flame_1で設定した領域の左上にLabel_3が生成されます。(”nw”の位置)
これらを組み合わせることで、複雑な配置が作れます。
「in_」と「子ウィジット側に書く」の2つの処理でも結果は同じです。
これで、下図のようなUI配置が作れます
そしたら、親ウィジットの形をFlameに「Fill = “both”」などを使って変形。
fill = “both” だと、領域が限界まで伸びます。
そして、伸びた領域に合わせて子ウィジットの配置も変わります。
また、Frame_1には2つの配置処理が入ってます。
なので、これまで紹介してきた配置順による位置ズレが “Flame_1” 内で発生します。
.pack()を使う限り、逃れられない問題。
あとは、ipadx/yなどで、ウィジットの形を調整。
必要であれば、ウィジットを「Button」などに変更。
といったテクニックを使って、UIの形を作る流れになります。
そして.pack()は描画順の位置ズレ調整が難しすぎるので…
細かく作り込むなら「.place()」一択が良いじゃんという所に行きつきます。
以上が、tkinterの.pack()を使った配置の調整方法です。
まとめ
今回は、Pythonのtkinterモジュールにある.pack()を使ったウィジット配置方法を紹介しました。
・.pack()は初心者向けだが、細かい作り込には向かない
・.pack()はコレを書くだけで要素が自動で並ぶのが強み
・試作品段階では、.pack()配置は手軽で重宝する
・.pack()はsideで上下左右に位置調整をできる
・.pack()は配置順の判定があり、微妙にside = で指定した位置がズレることがある
・この位置ズレ問題が起こるので、細かく作り込むのには向かない
・細かく作り込むなら「.place()」のほうがおすすめ
・あとは、必要に応じで目次や「Ctrl+F」キーで記事内検索して使い方を見てください
また、他にもPythonやプログラムについて解説してます。
ぜひ、こちらもご覧ください。
コメント