Reputation: 819
I have a little program that does a search in a database and shows the results. My plan is to have the results show up in a suitably-sized frame in the same window where the search parameters were placed.
Since I want to replace the results for each new query, I have a results_owner frame which is permanent and has a single child results_frame which is populated and destroyed as required. At least, that's the plan. It is not going well.
I can create the frame and put stuff in it. But when I want to remove or replace it, I'm left with stuff from the previous incarnation. Some stuff is modified, some stuff seems permanent, and it baffles me.
You may have to make the window taller to see the full effect. The basic flaw is that when you click the "Line+" button, lines that show up are permanent, so that when you click the "new frame" button, lines should disappear but don't.
Here's code:
#!/usr/bin/env python3
"""Find a matching record in the database
Last Modified: Fri Dec 15 18:20:32 PST 2017
"""
import tkinter as tk # https://docs.python.org/3.5/library/tkinter.html
class Asker(tk.Frame):
def __init__(self, root=None):
super().__init__(root)
self.root = root
root.title("Asker")
self.pack()
self._create_widgets()
results_owner = None
results_frame = None
iteration = 0
def _create_widgets(self):
noterow2 = tk.Frame(root)
msgtx = "This represents the fixed area of the window"
tx2 = tk.Label(noterow2, width=len(msgtx), text=msgtx, anchor='w')
noterow2.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2)
tx2.pack(side=tk.LEFT, padx=5, pady=5)
buttonrow = tk.Frame(root)
buttonrow.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2)
b2 = tk.Button(buttonrow, text='Quit', command=root.quit)
b2.pack(side=tk.LEFT, padx=5, pady=5)
test1 = tk.Button(buttonrow, text='Line+', command=(lambda arg=self.results_frame: self.addline(arg)))
test1.pack(side=tk.LEFT, padx=5, pady=5)
test2 = tk.Button(buttonrow, text='New Frame', command=self.new_results)
test2.pack(side=tk.LEFT, padx=5, pady=5)
self.results_owner = tk.Frame(root)
self.results_owner.pack(side=tk.TOP, fill=tk.X)
self.new_results()
def new_results(self):
if self.results_frame is not None:
self.results_frame.destroy()
self.results_frame = tk.Frame(self.results_owner)
sampletxt = "New frame " + str(self.iteration)
sample = tk.Label(self.results_frame, text=sampletxt, width=len(sampletxt), anchor='w')
sample.pack(side=tk.LEFT, padx=5, pady=5)
self.results_frame.pack(side=tk.TOP,fill=tk.X,padx=0, pady=self.iteration)
self.root.update_idletasks()
self.iteration += 1
def addline(self, results):
print("addline")
msg = "New Line"
mytx = tk.Label(results, text=msg, width=len(msg), anchor='w')
mytx.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2)
if __name__ == '__main__':
root = tk.Tk()
anti = Asker(root=root)
anti.mainloop()
Here's modified code using some of the suggestions in comments (but it still does not work as intended):
#!/usr/bin/env python3
"""Find a matching record in the database
Last Modified: Fri Dec 15 20:31:17 PST 2017
"""
import tkinter as tk # https://docs.python.org/3.5/library/tkinter.html
class Asker(tk.Frame):
def __init__(self, root=None):
super().__init__(root)
self.root = root
root.title("Asker")
self.pack()
self.results_owner = None
self.results_frame = None
self.iteration = 0
self._create_widgets()
def _create_widgets(self):
noterow2 = tk.Frame(root)
msgtx = "This represents the fixed area of the window"
tx2 = tk.Label(noterow2, width=len(msgtx), text=msgtx, anchor='w')
noterow2.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2)
tx2.pack(side=tk.LEFT, padx=5, pady=5)
buttonrow = tk.Frame(root)
buttonrow.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2)
b2 = tk.Button(buttonrow, text='Quit', command=root.quit)
b2.pack(side=tk.LEFT, padx=5, pady=5)
test1 = tk.Button(buttonrow, text='Line+', command=self.addline(self.results_frame))
test1.pack(side=tk.LEFT, padx=5, pady=5)
test2 = tk.Button(buttonrow, text='New Frame', command=self.new_results)
test2.pack(side=tk.LEFT, padx=5, pady=5)
self.results_owner = tk.Frame(root)
self.results_owner.pack(side=tk.TOP, fill=tk.X)
self.new_results()
def new_results(self):
if self.results_frame is not None:
self.results_frame.pack_forget()
self.results_frame.destroy()
self.results_frame = tk.Frame(self.results_owner)
sampletxt = "New frame " + str(self.iteration)
sample = tk.Label(self.results_frame, text=sampletxt, width=len(sampletxt), anchor='w')
sample.pack(side=tk.LEFT, padx=5, pady=5)
self.results_frame.pack(side=tk.TOP,fill=tk.X,padx=0, pady=self.iteration)
self.root.update_idletasks()
self.iteration += 1
def addline(self, results):
print("addline")
msg = "New Line" + str(self.iteration)
mytx = tk.Label(results, text=msg, width=len(msg), anchor='w')
mytx.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2)
if __name__ == '__main__':
root = tk.Tk()
anti = Asker(root=root)
anti.mainloop()
Upvotes: 1
Views: 73
Reputation: 819
It turns out I had to make the call to addline a lambda; as it was, the function was being called just once. I also adjusted packing and borders to make it clear how things are grouped. I can now use this as a model for my app.
#!/usr/bin/env python3
"""Last Modified: Sat Dec 16 07:28:31 PST 2017
"""
import tkinter as tk # https://docs.python.org/3.5/library/tkinter.html
class Asker(tk.Frame):
def __init__(self, root=None):
super().__init__(root)
self.root = root
root.title("Asker")
self.pack()
self.results_owner = None
self.results_frame = None
self.iteration = 0
self._create_widgets()
def _create_widgets(self):
noterow2 = tk.Frame(root)
msgtx = "This represents the fixed area of the window"
tx2 = tk.Label(noterow2, width=len(msgtx), text=msgtx, anchor='w')
noterow2.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2)
tx2.pack(side=tk.LEFT, padx=5, pady=5)
buttonrow = tk.Frame(root)
buttonrow.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2)
b2 = tk.Button(buttonrow, text='Quit', command=root.quit)
b2.pack(side=tk.LEFT, padx=5, pady=5)
test1 = tk.Button(buttonrow, text='Line+', command=(lambda :self.addline(self.results_frame)))
test1.pack(side=tk.LEFT, padx=5, pady=5)
test2 = tk.Button(buttonrow, text='New Frame', command=self.new_results)
test2.pack(side=tk.LEFT, padx=5, pady=5)
self.results_owner = tk.Frame(root, borderwidth=3, relief=tk.RAISED)
self.results_owner.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2)
self.new_results()
def new_results(self):
if self.results_frame is not None:
self.results_frame.destroy()
self.results_frame = tk.Frame(self.results_owner, borderwidth=1, relief=tk.GROOVE)
sampletxt = "New frame " + str(self.iteration)
sample = tk.Label(self.results_frame, text=sampletxt, width=len(sampletxt), anchor='w')
sample.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2)
self.results_frame.pack(side=tk.TOP,fill=tk.X,padx=2, pady=2)
self.root.update_idletasks()
self.iteration += 1
def addline(self, results):
msg = "New Line" + str(self.iteration)
mytx = tk.Label(results, text=msg, width=len(msg), anchor='w')
mytx.pack(side=tk.TOP, fill=tk.X, padx=2, pady=2)
self.root.update_idletasks()
self.iteration += 1
if __name__ == '__main__':
root = tk.Tk()
anti = Asker(root=root)
anti.mainloop()
Upvotes: 1
Reputation: 142641
In you situation you have to use self.results_frame
directly in lambda
command=lambda:self.addline(self.results_frame)
to have access always to current value in variable self.results_frame
Using arg=self.results_frame
in lambda
lambda arg=self.results_frame: self.addline(arg)
you copy value from self.results_frame
to arg
only once (at start) and later function uses the same value all the time.
Upvotes: 1