2

I am looking for a method to create widgets (most likely a Label) ad nauseam, with the caveat that they can be removed or unpacked later on.

I can generate the widgets just fine, but they are not assigned a name. I do not understand how I would, if it is possible, remove a certain anonymous widget.

My first instinct was to dynamically create variable names with a stable convention, but that may unnecessarily open a can of worms. The idea is expressed below. I'd like to be able to remove a certain Button widget while not knowing at run-time how many I will handle. Thank you.

from Tkinter import *
import time
import ttk


def onDoubleClick(event):
    item = t.selection()
 #print "you clicked on", t.item(item,"text")

    if (t.item(item,"text")=="Start IO"):
        Button2 = Button(frame2,text="Button2",command=but).pack()


def but():
    pack_forget()

root=Tk()
root.geometry("800x300")
frame1 = Frame(root)
frame2 = Frame(root)

t=ttk.Treeview(frame1)
t.heading("#0",text="Test steps")
t.insert("",0,"IO",text="IO")
t.insert("IO","end",text="Start")
t.bind("<Double-1>", onDoubleClick)
t.pack()
frame1.pack(side=LEFT)
frame2.pack(side=LEFT)

EDIT: My feature request was admittedly short-sighted. My ultimate goal is to have a Label widget and a Button side-by-side, both comprising what is to be a 'step' in a test launcher. Clicking the button will remove both itself and its respective Label from the GUI. I'm able to create both widgets and delete either one of them on the Button's callback, but to pack_forget both I believe I need to def a function. I believe my problem lies in passing a correct reference to def removeStep A use case is diagrammed below: ....[If this could be solved my RTFM please feel free to let me know, I just couldn't find it]

TEST: Make a PB&J

Step 0: Get Bread [Remove step]

Step 1: Smear PB [Remove step]

Step 2: Smear Jelly [Remove step]

trstowell
  • 33
  • 1
  • 1
  • 5

2 Answers2

9

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()

Edit: With more information about your use case, I would probably do

class RemovableTask(Frame):
    def __init__(self, master, name, **options):
        Frame.__init__(self, master, **options)
        lbl = Label(self, text=name)
        btn = Button(self, text='Remove step', command=self.destroy)
        lbl.grid(row=0, column=0)
        btn.grid(row=0, column=1)

Then just create instances of RemovableTask with names like "Step 0: Get Bread", and grid or pack them in a column. Everything else would be handled automatically.

Abe Karplus
  • 8,350
  • 2
  • 27
  • 24
5

You have a few options here. The first option is to store your dynamically created buttons in a list. Then you can add/remove buttons whenever you want -- And you keep a reference to all of the buttons. Clean and simple.

A second option is to use the config method after you've created the button (and have a reference to it) to destroy the widget -- Or at least remove it from the display (widget.pack_forget doesn't actually destroy the widget! It could be re-packed later. To actually destroy a widget, you want to call widget.destroy instead) ... :

import Tkinter as tk

root = tk.Tk()

def add_new():
    b = tk.Button(root,text="Click to destroy")
    b.pack()
    b.config(command=b.pack_forget) 

b = tk.Button(root,text="Add_new",command=add_new)
b.pack()
root.mainloop()
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • If all that should happen when a button is clicked is to remove it, this is cleaner. However, the config version gets messy if you want the button to do any more than just remove itself. – Abe Karplus Feb 11 '13 at 01:49
  • @AbeKarplus -- It really depends on what OP is trying to achieve with the buttons. Ultimately, the `list` solution has the problem of somehow keeping track of which button is at which index. However, this (second) solution is problematic as you don't have a good reference to the button in question -- It's easy to have the button perform other actions before destroying itself however. `b.config(command=lambda:do_something_with_b(b))` -- but ultimately you're right that unless that function finds a way to destroy `b`, it's going to be tricky to do get rid of it any other way. – mgilson Feb 11 '13 at 01:54
  • This seems like a memory leak to me -- you remove the widget from view but you never destroy it. – Bryan Oakley Feb 11 '13 at 03:20
  • @BryanOakley -- hence my comment about not actually *destroying* the widget ... Anyway, I've elaborated a little more about `widget.destroy` and added emphasis to my original comment. Thanks for the comment. – mgilson Feb 11 '13 at 03:26