2017-04-19 13 views
0

私は幾分大きなPython 3.6プロジェクト用のtkinterウィンドウ管理をテストしていますが、私が正しくやっているとは思えない、あるいはかなりよく理解できるものがあります。次のコードでは、ウィンドウが開いて閉じられます(赤い 'x'ボタンをクリックするか、OS XのCommandキーを押しながら)。しかし、セカンダリウィンドウクロージングイベントのコールバックを広告しようとすると、状況が面倒になります。たとえば、複数のセカンダリウィンドウがある場合、キーボードショートカットやボタンがアクティブなウィンドウを閉じるとは限りません。何がここで間違っているかについての任意のアイデア?なぜ "wm_protocol"はPython3/tkinterの通常のウィンドウ管理を破りますか?

#!/usr/bin/env python3.6 
# encoding: utf-8 

import tkinter as tk 
import tkinter.font 
from tkinter import ttk 


class baseApp(ttk.Frame): 
    """ 
    Parent classe for main app window (will include some aditional methods and properties). 
    """ 
    def __init__(self, master, *args, **kwargs): 
     super().__init__(master, *args, **kwargs) 
     self.master = master 
     self.mainframe = ttk.Frame(master) 
     self.mainframe.pack() 


class App(baseApp): 
    """ Base class for the main application window """ 
    def __init__(self, master, *args, **kwargs): 
     super().__init__(master, *args, **kwargs) 
     self.master = master 
     self.lbl_text = ttk.Label(self.mainframe, text="This is the Main Window") 
     self.lbl_text.pack() 
     self.btn = ttk.Button(self.mainframe, text="Open Second window", 
           command=lambda: self.create_detail_window(self, number=0)) 
     self.btn.pack() 

    def create_detail_window(self, *event, number=None): 
     self.newDetailsWindow = tk.Toplevel(self.master) 
     self.newDetailsWindow.geometry('900x600+80+130') 
     self.newDetailsWindow.title(f'Detail: {number}') 
     self.newDetailsWindow.wm_protocol("WM_DELETE_WINDOW", lambda: self.close_detail_window()) # This line breaks window management!... 
     self.detail_window = detailWindow(self.newDetailsWindow, 0) 
     self.newDetailsWindow.focus() 

    def close_detail_window(self, *event): 
     """ will test for some condition before closing, save if necessary and 
      then call destroy() 
     """ 
     self.newDetailsWindow.destroy() # Shouldn't this be enough to close the secondary window?... 


class detailWindow(ttk.Frame): 
    """ Base class for secondary windows """ 
    def __init__(self, master, rep_num, *args,**kwargs): 
     super().__init__(master,*args,**kwargs) 
     self.num_rep = rep_num 
     self.master.minsize(900, 600) 
     self.master.maxsize(900, 600) 
     print(f"Showing details about nr. {self.num_rep}") 
     self.mainframe = ttk.Frame(master) 
     self.mainframe.pack() 

     self.lbl_text = ttk.Label(self.mainframe, 
            text=f"Showing details about nr. {self.num_rep}") 
     self.lbl_text.pack() 


if __name__ == "__main__": 
    root = tk.Tk() 
    janela_principal = App(root) 
    root.title('Main Window') 
    root.bind_all("<Mod2-q>", exit) 
    root.mainloop() 

私がラインをデコメント時にウィンドウ管理が壊れますself.newDetailsWindow.wm_protocol("WM_DELETE_WINDOW", lambda: self.close_detail_window())ようだ:

は、ここに私の現在のテストコードです。行self.newDetailsWindow.destroy()は、二次ウィンドウを閉じるだけで十分ではありませんか?私はオブジェクトをインスタンス化している方法で間違ったことをしていますか?

+0

あなたは問題を詳しく説明できますか? Windowsコンピュータでエラーを再現することはできません。 – Dashadower

+0

私は現在、このコードを実行してボタンを2回以上クリックして2つ以上のセカンダリウィンドウを開くと、バックグラウンド内のウィンドウの1つをクリックすると、私は現在使用しています(つまり、ウィンドウ)、キーボードショートカットが期待どおりに機能しません。私は別のウィンドウ(最後に開いたウィンドウ)を閉じる。その後、私はアプリケーション全体を終了しない限り、最初の二次ウィンドウは閉じません。 –

答えて

1

コードをいくつか調整しました。今はうまくいくはずです。基本的に、あなたのメソッドapp.create_detail_windowは、あなたがそれを呼び出すたびに属性self.newDetailWindowを再割り当てしたので、「x」ボタンが間違ったウィンドウに送られます。私はあなたの場合には、コメントで述べたように、すべてのToplevel sのあなたは

#!/usr/bin/env python3.6 
# encoding: utf-8 

import tkinter as tk 
import tkinter.font 
from tkinter import ttk 


class baseApp(ttk.Frame): 
    """ 
    Parent classe for main app window (will include some aditional methods and properties). 
    """ 
    def __init__(self, master, *args, **kwargs): 
     super().__init__(master, *args, **kwargs) 
     self.master = master 
     self.mainframe = ttk.Frame(master) 
     self.mainframe.pack() 


class App(baseApp): 
    """ Base class for the main application window """ 
    def __init__(self, master, *args, **kwargs): 
     super().__init__(master, *args, **kwargs) 
     self.master = master 
     self.lbl_text = ttk.Label(self.mainframe, text="This is the Main Window") 
     self.lbl_text.pack() 
     self.btn = ttk.Button(self.mainframe, text="Open Second window", 
           command=lambda: self.create_detail_window(self, number=0)) 
     self.btn.pack() 
     self.newDetailsWindow = {} 
     self.windows_count=0 

    def create_detail_window(self, *event, number=None): 
     self.windows_count+=1 
     self.newDetailsWindow[self.windows_count]=tk.Toplevel(self.master) 
     self.newDetailsWindow[self.windows_count].geometry('900x600+80+130') 
     self.newDetailsWindow[self.windows_count].title(f'Detail: {self.windows_count}') 

     self.newDetailsWindow[self.windows_count].wm_protocol("WM_DELETE_WINDOW", self.newDetailsWindow[self.windows_count].destroy) 
     #self.newDetailsWindow[self.windows_count].bind("Command-w", lambda event: self.newDetailsWindow[-1].destroy()) 

     self.detail_window = detailWindow(self.newDetailsWindow[self.windows_count], self.windows_count) 
     self.newDetailsWindow[self.windows_count].focus() 
     print(self.newDetailsWindow) 

    def close_detail_window(self, *event): 
     """ will test for some condition before closing, save if necessary and 
      then call destroy() 
     """ 
     pass 
     #self.newDetailsWindow.destroy() # Shouldn't this be enough to close the secondary window?... 


class detailWindow(ttk.Frame): 
    """ Base class for secondary windows """ 
    def __init__(self, master, rep_num, *args,**kwargs): 
     super().__init__(master,*args,**kwargs) 
     self.num_rep = rep_num 
     self.master.minsize(900, 600) 
     self.master.maxsize(900, 600) 
     print(f"Showing details about nr. {self.num_rep}") 
     self.mainframe = ttk.Frame(master) 
     self.mainframe.pack() 

     self.lbl_text = ttk.Label(self.mainframe, 
            text=f"Showing details about nr. {self.num_rep}") 
     self.lbl_text.pack() 


if __name__ == "__main__": 
    root = tk.Tk() 
    janela_principal = App(root) 
    root.title('Main Window') 
    root.bind_all("<Mod2-q>", exit) 
    root.mainloop() 
+0

これで問題は解決します。私は本当にこれがどのように働くか詳しく調べる必要があります:) –

+0

あなたの答えでは、closeイベントコールバックは破棄されました。しかし、正しく理解すれば、 'self.newDetailsWindow [self.windows_count] .destroy 'ステートメントをそのメソッドに追加し(もちろんカッコを追加する)、' wm_protocol'呼び出しを私のメソッドの呼び出しに置き換えますか? –

+0

また、辞書は大きくなっているようですが、その項目は削除されません。たぶん私たちは@Dashadowerのようにして、ウィンドウの閉じるメソッドで参照を削除し、ウィンドウの数を調整するようにしてください。あるいは、 'destroy()'メソッドは辞書自体のクリーンアップを保証しますか? –

1

新しいトップレベルのガベージに既存のトップレベルを収集するためにself.newDetailsWindowを使用するように見えます。私は、タプルベルのリストであるAppにリストクラス変数を追加しました。

class App(baseApp): 
    """ Base class for the main application window """ 
    def __init__(self, master, *args, **kwargs): 
     super().__init__(master, *args, **kwargs) 
     self.master = master 
     self.lbl_text = ttk.Label(self.mainframe, text="This is the Main Window") 
     self.lbl_text.pack() 
     self.btn = ttk.Button(self.mainframe, text="Open Second window", 
          command=lambda: self.create_detail_window(self, number=0)) 
     self.btn.pack() 
     self.windows = [] #This is a list of the created windows instances. 

    def create_detail_window(self, *event, number=None): 
     newDetailsWindow = tk.Toplevel(self.master) 
     self.windows.append(newDetailsWindow) 

     newDetailsWindow.geometry('900x600+80+130') 
     newDetailsWindow.title(f'Detail: {number}') 
     newDetailsWindow.wm_protocol("WM_DELETE_WINDOW", lambda: 
     self.close_detail_window(newDetailsWindow)) # This line breaks window management!... 
     detail_window = detailWindow(newDetailsWindow, 0) 
     newDetailsWindow.focus() 


    def close_detail_window(self, window): 
     """ will test for some condition before closing, save if necessary and 
     then call destroy() 
     """ 
     self.windows.remove(window) 
     window.destroy() # destroy the specific instance in self.windows 
+0

問題を解決します。私は本当にこれがどのようにうまくいくか見える必要があります:) –

0

を作成し保存するためにdictを使用し、次のコードはまだ動作します。

import tkinter as tk 
import tkinter.font 
from tkinter import ttk 


class baseApp(ttk.Frame): 
    """ 
    Parent classe for main app window (will include some aditional methods and properties). 
    """ 
    def __init__(self, master, *args, **kwargs): 
     super().__init__(master, *args, **kwargs) 
     self.master = master 
     self.mainframe = ttk.Frame(master) 
     self.mainframe.pack() 


class App(baseApp): 
    """ Base class for the main application window """ 
    def __init__(self, master, *args, **kwargs): 
     super().__init__(master, *args, **kwargs) 
     self.master = master 
     self.lbl_text = ttk.Label(self.mainframe, text="This is the Main Window") 
     self.lbl_text.pack() 
     self.btn = ttk.Button(self.mainframe, text="Open Second window", 
           command=lambda: self.create_detail_window(self)) 
     self.btn.pack() 
     self.windows_count=0 

    def create_detail_window(self, *event): 
     self.windows_count+=1 
     newDetailsWindow=tk.Toplevel() 
     newDetailsWindow.geometry('900x600+80+130') 
     newDetailsWindow.title(f'Detail: {self.windows_count}') 
     newDetailsWindow.wm_protocol("WM_DELETE_WINDOW", newDetailsWindow.destroy) 
     self.detail_window = detailWindow(newDetailsWindow, self.windows_count) 
     newDetailsWindow.focus() 


class detailWindow(ttk.Frame): 
    """ Base class for secondary windows """ 
    def __init__(self, master, rep_num, *args,**kwargs): 
     super().__init__(master,*args,**kwargs) 
     self.num_rep = rep_num 
     self.master.minsize(900, 600) 
     self.master.maxsize(900, 600) 
     print(f"Showing details about nr. {self.num_rep}") 
     self.mainframe = ttk.Frame(master) 
     self.mainframe.pack() 

     self.lbl_text = ttk.Label(self.mainframe, 
            text=f"Showing details about nr. {self.num_rep}") 
     self.lbl_text.pack() 


if __name__ == "__main__": 
    root = tk.Tk() 
    janela_principal = App(root) 
    root.title('Main Window') 
    root.bind_all("<Mod2-q>", exit) 
    root.mainloop() 
関連する問題