使用 tkinter Toplevel parent/child windows 时如何避免破坏数据?

How to avoid destroying data when using tkinter Toplevel parent/child windows?

如何在parent和childwindows之间传递数据保留data/unique ID?

如果我使用 destroy() 方法关闭 child window,关联的值也会被破坏,即使我发送值的字典是用 parent。在销毁 child window 后点击 Get child1config 按钮给出错误:

_tkinter.TclError: invalid command name ".!child1.!entry"

所以,我不破坏。很多childwindows建议退出去图标化吗?

如何从 parent window 引用 child window(和相关值)?我做得对吗?

import tkinter as tk

class parent(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)

        self.dic={}
        self.dic["var"]=['default']

        self.title("Parent")
        self.button1=tk.Button(self,text="open child1", command = self.open_child1, width=20)
        self.button1.grid(row=0,column=0, padx=5, pady=5)
        self.button2=tk.Button(self,text="Get child1 config", command = self.get_child1_value, width=20)
        self.button2.grid(row=0,column=1, padx=5, pady=5)
        self.label1 = tk.Label(self, text="", width=10)
        self.label1.grid(row=0,column=2, sticky='ew')

        self.child1_from_parent=child1(self)
        self.child1_from_parent.withdraw()

    def open_child1(self):
        self.child1_from_parent.deiconify()

    def get_child1_value(self):
        self.label1.config(text=(self.dic["var"][0]+' \n'+self.child1_from_parent.child1_entry.get()))

class child1(tk.Toplevel):
    def __init__(self,master):
        tk.Toplevel.__init__(self, master)
        self.frame  = tk.Frame(self)

        self.title("Child")
        self.label1 = tk.Label(self, text="Config 1", width=10)
        self.label1.grid(row=0,column=0)
        self.child1_entry = tk.Entry(self, width=10)
        self.child1_entry.grid(row=0, column=1, padx=5, pady=5)
        self.child1_entry.insert ( tk.END, self.master.dic['var'][0])
        self.child1_entry.bind('<Return>', self.update_value)
        self.button4=tk.Button(self,text="Close", command = self.close_child1, width=20)
        self.button4.grid(row=0,column=2, padx=5, pady=5)
        self.button5=tk.Button(self,text="destroy", command = self.destroy_child1, width=20)
        self.button5.grid(row=0,column=3, padx=5, pady=5)

    def update_value(self, event):
        self.master.dic["var"][0]=self.master.child1_from_parent.child1_entry.get()

    def close_child1(self):
        self.withdraw()

    def destroy_child1(self):
        self.destroy()

def main():
    parent().mainloop()

if __name__ == '__main__':
    main()

我的程序会增长,所以我正在寻找可扩展性。 类 'seems' 喜欢一个好主意。我有一个 parent tkinter window 运行 实时数据,我会 open/navigate 到不同的 child windows 来执行不同的功能 主 window 是 运行,可访问,并从 child windows.

接收数据

如果我正确理解你的问题,那么这段代码可能会有所帮助。

这是一个 parent 多人实施的最小演示。

每个 child 的所有数据和微更改在关闭 child 后保留。

键盘 short-cuts 允许访问所有 children 和 parent

Parent 可访问,而 child 处于活动状态。

真正的退出是通过消息框的门卫对话。

创建 children 很容易而且是开放式的。


import tkinter as tk
from tkinter.messagebox import askyesno

def flexx(m, r = 0, c = 0, rw = 1, cw = 1):
    if r !=  None:
        m.rowconfigure(r, weight = rw)
    if c !=  None:
        m.columnconfigure(c, weight = cw)

class child(tk.Toplevel):

    def __init__(self, master, title, key):
        super().__init__(master)
        self.transient(master)
        self.title(title)
        flexx(self)
        self.protocol('WM_DELETE_WINDOW', self.toggle)
        self.bind('<Escape>', self.toggle)
        self.bind(key, self.toggle)

    def toggle(self, event = None):
        '''toggle child on|off'''
        if self.winfo_viewable():
            self.withdraw()
        else:
            self.deiconify()
            self.focus_force()


class parent(tk.Tk):

    def __init__(self, title, icon = None):

        super().__init__()
        self.title(title)
        flexx(self)
        self.protocol('WM_DELETE_WINDOW', self.closer)
        self.bind('<Escape>', self.closer)
        if icon:
            self.iconbitmap(default = icon)
        self.withdraw()

    def maker(self, title, key, geom):
        anon = child(self, title, key)
        # Connect parent and child
        self.bind(key, anon.toggle)
        anon.geometry(geom)
        return anon

    def closer(self, event = None):
        if askyesno(
            title = 'Confirm', message = 'Really',
            detail = 'Close Program?', default = 'no'):

            self.destroy()

if __name__ == '__main__':

    the = parent('The Parent', icon = None) # icon = '.\Icons\Py-006.ico')
    w,h,x,y = 500, 500, 100, 50
    the.geometry(f'{w}x{h}+{x}+{y}')
    the.boy = the.maker('harri', '<Control-h>', f'200x200+{x+w+5}+{y}')
    the.girl = the.maker('suzie', '<Control-s>', f'200x200+{x+w+5}+{y+235}')
    # make all other children
    # inter-connect all children
    the.girl.bind('<Control-h>', the.boy.toggle)
    the.boy.bind('<Control-s>', the.girl.toggle)
    the.deiconify()
    the.mainloop( )

可以很容易地更改您的代码来解决问题。 Tkinter 支持一种称为“变量 类”的东西——参见 The Variable Classes (BooleanVar, DoubleVar, IntVar, StringVar) — which are very handy for storing and passing around data within tkinter apps. In particular, Entry widgets support storing their contents in one (see Entry widget options),只需在创建时通过 textvariable= 关键字参数将其传递给它即可。完成后,无论 Entry 是否仍然存在,都可以随时检索小部件当前(或最后)值。

下面是您的代码的修改版本,其中包含创建和使用代码将放入子 window 小部件的信息传递回其父级所需的修改。我用 # ALL CAPS COMMENTS 指出了最重要的变化。另请注意,我还重新格式化了您的代码,使其遵循 PEP 8 - Style Guide for Python Code 建议并且更具可读性。我强烈建议您阅读并遵循这些指南。

import tkinter as tk

class Parent(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)

        self.dic = {}
        self.dic['var'] = ['default']
        self.dic['child1_value'] = tk.StringVar(value='')  # ADDED.

        self.title("Parent")
        self.button1 = tk.Button(self, text="open Child1",
                                 command=self.open_child1, width=20)
        self.button1.grid(row=0, column=0, padx=5, pady=5)
        self.button2 = tk.Button(self, text="Get Child1 config",
                                 command=self.get_child1_value, width=20)
        self.button2.grid(row=0, column=1, padx=5, pady=5)
        self.label1 = tk.Label(self, text="", width=10)
        self.label1.grid(row=0, column=2, sticky='ew')

        self.Child1_from_parent = Child1(self)
        self.Child1_from_parent.withdraw()

    def open_child1(self):
        self.Child1_from_parent.deiconify()

    def get_child1_value(self):
        self.label1.config(text=self.dic['var'][0] + '\n' +
                                self.dic['child1_value'].get())  # CHANGED.


class Child1(tk.Toplevel):
    def __init__(self, master):
        tk.Toplevel.__init__(self, master)
        self.frame  = tk.Frame(self)

        self.title("Child")
        self.label1 = tk.Label(self, text="Config 1", width=10)
        self.label1.grid(row=0, column=0)
        # ADDED `textvariable=` keyword argument.
        self.child1_entry = tk.Entry(self, width=10,
                                     textvariable=master.dic['child1_value'])
        self.child1_entry.grid(row=0, column=1, padx=5, pady=5)
        self.child1_entry.insert(tk.END, self.master.dic['var'][0])
        self.child1_entry.bind('<Return>', self.update_value)
        self.button4=tk.Button(self, text="Close", command=self.close_child1, width=20)
        self.button4.grid(row=0, column=2, padx=5, pady=5)
        self.button5=tk.Button(self, text="destroy", command=self.destroy_child1,
                               width=20)
        self.button5.grid(row=0, column=3, padx=5, pady=5)

    def update_value(self, event):
        self.master.dic['var'][0] = self.master.Child1_from_parent.child1_entry.get()

    def close_child1(self):
        self.withdraw()

    def destroy_child1(self):
        self.destroy()


def main():
    Parent().mainloop()

if __name__ == '__main__':
    main()