質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

2815閲覧

TkInter: Widgetの配置(GRID)が 思うようにならない

saya24

総合スコア222

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/10/20 13:51

編集2021/10/21 06:40

ちゃんと座標形式で画面設計を前もって行い
それにもとづいてWidget配置(GRID)を行っているのですが 毎回毎回自分が意図したようにならず 本当に困っています。

質問

なぜ意図した表示が行われないのか、どこを変更すれば 設計のように表示されるか? ご教示頂けますでしょうか?
TkInterで画面を作る都度、プログラム以前自分に何かの能力が欠落しているんじゃないかと 悩まされます。

設計書!
下記コードからの実行時画面

1021 1016 提示コードを全体に差替え、下段に実行時のメニュー画像を追加

Python

1# tkinterのインポート 2import tkinter as tk 3import tkinter.ttk as ttk 4import math 5import os 6from tkinter import filedialog 7import tkinter.messagebox as tkmb 8import openpyxl 9################################################################################# 10# # 11# 一般関数 # 12# # 13# # 14################################################################################# 15# フレーム切替え=画面遷移 16def change_frame(frame): 17 frame.tkraise() 18 19 20################################################################################# 21# # 22# 画面生成 # 23# # 24# # 25################################################################################# 26def resource_path(relative_path): 27 try: 28 # PyInstaller creates a temp folder and stores path in _MEIPASS 29 base_path = sys._MEIPASS 30 except Exception: 31 base_path = os.path.abspath(".") 32 33 return os.path.join(base_path, relative_path) 34 35 36def adjust_windowsize(root): 37 ww = root.winfo_screenwidth() 38 wh = root.winfo_screenheight() 39 40 lw = math.ceil(ww * 0.208) 41 lh = math.ceil(wh * 0.277) 42 43 root.geometry(str(lw)+"x"+str(lh)+"+"+str(int(ww/2-lw/2))+"+"+str(int(wh/2-lh/2))) 44################################################################################# 45# # 46# フレーム生成 # 47# # 48# # 49################################################################################# 50def generate_frame(root): 51 ############################################################################# 52 # # 53 # メインメニュー用フレーム設置 # 54 # # 55 ############################################################################# 56 frmMain = ttk.Frame(root) 57 frmMain.grid(row=0, column=0, sticky=tk.E + tk.W + tk.N + tk.S) 58 59 # メインメニューにボタン配置 60 btn_SettingMenu = tk.Button(frmMain, text = "入出力定義", font=("",0,"normal","roman","normal"), command=lambda: change_frame(frmIOMenu)) 61 btn_SettingMenu.pack(fill = tk.BOTH, expand=True) 62 63 btn_SettingMenu = tk.Button(frmMain, text = "変換定義", font=("",0,"normal","roman","normal")) 64 btn_SettingMenu.pack(fill = tk.BOTH, expand=True) 65 66 btn_RunMenu = tk.Button(frmMain, text = "実行メニュー", font=("",0,"normal","roman","normal")) 67 btn_RunMenu.pack(fill = tk.BOTH, expand=True) 68 btn_RunMenu.focus_set() 69 70 btn_SettingMenu = tk.Button(frmMain, text = "終了", font=("",0,"normal","roman","normal"), command=root.destroy) 71 btn_SettingMenu.pack(fill = tk.BOTH, expand=True) 72 ############################################################################# 73 # # 74 # 入出力定義メニュー用フレーム設置 # 75 # # 76 ############################################################################# 77 frmIOMenu = ttk.Frame(root) 78 frmIOMenu.grid(row=0, column=0, sticky=tk.E + tk.W + tk.N + tk.S) 79 80 # 0 81 cmbox_RecNo = ttk.Combobox(frmIOMenu, state="readonly", height=3) 82 cmbox_RecNo.grid(row=0, column=0, columnspan=2, sticky=tk.N + tk.S + tk.E + tk.W) 83 84 # 0 85 ent_RecName = tk.Entry(frmIOMenu) 86 ent_RecName.grid(row=0, column=2, columnspan=8, sticky=tk.E + tk.W + tk.N + tk.S) 87 88 # 3 シート構成・列構成ツリー 89 tree1 = ttk.Treeview(frmIOMenu, show="tree", height=4) 90 tree1.grid(row=3, column=0, rowspan=4, columnspan=5, sticky=tk.E + tk.W + tk.N + tk.S) 91 ###hscrollbar = ttk.Scrollbar(frmIOMenu, orient=tk.VERTICAL) 92 ###hscrollbar.grid(row=3, column=5, rowspan=4, sticky=tk.N + tk.S + tk.E) 93 94 # 3 95 btn_AddColumn = tk.Button(frmIOMenu, text = "→") 96 btn_AddColumn.grid(row=3, column=5, sticky=tk.E + tk.W + tk.N) 97 98 # 3 シート構成・列構成ツリー 99 tree2 = ttk.Treeview(frmIOMenu, show="headings", height=4) 100 tree2["columns"] =(1) 101 tree2.grid(row=3, column=6, rowspan=4, columnspan=4, sticky=tk.E + tk.W + tk.N + tk.S) 102 103 # 1 入力ファイル指定ボタン、選択ファイル格納テキストボックス配置 104 ent_InputPath = tk.Entry(frmIOMenu, state="disabled") 105 ent_InputPath.grid(row=1, column=3, columnspan=7, sticky=tk.E + tk.W + tk.N + tk.S) 106 107 btn_AssignInputFile = tk.Button(frmIOMenu, text = "入力ファイル指定") 108 btn_AssignInputFile.grid(row=1, column=0, columnspan=3, sticky=tk.E + tk.W + tk.N + tk.S) 109 110 # 2 出力ファイル指定ボタン、選択ファイル格納テキストボックス配置 111 ent_OutputPath = tk.Entry(frmIOMenu, state="disabled") 112 ent_OutputPath.grid(row=2, column=3, columnspan=7, sticky=tk.E + tk.W + tk.N + tk.S) 113 114 btn_AssignOutputFile = tk.Button(frmIOMenu, text = "出力ファイル指定") 115 btn_AssignOutputFile.grid(row=2, column=0, columnspan=3, sticky=tk.E + tk.W + tk.N + tk.S) 116 117 # 4 118 btn_Up = tk.Button(frmIOMenu, text = "↑") 119 btn_Up.grid(row=4, column=5, sticky=tk.E + tk.W + tk.N) 120 121 # 5 122 btn_Down = tk.Button(frmIOMenu, text = "↓") 123 btn_Down.grid(row=5, column=5, sticky=tk.E + tk.W + tk.N) 124 125 # 6 126 btn_Down = tk.Button(frmIOMenu, text = "←") 127 btn_Down.grid(row=6, column=5, sticky=tk.E + tk.W + tk.N) 128 129 # 7 130 cmbox_Type = ttk.Combobox(frmIOMenu, state="readonly", height=3) 131 cmbox_Type.grid(row=7, column=0, columnspan=4, sticky=tk.N + tk.S + tk.E + tk.W) 132 133 # 7 134 cmbox_ConvNo = ttk.Combobox(frmIOMenu, state="readonly", height=3) 135 cmbox_ConvNo.grid(row=7, column=4, columnspan=2, sticky=tk.N + tk.S + tk.E + tk.W) 136 137 # 7 138 cmbox_Type = ttk.Combobox(frmIOMenu, state="readonly", height=3) 139 cmbox_Type.grid(row=7, column=0, columnspan=4, sticky=tk.N + tk.S + tk.E + tk.W) 140 141 # 7 142 ent_AsName = tk.Entry(frmIOMenu) 143 ent_AsName.grid(row=7, column=6, columnspan=4, sticky=tk.E + tk.W + tk.N + tk.S) 144 145 # 8 146 btn_Define = tk.Button(frmIOMenu, text = "定義") 147 btn_Define.grid(row=8, column=4, columnspan=2, sticky=tk.N + tk.S + tk.E+ tk.W) 148 149 # 8 150 btn_Delete = tk.Button(frmIOMenu, text = "削除") 151 btn_Delete.grid(row=8, column=6, columnspan=2, sticky=tk.N + tk.S + tk.E + tk.W) 152 153 # 8 154 btn_ReturnMenu = tk.Button(frmIOMenu, text = "閉じる", command=lambda: change_frame(frmMain)) 155 btn_ReturnMenu.grid(row=8, column=8, columnspan=2, sticky=tk.E + tk.W + tk.N + tk.S) 156 157 # 起動時メインのフレームを前面に 158 frmMain.tkraise() 159 160 161 162 163if __name__ == "__main__": 164 # ウインドウの作成 165 root = tk.Tk() 166 167 #フォームサイズを実行端末から導き、ド真中に配置表示 168 adjust_windowsize(root) 169 170 #タイトルを指定 171 root.title("TkInterの勉強") 172 173 #フレーム切替え達成の上で とても重要、ルートのグリッド定義 174 root.grid_rowconfigure(0, weight=1) 175 root.grid_columnconfigure(0, weight=1) 176 177 root.grid_rowconfigure(1, weight=1) 178 root.grid_columnconfigure(1, weight=1) 179 180 root.grid_rowconfigure(2, weight=1) 181 root.grid_columnconfigure(2, weight=1) 182 183 root.grid_rowconfigure(3, weight=1) 184 root.grid_columnconfigure(3, weight=1) 185 186 root.grid_rowconfigure(4, weight=1) 187 root.grid_columnconfigure(4, weight=1) 188 189 root.grid_rowconfigure(5, weight=1) 190 root.grid_columnconfigure(5, weight=1) 191 192 root.grid_rowconfigure(6, weight=1) 193 root.grid_columnconfigure(6, weight=1) 194 195 root.grid_rowconfigure(7, weight=1) 196 root.grid_columnconfigure(7, weight=1) 197 198 root.grid_rowconfigure(8, weight=1) 199 root.grid_columnconfigure(8, weight=1) 200 201 root.grid_columnconfigure(9, weight=1) 202 203 #フォームの最大化、×ボタン操作を無効化 204 root.resizable(0,0) 205 root.protocol('WM_DELETE_WINDOW', (lambda: 'pass')()) 206 207 # カーソル変更 208 root["cursor"] = "hand2" 209 210 generate_frame(root) 211 212 root.mainloop()

イメージ説明

1021 1540 最終的にこうなり、満足!

イメージ説明

python

1frmIOMenu = tk.Frame(root, bg="red") 2 3frmIOMenu.grid_rowconfigure([0, 1, 2, 3, 4, 5, 6, 7, 8], weight=1) 4frmIOMenu.grid_columnconfigure([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], weight=1, minsize=40) 5frmIOMenu.grid(row=0, column=0, sticky=tk.E + tk.W + tk.N + tk.S)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fj68

2021/10/20 22:48

意図しない余白や見切れが発生しているということなので、rootに対してgeometryなどでサイズを指定しているからではと愚推します。 ただ、該当部分がないため判断できません。 質問にはコード全体を掲載していただけると助かります。 よろしくおねがいします。
saya24

2021/10/21 01:22

fj68さん、お時間を頂戴致しまして誠にありがとうございます。 本文に文字数制限があるため、画面遷移用途外の ボタン押下から動作する関数は除去し コード全容を貼り付けさせていただきました。rootの上にフレームを貼り付けておりますが 2種フレームの最上位表示を切替えることで 画面遷移イメージを達成しております 確認を頂けたら幸いです!
fj68

2021/10/21 01:45

編集、ありがとうございます。 adjust_windowsize()関数内の以下について lw = math.ceil(ww * 0.208) lh = math.ceil(wh * 0.277) それぞれ0.208や0.277というマジックナンバーはどのように決められましたか? それぞれwinfo_width()やwinfo_height()ではダメなのでしょうか? 出先でPCがなく検証できないので、頂いたコードを当方でも後ほど試してみます。 よろしくお願いいたします。
guest

回答1

0

ベストアンサー

問題の原因: 伸縮オプションを変更していない、固定幅でのレイアウト組

grid() でレイアウトを組む場合は、親ウィジェットの row/column configure で
各セルの細かい制御・設定が可能です。

root: tk.Tk frmIOMenu: ttk.Frame cmbox_RecNo: ttk.Combobox ent_RecName: tk.Entry ... 各フォーム部品

という構造になっているので、
それぞれの親ウィジェットに当たる root, frmIOMenu で必要なオプションを設定します。

  • root.grid_rowconfigure(...)
  • root.grid_columnconfigure(...)
  • frmIOMenu.grid_rowconfigure(...)
  • frmIOMenu.grid_columnconfigure(...)

レイアウト横幅が見切れる問題については、適切な minsize を指定する事で回避できます。

  • root.minsize(width, height)
  • root.grid_columnconfigure(..., minsize=width)
  • frmIOMenu.grid_columnconfigure(..., minsize=width)

具体的な設定は、目的の挙動により異なりますが

見切れや不要な空白が生まれる原因は、親ウィジェットで指定されたサイズ(固定値)に対して
内部の小ウィジェットの幅が固定幅・サイズが足りない・大きすぎる事によっておこるので、

解決策としては、(A) 小ウィジェットを伸縮可能にする。→ grid_(row|column)configureでweight指定
(B) 固定幅で不要な空白をなくしたい場合は、リサイズ禁止にして minsize を指定、という方針になります。

レイアウトを柔軟にしたいは、極力、固定値のサイズ指定は使わずに、
レイアウトマネージャにサイズ計算を任せた方が良いです。


Tkinterで画面を作る都度、プログラム以前自分に何かの能力が欠落しているんじゃないかと 悩まされます。。

レイアウトでの問題個所の見つけ方・デバッグ方法ですが、

  • ウィジェットの構造を書き出す (親子関係が把握できるように)
  • 親ウィジェットに色を付けてみる。ウィンドウをリサイズして伸縮可能になっているかどうかを確認。

diff

1- frmIOMenu = ttk.Frame(root) 2+ frmIOMenu = tk.Frame(root, bg="red")

※ ttk だと色変更が手間なので、一時的に tk.Frame を利用。デバッグが終われば元に戻します。


質問に掲載のコードだけでは、レイアウトの問題は再現しませんでした。

イメージ説明

python

1 2import tkinter as tk 3from tkinter import ttk 4 5def main(): 6 root = tk.Tk() 7 8 ## XXX: 問題の再現 9 # root.geometry("400x300") 10 11 12 # ここに質問のコードをコピー 13 14 15 root.mainloop() 16 17 18if __name__ == "__main__": 19 main() 20

投稿2021/10/21 02:22

teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

saya24

2021/10/21 02:59 編集

teamiklさん、ご親切ご丁寧な見解をありがとうございます。 興味深いコメントとして「質問に掲載のコードだけでは、レイアウトの問題は再現しませんでした。」の記載がございました。 当初提示したコード=【フレーム部分のみ】(現在本文に貼り付けたコードは全容ですが当初はフレーム部分のみを掲載していました)を、 root.geometry("400x300") の状況下で 採用して実行頂いたところ、ご提示の当方期待のイメージになった!!! ということですか?? となると、fj86さんも言及されているように rootの画面サイズとの関係性が原因なのかもしれませんね で、teamiklさんから A案・B案を試してみれば ということですね
teamikl

2021/10/21 03:39 編集

更新された質問のコードに関して、一点、明らかな誤解があり gridのオプションについて フォーム部品をgridで配置してる対象は frmIOMenu ウィジェットなので ルートのグリッド定義~の対象は root ではなく frmIOMenu の grid_(row|column)configure の設定が必要です。 ---- 見切れ・余白の原因はその通りで、固定値でサイズ指定していることに起因します。 コード上の問題としては、rootのgridオプションで伸縮可能に設定されてますが(※ ここが誤解) フォーム部品に関してはフレームに対してgridのオプション指定が必要です。
saya24

2021/10/21 04:45 編集

>frmIOMenu の grid_(row|column)configure に当方も気が付いて いま全行と全列に対し 上記を施したうえで 実行したのですが 設計書のとおり 0行目一つ目のコンボボックスが2列要して 1行目と2行目一つ目のボタンが3列要している状況を 表現した筈なのに 0~2行目はじめのウィジェットの列幅が同じになってしまっています。teamiklさんの実行状況もそのようですね また7行目はじめのコンッボックスしかり 設計書どおり4列を要す定義をした筈なのに 0行目~2行目の最初のウィジェット同様の列幅で表現されてしまいますよね。 ほんと、これ苦手!!! 一体何が原因でしょうか....
teamikl

2021/10/21 05:31 編集

> cmbox_RecNo = ttk.Combobox(frmIOMenu, state="readonly", height=3) 左上のウィジェットについては、試しにコンボボックスを 他のウィジェットや適当な色のフレームに差し替えて見て下さい。 フレームだと意図通り均等な長さに、 Entry や Button だと 下のボタンより少し短いくらいになります。 恐らく、Comboboxだと column 2 の列の横幅が0 もしくは 1 ドットになっている為、 columnspan=2 と指定したのに columnspan=3 とほぼ同じ幅に見えているようです。 grid の row, column, columnspan の指定には問題はありません。 対策 frmMenuIO.grid_columnconfigure([0, 1, 2], weight=1, minsize=100) column 0~2 に対して均等に最小幅を設定します。
saya24

2021/10/21 06:54

色々とありがとうございます。お陰様で思い通りになりました。 Widget種類ごとに 適用カラム幅(高さ)が違う?!ということですかねぇ... だとした場合、固定幅の升目で画面デザインを前もって対応したところで、たちうちできないような。 今回フレーム上の各列にminsize=40を設定して意図したイメージになりました。 この手段が鉄板だったりすれば 超うれしいですね。今後の画面設計に活かせるノウハウになるので TkInterでのアプリ開発・学習に着手したので、ここ1か月 多方面でお世話になることと思います。 またご支援を頂けたら 非常にありがたいです!
teamikl

2021/10/21 07:54 編集

minsize 指定での対応で良さそうですね。 >Widget種類ごとに 適用カラム幅(高さ)が違う?!ということですかねぇ... もう少し詳細に解説すると grid の row/column 幅は、 ウィンドウ幅やウィジェット幅を元に計算されるのですが、 ウィジェット毎に最小サイズが決まっているので、 今回の場合では、ウィンドウが小さすぎた為 ボタンとコンボボックスがどちらも最小幅となり、 columnspan 2 と 3 の区別がつかない状況になってました。 例えば、weight の指定だけでも、親フレームを伸縮可能にして ウィンドウを大きめにリサイズすれば想定通りの幅になります。 #### # コメント欄の為コードは詰めて書いてます。インデントは不要。 # 実行してウィンドウの横幅を変えて見て下さい import tkinter as tk from tkinter import ttk root = tk.Tk() ttk.Entry(root).grid(row=0, column=0, columnspan=2, sticky=tk.NSEW) ttk.Label(root).grid(row=0, column=2, columnspan=8, sticky=tk.NSEW) ttk.Button(root, text="AAA").grid(row=1, column=0, columnspan=3, sticky=tk.NSEW) ttk.Label(root).grid(row=1, column=3, columnspan=7, sticky=tk.NSEW) root.grid_columnconfigure([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], weight=1) root.mainloop()
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問