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.
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()
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( )
Related
NameError: name 'onOpen' is not defined
There is something wrong with the command function. I am not sure what I did wrong here. I had the code tested before the onOpen function and it works fine.
import tkinter as tk
from tkinter import filedialog
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
def onOpen():
""" Ask the user to choose a file and update the values of label change button"""
button_label.set(filedialog.askopenfilename(filetypes = ()))
def createWidgets(self):
#instruction label
self.labelInstruct = tk.Label(self, text="Instructions:", padx=10, pady=10)
self.labelInstruct = tk.Label(self, text="All you have to do is insert the file and save it.\n The conversion is instant", padx=10, pady=10)
self.labelInstruct.pack()
#insertfile button
self.ifbut = tk.Button(self, text="Insert File", command=onOpen)
self.ifbut.pack()
button_label = tk.StringVar(self)
text = tk.Label(self, textvariable = button_label)
text.pack()
#save button
self.saveLabel = tk.Label(self, text="Save File", padx=10, pady=10)
self.saveLabel.pack()
self.saveButton = tk.Button(self)
self.saveButton.pack()
#quit button
self.quitButton = tk.Button(self, text='Quit',
command=self.quit)
self.quitButton.pack()
app = Application()
app.master.title('Sample application')
app.mainloop()
Seeing that there are many problems with the way this code is written I am only going to point out a few of them and tackle the main question from the OP.
Lets start with the fact that you need to define the main window with something like root = tk.Tk() and you also need to make sure all your methods in your class have the argument self as the first argument.
Also any variable you are defining also should have self so that the class can interact with it. This (button_label) for example should be self.button_label.
There is no reason to use return the way you are trying to in the onOpen(self): method. Return does not work like that. Return is there so you can return a value to something that is calling the function/method to be used for something else and is not for setting the value of something.
Note that I also add the root window variable to the app = Application(root) line. This lets us pass the main window into the class.
all and all the following should work for the onOpen(self): function but the rest still needs some work.
import tkinter as tk
from tkinter import filedialog
class Application(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.pack()
self.createWidgets()
def onOpen(self):
""" Ask the user to choose a file and update the values of label change button"""
self.button_label.set(filedialog.askopenfilename(filetypes = ()))
def createWidgets(self):
#instruction label
self.labelInstruct = tk.Label(self.parent, text="Instructions:", padx=10, pady=10)
self.labelInstruct = tk.Label(self.parent, text="All you have to do is insert the file and save it.\n The conversion is instant", padx=10, pady=10)
self.labelInstruct.pack()
#insert file button
self.ifbut = tk.Button(self.parent, text="Insert File", command = self.onOpen)
self.ifbut.pack()
self.button_label = tk.StringVar()
self.text = tk.Label(self.parent, textvariable = self.button_label)
self.text.pack()
#save button
self.saveLabel = tk.Label(self.parent, text="Save File", padx=10, pady=10)
self.saveLabel.pack()
self.saveButton = tk.Button(self.parent, text = "Save")
self.saveButton.pack()
#quit button
self.quitButton = tk.Button(self.parent, text='Quit', command=self.quit)
self.quitButton.pack()
root = tk.Tk()
app = Application(root)
app.master.title('Sample application')
app.mainloop()
You need to return the function value as below:
def onOpen():
""" Ask the user to choose a file and update the values of label change button"""
return button_label.set(filedialog.askopenfilename(filetypes = ()))
How can I get a blocking modal input dialog box in standard Python?
I need the user to input a value before the code can proceed.
Here is some not-working test code, but the idea is that I should be able to call MyDialog from anywhere in the script, so this is just a simplified example.
import tkinter
class MyDialog:
def __init__(self, prompt):
self.top = tkinter.Toplevel()
tkinter.Label(self.top, text=prompt).pack()
self.e = tkinter.Entry(self.top)
self.e.pack(padx=5)
tkinter.Button(self.top, text="OK", command=self.ok).pack(pady=5)
def ok(self):
self.top.destroy()
return self.e.get()
root = tkinter.Tk()
userName = MyDialog('Enter your name')
tkinter.Label(root, text="Hello {}".format(userName)).pack()
root.mainloop()
The dialog should not only disable the master window, but block whatever code called it. And it should be able to pass the value back to the calling code.
The solution requires two critical pieces. First, use grab_set to block all events in the other window (or, more correctly, send all events to the dialog window). Second, use wait_window to prevent the method from returning until the dialog has been destroyed.
That being said, you shouldn't be using it like in your example. You need to have the mainloop running before you create the window. It might work OK on some platforms, but in general you shouldn't expect your GUI to behave properly until mainloop is running.
Here's a simple example:
import Tkinter as tk
class MyDialog(object):
def __init__(self, parent, prompt):
self.toplevel = tk.Toplevel(parent)
self.var = tk.StringVar()
label = tk.Label(self.toplevel, text=prompt)
entry = tk.Entry(self.toplevel, width=40, textvariable=self.var)
button = tk.Button(self.toplevel, text="OK", command=self.toplevel.destroy)
label.pack(side="top", fill="x")
entry.pack(side="top", fill="x")
button.pack(side="bottom", anchor="e", padx=4, pady=4)
def show(self):
self.toplevel.grab_set()
self.toplevel.wait_window()
value = self.var.get()
return value
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.button = tk.Button(self, text="Click me!", command=self.on_click)
self.label = tk.Label(self, text="", width=40)
self.label.pack(side="top", fill="x")
self.button.pack(padx=20, pady=20)
def on_click(self):
result = MyDialog(self, "Enter your name:").show()
self.label.configure(text="your result: '%s'" % result)
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
I want to create a custom widget in tkinter such that when instantiated, displays a label and an entry box. Example I created a class named entry and call as.. entry ('name', master ) and this would display a label with text as main along side an entry box.
I have succeeded in doing that but my problem is with the geometry managers. they all seem to mess up everything
Your widget should subclass Frame. Within the frame you can use any geometry manager you want without affecting any other code. It's important that the widget class does not call grid, pack or place on itself -- that's the job of the function that creates the widget. Every widget, or function that creates a widget, should only ever worry about laying out its children.
Here's an example that creates a couple of different custom widgets. Each uses a different geometry manager to illustrate that they don't interfere with each other:
try:
# python 3.x
import tkinter as tk
except ImportError:
# python 2.x
import Tkinter as tk
class CustomWidget(tk.Frame):
def __init__(self, parent, label, default=""):
tk.Frame.__init__(self, parent)
self.label = tk.Label(self, text=label, anchor="w")
self.entry = tk.Entry(self)
self.entry.insert(0, default)
self.label.pack(side="top", fill="x")
self.entry.pack(side="bottom", fill="x", padx=4)
def get(self):
return self.entry.get()
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.label = tk.Label(self)
self.e1 = CustomWidget(self, "First Name:", "Inigo")
self.e2 = CustomWidget(self, "Last Name:", "Montoya")
self.submitButton = tk.Button(self, text="Submit", command=self.submit)
self.e1.grid(row=0, column=0, sticky="ew")
self.e2.grid(row=1, column=0, sticky="ew")
self.label.grid(row=2, column=0, sticky="ew")
self.submitButton.grid(row=4, column=0)
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(2, weight=1)
def submit(self):
first = self.e1.get()
last = self.e2.get()
self.label.configure(text="Hello, %s %s" % (first, last))
if __name__ == "__main__":
root = tk.Tk()
Example(root).place(x=0, y=0, relwidth=1, relheight=1)
root.mainloop()
I agree with Mr. Oakley. You should subclass frame to do your job.
The simplest way to do what you want is to create a module with the following code:
# AnnotatedEntry.py
def AnnotatedEntry(master, name="An annoted entry box"):
'''
As a little extra, name is a keyword-argument, which defaults to "An annotated
entry box."
'''
import tkinter as tk
overlord = tk.Frame(master, height=5, width=40)
labeller = tk.Label(overlord, text=name, font="Times 14 bold")
labeller.grid(sticky='new')
inputter = tk.Entry(overlord, font="Times 14 bold")
inputter.grid(sticky='sew', pady=(10,0))
return overlord
This would be used as follows:
# Main program
import tkinter
import AnnotatedEntry
root = tkinter.Tk()
hold = AnnotatedEntry.AnnotatedEntry(root, name="Hello, world!")
hold.grid()
I hereby affirm, on my Scout Honor, that this code has been fully tested, and is guaranteed to work in Python 3.7.4. That being said, there is currently no method for returning the data contained in the Entry; you will have to work that out for yourself.
Based on #Bryan Oakley answer, I do have some modification. I know it's out of topic somehow. This is how to return a value from the widget and it only allows integer up to some number of digits that the user must entered.
#create a global value
global tbVal
tbVal = 0
class CustomWidget(tk.Frame):
def __init__(self, parent, nDigits):
tk.Frame.__init__(self, parent)
self.entry = tk.Entry(self)
self.entry.pack(side="bottom", fill="x", padx=4)
self.entry.configure(validate='all',validatecommand=windows.register(self.sbValidate),'%P','%W',nDigits))
def get(self):
return self.entry.get()
def sbValidate(self, userInput, widget, nDigits):
global tbVal
tbVal = userInput
if userInput == '':
return True
if '.' in userInput or ' ' in userInput:
return False
n = len(userInput)
if n > int(nDigits):
return False
try:
val = int(float(userInput))
except ValueError:
return False
return val
class Example(tk.Frame):
def __init__(self, parent, nDigitsLimit):
tk.Frame.__init__(self, parent)
self.e1 = CustomWidget(self, nDigitsLimit)
self.e1.grid(row=0, column=0, sticky="ew")
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(2, weight=1)
def btnStartClick():
print(tbVal)
nDigitsLimit = 8
tbTest = ttk.Entry(Example(windows, nDigitsLimit).place(x=20, y=20, relwidth=0.25, relheight=0.05))
btnStart = tk.Button(frame, text='Start', command=btnStartClick)
btnStart.place(relx=0.50, rely=0.50)
I am new to Python and Tkinter so unable to figure out which might be the simplest thing to do. Could someone please check the below code and tell me how can I trace value returned by radiobutton defined in child class and pass it to parent class. I get following error after compiling:
AttributeError: Toplevel instance has no attribute 'trace_fun'
I am not sure why am I getting this error since I have defined trace_fun in child class body. I have successfully traced variables in parent class but getting above error while trying to do it in the child class.
from Tkinter import *
class Parent(Frame):
classvar = 0
def __init__(self):
Frame.__init__(self)
self.master.title("Parent WIndow")
self.master.geometry("200x100")
self.grid()
self._button = Button(self, text="Create", width=10, command=self.new_window)
self._button.grid(row=0, column=0, sticky=E+W)
def new_window(self):
self.new = Child()
class Child(Parent, Frame):
def __init__(self):
Parent.__init__(self)
new = Frame.__init__(self)
new = Toplevel(self)
new.title("Child Window")
new.grid()
new._var = IntVar()
new._var.set(0)
new._var.trace("w", new.trace_fun)
new._radioButton = Radiobutton(new, text = "Option 1", variable = new._var, value = 1)
new._radioButton.grid(row=0, column=0, sticky=W, padx=10, pady=10)
new._radioButton2 = Radiobutton(new, text = "Option 2", variable = new._var, value = 2)
new._radioButton2.grid(row=1, column=0, sticky=W, padx=10, pady=10)
new._button = Button(new, text = 'Ok', command=new.destroy)
new._button.grid(row=2, column=0, pady=10)
def trace_fun(new, *args):
print new._var.get()
Parent.classvar = new._var.get()
obj = Parent()
def main():
obj.mainloop()
main()
You've overwritten your new variable here:
new = Frame.__init__(self)
new = Toplevel(self)
After those two statements execute, new is equal to an instance of the Toplevel class.
Next, this code executes:
new._var.trace("w", new.trace_fun)
and in particular:
new.trace_fun
So, you have a Toplevel instance trying to access an attribute named trace_fun. The error message is telling you that the Toplevel class does not have any attribute named trace_fun.
Edit:
You can't call trace_fun on a Toplevel instance--ever. Nor can you call trace_fun on a Parent instance--ever. So print out a copy of your program, then get a pen and circle all the variables that are Toplevel instances; then circle all the variables that are Parent instances. You can't call trace_fun on any of those variables. Alternatively, circle all the variables that are Child instances. You can call trace_fun on those variables.
Here is an example of what you can do:
class Child:
def do_stuff(self): #1) self is an instance of class Child, i.e. the object that is calling this method
self.trace_fun() #2) The Child class defines a method named trace_fun()
#3) Therefore, self can call trace_fun()
x = self.trace_fun #4) ...or you can assign self.trace_fun to a variable
#5) ...or pass self.trace_fun to another function
def trace_fun(self):
print 'hello'
d = Chile()
d.do_stuff()
--output:--
hello
It doesn't look like you have a Parent/Child relationship between your two frames--because the Child frame doesn't use anything inherited from the Parent frame. So you could just create two separate frames for your app. Here is an example:
import Tkinter as tk
class EntryFrame(tk.Frame):
classvar = 0
def __init__(self, root):
tk.Frame.__init__(self, root) #Send root as the parent arg to Frame's __init__ method
root.title("Parent Window")
root.geometry("400x200")
tk.Label(self, text="First").grid(row=0)
tk.Label(self, text="Second").grid(row=1)
e1 = tk.Entry(self)
e2 = tk.Entry(self)
e1.grid(row=0, column=1)
e2.grid(row=1, column=1)
button = tk.Button(self, text="Create", width=10, command=self.create_new_window)
button.grid(row=2, column=0, sticky=tk.E + tk.W)
self.grid()
def create_new_window(self):
RadioButtonFrame()
class RadioButtonFrame(tk.Frame):
def __init__(self):
new_top_level = tk.Toplevel()
tk.Frame.__init__(self, new_top_level) #Send new_top_level as the parent arg to Frame's __init__ method
new_top_level.title("Radio Button Window")
new_top_level.geometry('400x300+0+300') # "width x height + x + y"
self.int_var = int_var = tk.IntVar()
int_var.trace("w", self.trace_func)
int_var.set(0)
rb1 = tk.Radiobutton(self, text = "Option 1", variable = int_var, value = 1)
rb1.grid(row=0, column=0, sticky=tk.W, padx=10, pady=10)
rb2 = tk.Radiobutton(self, text = "Option 2", variable = int_var, value = 2)
rb2.grid(row=1, column=0, sticky=tk.W, padx=10, pady=10)
button = tk.Button(self, text = 'Ok', command=new_top_level.destroy)
button.grid(row=2, column=0, pady=10)
self.grid()
def trace_func(self, *args):
radio_val = self.int_var.get()
print radio_val
EntryFrame.classvar = radio_val
def main():
root = tk.Tk()
my_frame = EntryFrame(root)
root.mainloop()
main()
By making slight changes, now my code is working perfectly. Posting new code for someone stuck on the same point as me earlier. Changes can be seen in the below code:
import Tkinter as tk
class Parent:
classvar = 0
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.master.title("Parent Window")
self.master.geometry("400x100")
self.frame.grid()
self._button = tk.Button(self.frame, text="Create", width=10, command=self.new_window)
self._button.grid(row=0, column=0, sticky=tk.E+tk.W)
def new_window(self):
self.child_window = tk.Toplevel(self.master)
self.app = Child(self.child_window)
class Child(Parent):
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.master.title("Child Window")
self.frame.grid()
self._var = IntVar()
self._var.set(0)
self._var.trace("w", self.trace_fun)
self._radioButton = tk.Radiobutton(self.frame, text = "Option 1", variable = self._var, value = 1)
self._radioButton.grid(row=0, column=0, sticky=W, padx=10, pady=10)
self._radioButton2 = tk.Radiobutton(self.frame, text = "Option 2", variable = self._var, value = 2)
self._radioButton2.grid(row=1, column=0, sticky=W, padx=10, pady=10)
self._button = tk.Button(self.frame, text = 'Ok', command=self.master.destroy)
self._button.grid(row=2, column=0, pady=10)
def trace_fun(self, *args):
Parent.classvar = self._var.get()
print Parent.classvar
root = tk.Tk()
obj = Parent(root)
def main():
root.mainloop()
main()
This is the front end I developed for my application using Tkinter:
from Tkinter import *
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Simple")
self.pack(fill=BOTH, expand=1)
frame = Frame(self, relief="flat", borderwidth=1)
label=Label(frame,text="Scope:")
label.pack(side="left", fill=None, expand=False)
var = StringVar()
var.set("today")
list = OptionMenu(frame, var, "today","yesterday","this week","last week","this month","last month")
list.pack(side="left", fill=None, expand=False)
fetchButton = Button(frame, text="Fetch",command=self.handle(var))
fetchButton.pack(side="left", fill=None, expand=False)
frame.grid(row=1,column=1,pady=4,padx=5,sticky=W)
area = Text(self,height=15,width=60)
area.grid(row=2,column=1,rowspan=1,pady=4,padx=5)
scroll = Scrollbar(self)
scroll.pack(side=RIGHT, fill=Y)
area.config(yscrollcommand=scroll.set)
scroll.config(command=area.yview)
scroll.grid(row=2, column=2, sticky='nsew')
quitButton = Button(self, text="Cancel",command=self.quit)
quitButton.grid(pady=4,padx=5,sticky=W,row=3, column=1)
root = Tk()
app = Example(root)
root.mainloop()
Where exactly do I have to put the handle() method so it can write repeatedly to the text widget? When I put handle() within the Example class and use self.area.insert(), it shows an error saying
Example instance has no attribute 'area'
Please help out.
You need to pass the function object to the Button instance, not a function call. i.e.
fetchButton = Button(frame, text="Fetch",command=self.handle)
To make the handle work in the context of the rest of the code:
from Tkinter import *
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.parent.title("Simple")
self.pack(fill=BOTH, expand=1)
self.init_ui()
def init_ui(self):
self.frame = Frame(self, relief="flat", borderwidth=1)
self.frame.grid(row=1,column=1,pady=4,padx=5,sticky=W)
self.label=Label(self.frame,text="Scope:")
self.label.pack(side="left", fill=None, expand=False)
self.var = StringVar()
self.var.set("today")
self.list = OptionMenu(self.frame, self.var, "today","yesterday",
"this week","last week","this month",
"last month")
self.list.pack(side="left", fill=None, expand=False)
self.fetchButton = Button(self.frame, text="Fetch",command=self.handle)
self.fetchButton.pack(side="left", fill=None, expand=False)
self.area = Text(self,height=15,width=60)
self.area.grid(row=2,column=1,rowspan=1,pady=4,padx=5)
self.scroll = Scrollbar(self)
self.scroll.pack(side=RIGHT, fill=Y)
self.area.config(yscrollcommand=self.scroll.set)
self.scroll.config(command=self.area.yview)
self.scroll.grid(row=2, column=2, sticky='nsew')
self.quitButton = Button(self, text="Cancel",command=self.quit)
self.quitButton.grid(pady=4,padx=5,sticky=W,row=3, column=1)
def handle(self):
self.area.delete(1.0, END)
self.area.insert(CURRENT,self.var.get())
if __name__ == "__main__":
root = Tk()
app = Example(root)
root.mainloop()
Declaring your widgets as attributes will save you a lot of pain an suffering as your application expands. Also keeping references to everything in Tk can stop some unwanted garbage collection, particularly with images in Label instances.
It is also worth noting that using grid and pack interchangeably is likely to lead to bugs later on.