89103


tkinterを用いて、画面遷移を含むプログラムを開発しております。1画面目に表示されるボタンがクリックされたら、1画面目が消え、2画面目が表示されるといったプログラムを作りたいです。実行してみたところ、動作は想定通りなのですが、エラーメッセージが表示されてしまいます。どなたか、この理由を教えていただけますでしょうか。

#### 想定する動作

  1. 1画面目が表示される
  2. ボタンを押すと、1画面目が消え、2画面目が表示される

#### 実際の動作

想定通りの動作をしました。

**エラーメッセージ**

[code]

    invalid command name ".!button"
        while executing
    "$w cget -state"
        (procedure "tk::ButtonDown" line 4)
        invoked from within
    "tk::ButtonDown .!button"
        (command bound to event)
    
[/code]

### やってみたこと

ネットで調べてみたところ、quitとdestoroyが存在することを知りました。  
おそらく、何らかの理由でdestoroyの後にボタンクリック判定が来て、存在しないボタンを参照し、エラーが発生しているものと思っております。  
destoyをquitに変更してみましたが、1画面目のボタンが2画面目にも表示されてしまいます。

**コード**

[code]

    import tkinter as tk
    
    
    # 1画面目のルート設定
    root = tk.Tk()
    root_width = round(root.winfo_screenwidth()/2)
    root_height = round(root.winfo_screenheight()/2.5)
    root.geometry(f"{root_width}x{root_height}")
    
    # 1画面目のボタン設定
    button = tk.Button(root, text="ボタン", font=(
        "MSゴシック", "40"), width=5, height=2)
    button.pack()
    
    
    def dest(event):
        root.destroy()
    
    
    # 押された時に画面を削除
    button.bind("<1>", dest)
    
    # 1画面目を表示する
    root.mainloop()
    
    # 2画面目の設定
    root = tk.Tk()
    root.geometry(f"{root_width}x{root_height}")
    label1 = tk.Label(text="2画面目です。")
    label1.pack()
    
    # 2画面目の表示スタート
    root.mainloop()
    
[/code]

**環境**

Python 3.7.1  
macOS


CC BY-SA 4.0
2022-05-28T06:30:55.270
89101
2022-05-29T16:50:41.163
2022-05-29T16:50:41.163
3060
51915
question
python tkinter gui
tkinterを用いた画面遷移においてエラーが発生してしまう


コールバック関数内での `root.destroy()` の実行は無効の様ですので、`root.quit()` でアプリケーションループから抜けた後で
`root.destroy()` を実行してみて下さい。

[code]

    def dest(event):
        root.quit()
    
    # 押された時に画面を削除
    button.bind("<1>", dest)
    
    # 1画面目を表示する
    root.mainloop()
    root.destroy()
    
[/code]


CC BY-SA 4.0
2022-05-28T07:14:11.243
89103
2022-05-28T07:14:11.243
47127
89101
answer


もう一つの方法として：

理由は分かりませんが、以下の記事を基に推測してみると、「root.destroy()を実行する呼び出し可能オブジェクトを作ってバインドしていること(@metropolisさん回答のコールバック関数内での
root.destroy() の実行でしょう)」と「スクリプトを終了せずに再度tkinterを使っていること」の組み合わせでは無いかと思われます。  
[ボタンを使って Tkinter ウィンドウを閉じる](https://www.delftstack.com/ja/howto/python-
tkinter/how-to-close-a-tkinter-window-with-a-button/)

上記記事の例では最後以外はいずれもroot.destroy()を呼び出していますが正常終了しています。  
ただし1つのmainloopを終了したらスクリプトは終了しているので、それによって問題が無いのだと思われます。

試しに、上記記事中の呼び出し可能オブジェクトを作成せず、Buttonの`command`パラメータに直接`root.destroy`を指定する方法ならば問題無く動作するようです。  
[root.destroy 関数をボタンの command
属性に直接関連付ける](https://www.delftstack.com/ja/howto/python-tkinter/how-to-close-a-
tkinter-window-with-a-button/#root-
destroy-%E9%96%A2%E6%95%B0%E3%82%92%E3%83%9C%E3%82%BF%E3%83%B3%E3%81%AE-
command-%E5%B1%9E%E6%80%A7%E3%81%AB%E7%9B%B4%E6%8E%A5%E9%96%A2%E9%80%A3%E4%BB%98%E3%81%91%E3%82%8B)

この行を：

[code]

    # 1画面目のボタン設定
    button = tk.Button(root, text="ボタン", font=(
        "MSゴシック", "40"), width=5, height=2)
    
[/code]

`, command=root.destroy`を追加してこちらに変更する。

[code]

    # 1画面目のボタン設定
    button = tk.Button(root, text="ボタン", font=(
        "MSゴシック", "40"), width=5, height=2, command=root.destroy)
    
[/code]

こちらは削除するかコメントアウトしておく。  
`button.bind("<1>", dest)`の行だけコメントアウトしても良い。

[code]

    def dest(event):
        root.destroy()
    
    # 押された時に画面を削除
    button.bind("<1>", dest)
    
[/code]


CC BY-SA 4.0
2022-05-28T07:40:41.717
89104
2022-05-28T07:40:41.717
26370
89101
answer