PascalVKooten
PascalVKooten

Reputation: 21443

Destroying a dynamically created widget

I have basically a similar question, though I do not feel it has been answered properly:

Tkinter: How can I dynamically create a widget that can then be destroyed or removed?

The accepted answer is:

You'll want to store the dynamically-created widgets in a list. Have something like

     dynamic_buttons = []

     def onDoubleClick(event):
     ...
     button = Button(...)
     dynamic_buttons.append(button)
     button.pack() 

You can then access the buttons for removal with, say,

     dynamic_buttons[0].destroy()

You can see that the reference they speak of is not variable, here the number 0 is used. But when dynamically creating widgets, how do you connect these references to the buttons?

Say that you create a Toplevel widget (displays a file's content), and want to have a button to close the widget. The dynamic creation will allow multiple files to be open. The problem is that even with this list, how will the button "know" to which widget it belongs, as there is no hard reference (great that you have a list of the items, but toplevel 5 + button 5 have no clue they are 5th in their lists). There will always be just one "active" version of the button and the toplevel, and this one can be deleted.

aanstuur_files = []
aanstuur_frames = []
aanstuur_buttons = []

def editAanstuur():
    openfiles = filedialog.askopenfilenames()
    if not openfiles:
        return 
    for file in openfiles:
        newtop = Toplevel(nGui, height=100, width=100)
        labelTitle = Label(newtop, text=file).pack()
        newButton = Button(newtop, text="save & close", command= ...).pack()
        aanstuur_files.append(file)
        aanstuur_buttons.append(newButton)
        aanstuur_frames.append(newtop)

Upvotes: 1

Views: 298

Answers (2)

Bryan Oakley
Bryan Oakley

Reputation: 385980

How does the button know which window it belongs to? You tell it:

newButton = Button(newtop, command=lambda top=newtop: top.destroy())

By the way, you're assigning None to newButton in your code. This is because you are doing newbutton = Button(...).pack(), which means newbutton gets the value of pack() which is always None.

If you are going to save a reference to a widget, you must create the widget in a separate step from when you place it in a window.

An even better solution is to take advantage of classes and objects. Create your own subclass of Toplevel, and the instance will keep track of all of the subwidgets for you. For example:

class MyToplevel(Toplevel):
    def __init__(self, parent, filename, *args, **kwargs):
        Toplevel.__init__(self, parent, *args, **kwargs)
        self.filename = filename
        self.savebutton = Button(..., command=self.save)
        ...
    def save(self):
        print "saving...", self.filename
        ...
        self.destroy()
...
openfiles = filedialog.askopenfilenames()
if not openfiles:
    return 
for file in openfiles:
    newtop = MyToplevel(nGui, file, height=100, width=100)

Upvotes: 1

Martijn Pieters
Martijn Pieters

Reputation: 1122002

APass in a index to your command function using the enumerate() function:

def editAanstuur():
    openfiles = filedialog.askopenfilenames()
    if not openfiles:
        return 
    for i, file in enumerate(openfiles):
        newtop = Toplevel(nGui, height=100, width=100)
        labelTitle = Label(newtop, text=file).pack()
        newButton = Button(newtop, text="Save", command=lambda index=i: print(index)).pack()
        aanstuur_files.append(file)
        aanstuur_buttons.append(newButton)
        aanstuur_frames.append(newtop)

Make sure that you pass the index as a keyword parameter to bind the value when defining the lambda (a closure would use the last value of i).

enumerate() takes a second argument, the index to start at, which defaults at 0.

Upvotes: 0

Related Questions