タブで 2つの Frame の切り替えができるようにしようとしたら、Frame が2つ縦に並んでしまった件 (とりあえず解決)

タブを 2つ用意し、そのタブ毎にフレームを作成して、タブでフレームを切り替えられるようにしようとしたところ、こんな感じになってしまいました。

f:id:koreyakono:20210210220120p:plain

 

なんというか、Frame がタブに収まってないって感じです。

 

フレーム作成の共通の処理をするために BaseFrame というクラスを作成したのですが、このクラスに問題があるらしく、BaseFrame クラスを経由しなければうまくいく、というのが前回分かりました。

koreyakono.hatenadiary.jp

 

 

さて、問題の BaseFrame クラスですが、以下のようなものです。

class BaseFrame(tk.Frame):
def __init__(self, master):
frame_color = "lightskyblue"

# call constructor
super().__init__(master, relief="groove", bd=6)

self.base_frame = tk.Frame(master,
bg=frame_color,
relief="sunken", bd=5)
self.base_frame.grid()

# put label
self.r0c0_label = tk.Label(self.base_frame, font=("system"),
text="path: ", anchor="w", padx=1, width=10)
self.r0c0_label.grid(row=0, column=0, sticky="w")

# put entry
self.r0c0_entry = tk.Entry(self.base_frame, width=50, bd=2, font=("system"))
self.r0c0_entry.grid(row=0, column=0, columnspan=2, padx="60", sticky="w")

self.r1c0_button = tk.Button(self.base_frame, text="put button",
command=lambda obj=self.r0c0_entry:
run_script(obj)
)
self.r1c0_button.grid(row=1, column=0, pady=5, sticky="w")
 

 

Python のマニュアルを改めてみてみると、Frame クラスを継承したクラスのサンプルは以下のように記載されています。

class App(tk.Frame):
    def __init__(self, master):
        super().__init__(master)
        self.pack()

tkinter --- Tcl/Tk の Python インタフェース — Python 3.9.1 ドキュメント

 

 私との違いは、tk.Frame の生成のところです。Python のマニュアルは、tk.Frame  の __init__ を実行後、すぐ、self.pack() をしています。App は、tk.Frame を継承しているので、self 自体が tk.Frame なんですね。だから、self.pack() でよいのだということなのでしょう。

 

私の場合、BaseFrame(tk.Frame) の中で、さらに tk.Frame をインスタンス化していたので、この部分が冗長でした。そこで、BaseFrame(tk.Frame) の BaseFrame.base_frame 記述を self に置き換えることにしました。

 

ここの部分です。

self.base_frame = tk.Frame(master,
bg=frame_color,
relief="sunken", bd=5)
self.base_frame.grid()

 

以下のように直しました。

super().__init__(master, relief="groove", bg=frame_color, bd=5)

# 修正: tk.Frame を呼ぶ必要がない。なぜなら、tk.Frame を継承しているから。
# self.base_frame = tk.Frame(master,
# bg=frame_color,
# relief="sunken", bd=5)

# 自分自身を grid する
# self.base_frame.grid()
self.grid()

 

 

その結果、

  

 

うまく動作しました。

f:id:koreyakono:20210211103652p:plain

 

・修正後のコード

import tkinter as tk
import tkinter.ttk as ttk


class BaseFrame(tk.Frame):
def __init__(self, master, button_text=None):
frame_color = "lightskyblue"

# call constructor
super().__init__(master, relief="groove", bg=frame_color, bd=5)

# 修正: tk.Frame を呼ぶ必要がない。なぜなら、tk.Frame を継承しているから。
# self.base_frame = tk.Frame(master,
# bg=frame_color,
# relief="sunken", bd=5)

# 自分自身を grid する
# self.base_frame.grid()
self.grid()

# 以下、self.base_frame parent にしていた部分を self へ置き換える

# put label
self.r0c0_label = tk.Label(self, font=("system"),
text="path: ", anchor="w", padx=1, width=10)
self.r0c0_label.grid(row=0, column=0, sticky="w")

# put entry
self.r0c0_entry = tk.Entry(self, width=50, bd=2, font=("system"))
self.r0c0_entry.grid(row=0, column=0, columnspan=2, padx="60", sticky="w")

self.r1c0_button = tk.Button(self, text=button_text,
command=lambda obj=self.r0c0_entry:
run_script(obj)
)
self.r1c0_button.grid(row=1, column=0, pady=5, sticky="w")


def run_script(entry=None):
url = entry.get()
print(url)


class MainWindow:
def __init__(self):
self.root = tk.Tk()
self.root.title("GUI")
self.root.attributes("-topmost", True)

# -----------------------------------
# Notebook を使ってタブを設定してみる
# -----------------------------------
notebook = ttk.Notebook(self.root)
notebook.grid(sticky="w")

# tab に表示する frame1 frame2 を生成する。
# タブの切り替えが分かるように button のテキストを変えられるようにする
self.frame1 = BaseFrame(master=self.root, button_text="button1")
self.frame2 = BaseFrame(master=self.root, button_text="button2")

# notebook に追加する
notebook.add(self.frame1, text="tab1")
notebook.add(self.frame2, text="tab2")

# 変更はここまで ------------------------

self.root.mainloop()


# start program
MainWindow()

if __name__ == '__main__':
pass

 

  

ちなみに、逆に  BaseFrame を呼び出す側で、notebook.add() に BaseFrame.base_frame を指定すればいいんじゃない ?  というのも思ったので、やってみると....

  

できました !     

f:id:koreyakono:20210211103335p:plain

  

・コード

import tkinter as tk
import tkinter.ttk as ttk


class BaseFrame(tk.Frame):
def __init__(self, master, button_text):
frame_color = "lightskyblue"

# call constructor
super().__init__(master, relief="groove", bd=6)

self.base_frame = tk.Frame(master,
bg=frame_color,
relief="sunken", bd=5)
self.base_frame.grid()

# put label
self.r0c0_label = tk.Label(self.base_frame, font=("system"),
text="path: ", anchor="w", padx=1, width=10)
self.r0c0_label.grid(row=0, column=0, sticky="w")

# put entry
self.r0c0_entry = tk.Entry(self.base_frame, width=50, bd=2, font=("system"))
self.r0c0_entry.grid(row=0, column=0, columnspan=2, padx="60", sticky="w")

self.r1c0_button = tk.Button(self.base_frame, text=button_text,
command=lambda obj=self.r0c0_entry:
run_script(obj)
)
self.r1c0_button.grid(row=1, column=0, pady=5, sticky="w")


def run_script(entry=None):
url = entry.get()
print(url)


class MainWindow:
def __init__(self):
self.root = tk.Tk()
self.root.title("GUI")
self.root.attributes("-topmost", True)

# -----------------------------------
# Notebook を使ってタブを設定してみる
# -----------------------------------
notebook = ttk.Notebook(self.root)
notebook.grid(sticky="w")

# tab に表示する frame1 frame2 を生成する
self.frame1 = BaseFrame(master=self.root, button_text="b1")
self.frame2 = BaseFrame(master=self.root, button_text="b2")


# notebook に追加する
# 変更: 追加する対象を BaseFrame.base_frame にする
notebook.add(self.frame1.base_frame, text="tab1")
notebook.add(self.frame2.base_frame, text="tab2")

# 変更はここまで ------------------------

self.root.mainloop()


# start program
MainWindow()

if __name__ == '__main__':
pass

でも、こっちは階層が深くなって分かりにくい感じがします。

 

 

今日はここまでです。