5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

tkinterで子ウィンドウを出すときに、複数作成できてしまうので防止する方法

Last updated at Posted at 2020-01-30

事の発端

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つしか子ウィンドウが作れないボタンができます。
スクリーンショット 2020-01-30 18.37.47.png

感想

自分にはどうしてこのような違いが出るのか、ちょっとわかりませんが、結果として、このような違いが出るので、tkinterで子ウィンドウを表示させる場合は、この問題点に引っかかると思われるので、気をつけて頂きたいです。

5
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?