Reputation: 13
How do I pass data between parent and child windows and preserve data/unique IDs?
If I use the destroy()
method for closing a child window, associated values are destroyed too, even when the dictionary that I send values to was initiated with the parent. Clicking on the Get child1config button after destroying the child window gives the error:
_tkinter.TclError: invalid command name ".!child1.!entry"
So, I don't destroy. Is it recommended to withdraw and deiconify many child windows?
How do I refer to the child window (and associated values) from parent window? Am I doing it correctly?
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()
My program will grow, so I am looking for expandability. Classes 'seems' like a good idea. I have a parent tkinter window running with live data, and I will open/navigate to different child windows to perform different functions while the main window is running, accessible, and receiving data from child windows.
Upvotes: 1
Views: 251
Reputation: 123463
The code you have can be changed fairly easily to fix the problem. Tkinter supports something called "Variable Classes" — see 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) simply by passing one to it via the textvariable=
keyword argument when its created. Once that's done, the widgets current (or last) value can be retrieved at any time whether the Entry
still exists or not.
Below is a modified version of your code with the modifications needed to create and use one to pass information that's put into the widget in the child window back to its parent. I've indicated the most important changes with # ALL CAPS COMMENTS
. Also note that I have also reformatted your code so it follows the PEP 8 - Style Guide for Python Code recommendations and is more readable. I strongly suggest you read and follow these guidelines.
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()
Upvotes: 1
Reputation: 2234
If I understand your question correctly then this code may help.
It is a minimal demonstration of a one parent many children implementation.
All data and micro changes to each child are maintained after closing child.
Keyboard short-cuts give access to all children and parent
Parent is accessible while child is active.
Real exit is via gate keeper dialog from messagebox.
Creating children is easy and open ended.
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( )
Upvotes: 0