質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

3188閲覧

python tkinter サブウインドウを複数同時に閉じたい

Kazu

総合スコア5

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2022/03/08 23:51

編集2022/03/16 04:09

前提・実現したいこと

python tkinterを利用してメイン画面⇒子画面⇒孫画面と遷移しております。
孫画面に閉じるボタンを作成し孫画面と子画面を同時に閉じたい。
ご教授の程よろしくお願い致します。

発生している問題・エラーメッセージ

孫画面については Toplevel.destroy(self.master) で閉じることは出来ているのですが 子画面は閉じることが出来ません。 print(self) print(self.master) を実施したところ .!toplevel2.!correctwindow .!toplevel と表示されました。

該当のソースコード

python

1#親ウィンドウ生成(メイン画面) 2class Application(tk.Frame): 3 def __init__(self,master): 4 super().__init__(master) 5 self.pack() 6 self.master.title("テスト") 7 self.master.geometry("550x280+500+350") 8 self.mainwidget() 9 self.mainwindow = [] 10 self.subwindow = [] 11 style = ttk.Style() 12 style.theme_use("default") 13 style.map("Treeview") 14 15 #子ウィンドウ生成指示(検索画面生成指示) 16 def search_window(self): 17 self.mainwindow.append(tk.Toplevel(self.master, highlightthickness=5, highlightbackground="white", highlightcolor="red")) 18 self.subwindow.append(SubWindow(self.mainwindow[len(self.mainwindow)-1],len(self.mainwindow))) 19 #子ウィンドウ生成ボタン 20 def mainwidget(self): 21 self.btn13 = ttk.Button(self.master, text="検索", width=10, state="enable", command=self.search_window) 22 self.btn13.place(x=10, y=10) 23 24#子ウィンドウ生成(検索画面) 25class SubWindow(tk.Frame): 26 def __init__(self,master,num): 27 super().__init__(master) 28 self.pack() 29 master.geometry("550x280+500+350") 30 master.title("検索画面") 31 master.grab_set() 32 self.sub_widget() 33 self.subwindow = [] 34 self.CorrectWindow = [] 35 36 def sub_widget(self): 37 self.btn13 = ttk.Button(self.master, text="修正", width=10, state="enable", command=self.rep_window) 38 self.btn13.place(x=10, y=10) 39 40 #修正画面表示 41 def rep_window(self): 42 self.subwindow.append(tk.Toplevel(self.master, highlightthickness=5, highlightbackground="white", highlightcolor="red")) 43 self.CorrectWindow.append(CorrectWindow(self.subwindow[len(self.subwindow)-1],len(self.subwindow))) 44 45 #こちらの記載が有効にならない状況です。 46 win = tk.Toplevel(self.master, ......) 47 win.bind("<<Close>>", lambda e: self.master.destroy()) 48 self.subwindow.append(win) 49 50class CorrectWindow(tk.Frame): 51 def __init__(self,master,num): 52 super().__init__(master) 53 self.pack() 54 master.geometry("550x250+500+350") 55 master.title("修正画面") 56 self.correct_widget() 57 master.grab_set() 58 59 def correct_widget(self): 60 self.correct_btn14 = ttk.Button(self.master, text="キャンセル", width=10, state="enable", command=self.onclick) 61 self.correct_btn14.place(x=10, y=10) 62 63 def onclick(self): 64 self.master.event_generate("<<Close>>") 65 self.correct_btn14.bind("<1>", onclick) 66 self.correct_btn14.pack() 67 print(self.master) 68 69def main(): 70 win = tk.Tk() 71 app = Application(win) 72 app.mainloop() 73 74if __name__ == '__main__': 75 main() 76 77

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

.!toplevel2.!correctwindow
.!toplevel
と表示されました。

以前の回答で指摘した点ですが、
ウィジェットの親子関係が正しく「孫ウィンドウ」になってません。

Toplevel 生成時の第一引数に親ウィンドウを指定する必要があります。
省略時は大元の親ウィンドウとなる為、「子ウィンドウ」は意図通りですが、
「孫ウィンドウ」と思われているウィンドウは、実際は「子ウィンドウ2」になってます。

孫画面に閉じるボタンを作成し孫画面と子画面を同時に閉じたい。

ウィジェットの親子関係の整理が必要です。
親ウィジェットを破棄すると、その親に所属する子ウィジェットは自動的に破棄されるようになってます。
正しく「孫画面」を構成すると、「子画面」を閉じると自動的に「孫画面」も閉じられます。

解決策:

  • Toplevel の第一引数に親ウィンドウを指定する(正しくウィジェットの親子関係を構築)
  • 「孫画面」の閉じるボタンで、「子画面」を閉じる

実装方法は、
クラスの設計上、CorrectWindowクラス内からSubWindow を直接参照するのは好ましくない為、
(理由: SubWindow への依存が出来てしまい、CorrectWindowクラスを単体で使い難くなる)
孫画面を閉じるボタンを押したときに generate_event でイベント生成し、
子画面側で、孫画面のイベント時にウィンドウを閉じるように設定します。

python

1import tkinter as tk 2 3root = tk.Tk() 4win = tk.Toplevel(root) 5 6def onclick(event): 7 # 小ウィンドウ側でイベント生成 8 win.event_generate("<<Close>>") 9button = tk.Button(win, text="Close") 10button.bind("<1>", onclick) 11button.pack() 12 13# 親ウィンドウ側でイベント時のアクションを設定 14win.bind("<<Close>>", lambda e: root.destroy()) 15 16root.mainloop()

投稿2022/03/09 01:37

編集2022/03/09 11:36
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Kazu

2022/03/10 04:01

ご教授ありがとうございます。 根本的に修正が必要な状況ですね。 今現在は ボタンを押して #サブ画面作成指示  def search_window(self): self.mainwindow.append(tkm.Toplevel(highlightthickness=5, highlightbackground="white", highlightcolor="red")) self.subwindow.append(SubWindow(self.mainwindow[len(self.mainwindow)-1],len(self.mainwindow))) を呼び出しておりますが、このような方式は通常だとやはり問題になるのでしょうか?
teamikl

2022/03/10 07:27 編集

冗長ではありますが、問題にはなってないと思います。 tkinter では、ウィンドウは Toplevel で、 その中に配置するウィジェットを纏めるコンテナが Frame ですが、 Frame に対して Window と名前を付けているのが、コードを読む上で誤読を招きやすい、 といった問題です。 質問とはあまり関係ない部分です。上記のコードで重要な点は 子ウィンドウではなく、「孫ウィンドウ」と思われてる方の 「tkm.Toplevel(...) の第一引数に親となるウィンドウを渡す」 (補足: ここで意図する「孫ウィンドウ」の親は「子ウィンドウ」) コードが完全ではないので、 Application を呼び出してる部分のコードが無いと文脈が解りませんが、 self.master を Toplevel の第一引数に与えて見て下さい。 また、正しく親子関係を整理すると、通常はウィジェットをリストに保持する必要はありません。 親ウィンドウへ問い合わせて子ウィンドウを一覧を得ることができます。 print での表記が以下の様に表示されれば意図通りです。 . 親ウィンドウ .!toplevel 子ウィンドウ .!toplevel!toplevel 孫ウィンドウ ウィジェット間の関係を構築できれば、 子ウィンドウを閉じると自動的に孫ウィンドウも閉じられます。 孫ウィンドウの閉じるボタンと 子ウィンドウの破棄(destroy) を結びつける方法は 解答を参考にして下さい。イベントの仕組み (event_generate と bind) を使います。
Kazu

2022/03/11 03:35

いつもありがとうございます。 「tkm.Toplevel(...) の第一引数に親となるウィンドウを渡す」のコード修正行ってみました。 現在、孫を開いた時に print(self) print(self.master) .!toplevel.!toplevel.!correctwindow .!toplevel.!toplevel と表示になれました。 これであれば意図している動作でしょうか? ※理解が乏しくて申し訳ございません。 こちらでよろしい場合ですが 先にお聞きしているbindになるのですが 画面関係があっていると仮定してなのですが def onclick(event): # 子ウィンドウ側でイベント生成 win.event_generate("<<Close>>") button = tk.Button(win, text="Close") button.bind("<1>", onclick) button.pack() は子画面に追記でしょうか? 孫画面に追記となるのでしょうか? 分からないなりに上記コードを孫のボタンに割り付けてみたのですが 上手く動作せずに【pytho Tutle Graphics】と表示がなされてしまいました。 お手間お掛けいたし申し訳ございません。 何卒宜しくお願い致します。
teamikl

2022/03/11 07:45

.!toplevel.!toplevel となっていれば意図通りです。 これで、子ウィンドウに当たる .!toplevel を破棄するとと .!toplevel.* 以下の全ての子ウィジェットも破棄されるようになります。 私の回答では、簡略化して「親」「子」のみとしたので、「子画面」「孫画面」に読み替えて下さい。 孫画面の閉じるボタンを押したときに、event_generate で仮想イベント "<<Close>>" を発生させます。 ここの Close という文字列は自分で自由に決められるので、他と被らなければなんでも構いません。 子画面側で、"<<Close>>" イベントを bind し、 孫画面の閉じるボタンが押されたときの挙動を設定します。 Frame と Toplevel の違いにのみ注意してください。 混同しそうな点、各クラス内で self は Frame になってるので、 self.master がウィンドウに当たる Toplevel です。 孫画面側では self.master.event_generate を使う。
Kazu

2022/03/14 10:06

ご回答ありがとうございます。 やはり中々意図した動作が出来ません・・・ ご教授いただいたbindの適用がうまくいきません。 コードの記載方法が悪いのだと思いますが・・・ やってみただことは 孫画面クラスのボタンに self.correct_btn14 = ttk.Button(self.master, text="キャンセル", width=10, state="enable", command=self.onclick) と記載し、孫画面クラス内に def onclick(self): self.master.event_generate("<<Close>>") self.correct_btn14.bind("<1>", onclick) self.correct_btn14.pack() を作成 子画面クラス内に win.bind("<<Close>>", lambda e: root.destroy()) この内容の winとroot部分を変更して記載しているのですがエラーのままで 実行不可状態です。 記載場所が間違っているのでしょうか? お手間ばかりお掛けし申し訳ございません。
teamikl

2022/03/15 03:28

エラーはどのようなエラーが出ますか? bind対象が違ってるのかもしれません、 printで同じIDになるか確認して見て下さい。 self.master.event_generate("<<Close>>") print(self.master) win.bind("<<Close>>", lambda e: root.destroy()) print(win) "win" が未定義ですが、Toplevel なのか CorretWindow どちらなのかを確認してください。 self.master.event_generate とする場合は、Toplevel がイベントを発します。 追記: 回答内の変数 "win" "root" 等は、サンプル回答のコードなので 質問のコードに適応するには、実際の変数に変更してください。
Kazu

2022/03/16 04:07 編集

teamikl様 いつもご回答ありがとうございます。 コード修正させていただきます。
teamikl

2022/03/15 06:43

実際のエラーを見ないと解らない部分なので、 質問のコードを編集し、実際に動かしたコードを掲載して下さい。 > についてはwinとrootに孫画面のCorretWindowや子画面のSubWindowを入れているのですが有効にならず 文章通りだとすると、<<Close>>イベントを発するのは .!toplevel.!toplevel に当たる Toplevelです CorrectWindow や SubWindow は Frame を継承したクラスなので、bind する対象が違います。
Kazu

2022/03/16 02:08

コード修正させていただきました。 勉強不足で大変申し訳ございません。
teamikl

2022/03/16 02:35

コメント欄だとインデントが有効にならないので、 出来れば質問のコードの編集でお願いします。(今回は問題個所を得られたので大丈夫です) > SubWindow.bind("<<Close>>", lambda e: SubWindow.destroy()) は、Pythonでのクラス・オブジェクトの使用法の誤り & イベント対象はsubwindow ではなく toplevel です。 win = tk.Toplevel(self.master, ...略...) win.bind("<<Close>>", lambda e: self.master.destroy()) self.subwindow.append(win) ==== Pythonでのオブジェクト指向 一般的なメソッドの呼び出しは以下の様に記述します インスタンス = クラス名() インスタンス.メソッド(引数1, 引数2, ...) 間違い: クラス名.メソッド(引数1, 引数2, ...) 稀に使う記述方法: クラス名.メソッド(インスタンス, 引数1, 引数2, ...) SubWindow.bind は クラス名.bind( ... ) という形式で「インスタンス」との結びつきがありません。 メソッド定義の第一引数に selft を必ず渡してると思いますが、self に当たる引数が渡されない状態です。 インスタンス.メソッド() という呼び出しでは、暗黙的に第一引数のself にインスタンスが渡されます。 クラス名.メソッド() での呼び出しでは、明示的に第一引数selfにインスタンスを渡す必要があります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問