4dummies
4dummies

Reputation: 819

How to really destroy a Frame so it disappears

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

Answers (2)

4dummies
4dummies

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

furas
furas

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

Related Questions