事の発端
tkinterでサブウィンドウを作っていたら、サブウィンドウ内のラジオボタンがバグってしまい、表示した瞬間に2つ選択されているような状態になったので、原因を探っていった結果、以下のコードのようにすれば、うまくバグも出なく待ったし、他の問題点も発見できました。
バグった原因
たぶん、親windowのmainloop()と子windowのmainloop()が、Toplevelで親子の関係付けをしていなかったために、意図しない表示になったと思われます。
参考
https://qiita.com/koji-hirono@github/items/df9a40dd3351bfbd0d9a
↑このページのコードを参考にしました。非モーダルでsingletonにするということらしいです。
以下のコードのポイント
親ウィンドウと子ウィンドウをToplevel()で紐付けするのですが、ボタンのcommandでコールバックさせた場合はうまくいくが、そうでない場合は、子ウィンドウが何個でも作成できてしまうという違いを発見しました。
サンプルコード
以下のコードをコピペすればサンプルを実行できます。
追記)バグを見つけたため、this_window_close(self)のメソッドを追加修正しました。
import tkinter
# 子ウィンドウ
class ChildWindow:
def __init__(self, parent):
self.parent = parent
self.window = None
self.radio_1 = None
self.radio_2 = None
self.radio_3 = None
self.var = tkinter.IntVar() # チェック有無変数
def init(self):
if not self.window:
self.window = tkinter.Toplevel(self.parent)
self.var.set(1) # value=0のラジオボタンにチェックを入れる
# ラジオボタン作成
self.radio_1 = tkinter.Radiobutton(self.window, value=0, variable=self.var, text="幅32")
self.radio_1.place(x=10, y=10)
self.radio_2 = tkinter.Radiobutton(self.window, value=1, variable=self.var, text="幅140")
self.radio_2.place(x=60, y=10)
self.radio_3 = tkinter.Radiobutton(self.window, value=2, variable=self.var, text="幅200")
self.radio_3.place(x=110, y=10)
# ×ボタンの処理をカスタマイズする
self.window.protocol('WM_DELETE_WINDOW', self.this_window_close)
self.window.mainloop()
def this_window_close(self):
self.window.withdraw()
# ↓これをしないと、子ウィンドウを閉じた後に、再度開くことが出来なくなる。
self.window = None
return "break"
# 親ウィンドウ
class ParentWindow:
def __init__(self):
self.root = tkinter.Tk()
self.sub_root = ChildWindow(self.root)
self.sub_root_failure = None
self.root.geometry(str(320) + "x" + str(200) + "+" + str(200) + "+" + str(200))
self.btn_parent = tkinter.Button(text="何個も作成できてしまうボタン",
command=self.create_new_window)
self.btn_parent.place(x=10, y=10, width=300, height=25)
self.btn_child = tkinter.Button(text="一つだけしか作成できないボタン", command=self.sub_root.init)
self.btn_child.place(x=10, y=50, width=300, height=25)
self.root.mainloop()
def create_new_window(self):
self.sub_root_failure = ChildWindow(self.root)
self.sub_root_failure.init()
self.sub_root_failure.window.title("沢山")
def main():
ParentWindow()
if __name__ == "__main__":
main()
動作結果
以下のイメージの通り、何個でも子ウィンドウが作成できてしまうボタンと、1つしか子ウィンドウが作れないボタンができます。
感想
自分にはどうしてこのような違いが出るのか、ちょっとわかりませんが、結果として、このような違いが出るので、tkinterで子ウィンドウを表示させる場合は、この問題点に引っかかると思われるので、気をつけて頂きたいです。