Tkinterでウィジェットを作成・配置する際の階層構造の構成と、そのために使用するフレーム(Frame)ウィジェットについて解説します。あるべき構造を理解してから作り始めることで効率的な運用が見込めます。
目次
TkinterにおけるWidgetの階層構造
Tkinterではウィジェットは階層構造を構成するように作成します。
root要素にそのまま各ウィジェットを配置していくこともできますが、フレームで各ウィジェットをまとめてグループ化することでプログラムをフレーム単位でモジュール化することができ、フレームを乗せ換えることでアプリケーションの画面を切り替えることもできるようになります。
GUI画面では次のように表示されます。
フレーム(Frame)ウィジェットとは
フレーム自体はほとんど何をするわけでもありませんが、他のウィジェットをまとめて配置するためのコンテナとして働きます。
Frameインスタンスを作成する際は次のようなオプション引数を指定することができます。
属性 | 説明 | 値 |
---|---|---|
bg or background | 背景の色を指定します | red,blue,grayなど |
bd or borderwidth | ボーダーの幅を指定します | ピクセル単位の数値 |
relief | ボーダー部分の浮き彫りを指定します | flat,raised,sunken,groove,ridge |
width | 幅をピクセル単位で指定します | ピクセル単位の数値 |
height | 高さをピクセル単位で指定します | ピクセル単位の数値 |
Frame を作成する際は親ウィジェット(今回はルート要素)を指定します。
import tkinter as tk root = tk.Tk() # ウィンドウの設定 root.title("ウィンドウのタイトル") root.geometry("400x300") # フレームの作成 frame1 = tk.Frame( # frame1インスタンスを作成 root, # root要素であるウィンドウを指定 background="gray", # 背景色を指定 borderwidth=5, # ボーダーの幅を指定 relief="sunken", # ボーダーの浮き彫りを指定 width=400, # 幅をpixel単位で指定 height=300, # 高さをpixel単位で指定 ) frame1.pack() # frame1インスタンスを配置 root.mainloop() # イベントハンドラの呼び出し
上記のようなウィンドウが表示されます。
実際の例としてFrameウィジェットの中にLabel、Entry、Buttonを配置してみましょう。
先ほどのように手続き型で書いていくこともできますが、Tkinterを含めPythonのGUIプログラムは次のようにFrameの導出クラスを定義してオブジェクト指向で書くのが一般的です。
import tkinter as tk # tk.Frameを継承したFrame1クラスを作成 class Frame1(tk.Frame): def __init__(self, master=None): # コンストラクタを定義 super().__init__(master) # 継承元クラス(tk.Frame)のコンストラクタを呼び出し #ウィンドウの設定 master.title("ウィンドウのタイトル") master.geometry("400x300") # 幅と高さを指定 # フレームの設定 self.config(bg="whitesmoke") # 背景色を指定 self.propagate(False) # フレームのpropagate設定 (この設定がTrueだと内側のwidgetに合わせたフレームサイズになる) # 実行内容 self.pack() # フレームを配置 self.create_widget() # 下記で定義しているcreate_widgetメソッドを実行 #create_widgetメソッドを定義 def create_widget(self): # ラベルを作成 self.label1 = tk.Label(self.master, text=u"これはラベルです") self.label1.pack() # エントリーを作成 self.entry1 = tk.Entry(self.master) self.entry1.insert(tk.END, u"これはエントリーです") self.entry1.pack() # ボタンを作成 button1 = tk.Button(self.master, text=u"これはボタンです") button1.pack() if __name__ == "__main__": # このファイルが実行されている場合の処理 root = tk.Tk() # rootインスタンスを生成 f1 = Frame1(master=root) # Frame1クラスからf1インスタンスを生成 f1.mainloop() # f1インスタンスのイベントハンドラを呼び出し
見た目上はラベル、エントリー、ボタンをそのまま配置したのと変わりませんが、このコードではそれらのウィジェットはFrame1という1つのフレームにまとめられているため効率的に管理・運用できます。
フレームを(Frame)入れ子にする
フレームを入れ子にすることで複雑なウィンドウを作ることができます。
次のコードではメインフレームの中にframe1とframe2という2つのフレームを含み、frame1とframe2はそれぞれ4つのラベルウィジェットを含んでいます。
import tkinter as tk # tk.Frameを継承したApplicationクラスを作成 class Application(tk.Frame): def __init__(self, master=None): super().__init__(master, width=200, height=150) # ウィンドウの設定 self.master.title("ウィンドウのタイトル") # 実行内容 self.pack() # メインフレームを配置 self.create_widget() # create_widgetメソッドを実行 #create_widgetメソッドを定義 def create_widget(self): # 1つ目のフレーム frame1 = tk.Frame(self.master, relief=tk.RIDGE, bd=2) # tk.Frameクラスからframe1インスタンスを生成 list1 = [("A", "lightskyblue"), ("B", "khaki"), ("C", "yellowgreen"), ("D", "hotpink")] # list1リストを定義 for text, color in list1: # ループ開始 label=tk.Label(frame1, text=text, bg=color, font=("20")) # labelウィジェットを作成 label.pack(side=tk.LEFT) # labelウィジェットを配置 frame1.place(relx=0.1, rely=0.1) # frame1フレームを配置 # 2つ目のフレーム frame2 = tk.Frame(self.master, relief=tk.RIDGE, bd=2) # tk.Frameクラスからframe2インスタンスを生成 list2 = [("A", "lightskyblue"), ("B", "khaki"), ("C", "yellowgreen"), ("D", "hotpink")] # list2リストを定義 for i, (text, color) in enumerate(list2): # ループ開始 label=tk.Label(frame2, text=text, bg=color, font=("20")) # labelウィジェットを作成 label.grid(row=i//2, column=i%2) # labelウィジェットを配置 frame2.place(relx=0.6, rely=0.5) # frame2フレームを配置 if __name__ == "__main__": root = tk.Tk() app = Application(master=root) app.mainloop()
このようにTkinterの階層構造の構成を理解してFrameウィジェットを使いこなすことで、複雑なウィンドウ構成を実現することができます。