I started programming in Python on Monday. I have enjoyed learning it. But I am stuck trying to understand how to avoid recursion when switching between tkinter menus! I am sure this is a very basic question, and I appreciate you tolerating my ignorance on this subject, but I have been unable to find an answer elsewhere.
What I am now doing is, eventually, giving me the error: RuntimeError: maximum recursion depth exceeded while calling a Python object
This is the pattern I am currently using. UPDATED: The code below is now a full, isolated copy reproducing the problem I am facing! :D
from tkinter import *
def mainmenu():
global frame, root
frame.destroy()
frame = Frame()
frame.pack()
button1 = Button(frame, text="anothermenulikethis", command = anothermenulikethis)
button2 = Button(frame, text="anothermenulikethis", command = anothermenulikethis)
button3 = Button(frame, text="mainmenu", command = mainmenu)
button1.pack(side=LEFT)
button2.pack(side=LEFT)
button3.pack(side=LEFT)
root.mainloop()
def anothermenulikethis():
global frame, root
frame.destroy()
frame = Frame()
frame.pack()
button1 = Button(frame, text="mainmenu", command = mainmenu)
button2 = Button(frame, text="mainmenu", command = mainmenu)
button3 = Button(frame, text="anothermenulikethis", command = anothermenulikethis)
button1.pack(side=LEFT)
button2.pack(side=LEFT)
button3.pack(side=LEFT)
root.mainloop()
root = Tk()
root.title("Recursive Menu Problem Isolation")
root.geometry("1200x600")
frame = Frame()
mainmenu()
And it all works fine, until its inevitable failure from maximum recursion depth. If anyone can suggest a better way of doing things, or has a link to an example of a better way of doing this, I am eager to learn.
PS: I have looked at and tried increasing the recursion depth, but I feel that is a poor man's solution to what is a fundamental problem with my approach.
Thank you in advance, everyone.
As requested, here is the traceback:
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/tkinter/__init__.py", line 1399, in __call__
return self.func(*args)
File "/Users/diligentstudent/Desktop/menutest.py", line 11, in mainmenu
button1 = Button(frame, text="anothermenulikethis", command = anothermenulikethis)
File "/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/tkinter/__init__.py", line 2028, in __init__
Widget.__init__(self, master, 'button', cnf, kw)
File "/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/tkinter/__init__.py", line 1958, in __init__
(widgetName, self._w) + extra + self._options(cnf))
File "/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/tkinter/__init__.py", line 1043, in _options
v = self._register(v)
File "/Library/Frameworks/Python.framework/Versions/3.2/lib/python3.2/tkinter/__init__.py", line 1079, in _register
f = CallWrapper(func, subst, self).__call__
RuntimeError: maximum recursion depth exceeded
Only one mainloop() is needed to handle a tkinter GUI.
With that said, I think you just need an example of the class structure:
from tkinter import Tk,Button
class Application(Tk):
def say_hi(self):
print('Hello world?!')
def close_app(self):
self.destroy()
def create_Widgets(self):
self.quitButton = Button(self, width=12, text='Quit', bg='tan',
command=self.close_app)
self.quitButton.grid(row=0, column=0, padx=8, pady=8)
self.helloButton = Button(self, width=12, text='Hello',
command=self.say_hi)
self.helloButton.grid(row=0, column=1, padx=8, pady=8)
def __init__(self):
Tk.__init__(self)
self.title('Hello world!')
self.create_Widgets()
app = Application()
app.mainloop()
To avoid possible conflicts with other modules, some people prefer importing like this
(clearly stating where everything comes from):
import tkinter as tk
class Application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title('Hello world!')
self.quitButton = tk.Button(self, width=12, text='Quit', bg='tan',
command=self.close_app)
self.quitButton.grid(row=0, column=0, padx=8, pady=8)
self.helloButton = tk.Button(self, width=12, text='Hello',
command=self.say_hi)
self.helloButton.grid(row=0, column=1, padx=8, pady=8)
def say_hi(self):
print('Hello world?!')
def close_app(self):
self.destroy()
app = Application()
app.mainloop()
And as you can see, creating the widgets can easily happen in the __init__
I decided to make a more practical / educational example based on what I've learned in the past month. While doing so I had a bit of a revelation: not everything requires a self. prefix in a class! This is especially true with a tkinter class, because you aren't manipulating it as an object in the main program. Mostly you need the self. prefix when you are going to use something in a method later. The previous examples display how anything (like the buttons) can receive a self. prefix, even when completely unnecessary.
Some things this example will show:
• pack() and grid() can be used in the same GUI as long as they don't share a master.
• A Text widget can be made to not expand when the font size changes.
• How to toggle a bold tag on and off of selected text.
• How to truly center a GUI on the screen. (more information here)
• How to make a Toplevel window appear in the same location relative to the main window.
• Two ways to prevent a Toplevel window from being destroyed, so it only needs to be created once.
• Make ctrl+a (select all) function properly.
import tkinter as tk
import tkFont
class Application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.title('T-Pad')
# Menubar
menubar = tk.Menu(self)
filemenu = tk.Menu(menubar, tearoff=0)
filemenu.add_command(label="Exit", command=self.close_app)
menubar.add_cascade(label="File", menu=filemenu)
formatmenu = tk.Menu(menubar, tearoff=0)
formatmenu.add_command(label="Font", command=self.show_sizeWin)
menubar.add_cascade(label="Format", menu=formatmenu)
self.config(menu=menubar)
# Bold Button
boldButton = tk.Button(self, width=12, text='Bold',
command=self.make_bold)
boldButton.pack()
# Text widget, its font and frame
self.defaultFont = tkFont.Font(name="defFont")
textFrame = tk.Frame(self, borderwidth=1, relief="sunken",
width=600, height=600)
textFrame.grid_propagate(False) # ensures a consistent GUI size
textFrame.pack(side="bottom", fill="both", expand=True)
self.mText = tk.Text(textFrame, width=48, height=24, wrap='word',
font="defFont")
self.mText.grid(row=0, column=0, sticky="nsew")
# Scrollbar and config
tScrollbar = tk.Scrollbar(textFrame, command=self.mText.yview)
tScrollbar.grid(row=0, column=1, sticky='nsew', pady=1)
self.mText.config(yscrollcommand=tScrollbar.set)
# Stretchable
textFrame.grid_rowconfigure(0, weight=1)
textFrame.grid_columnconfigure(0, weight=1)
# Bold Tag
self.bold_font = tkFont.Font(self.mText, self.mText.cget("font"))
self.bold_font.configure(weight="bold")
self.mText.tag_configure("bt", font=self.bold_font)
# Center main window
self.update_idletasks()
xp = (self.winfo_screenwidth() / 2) - (self.winfo_width() / 2) - 8
yp = (self.winfo_screenheight() / 2) - (self.winfo_height() / 2) - 30
self.geometry('{0}x{1}+{2}+{3}'.format(self.winfo_width(), self.winfo_height(),
xp, yp))
# Font Size Window (notice that self.sizeWin is given an alias)
sizeWin = self.sizeWin = tk.Toplevel(self, bd=4, relief='ridge')
self.sizeList = tk.Listbox(sizeWin, width=10, height=17, bd=4,
font=("Times", "16"), relief='sunken')
self.sizeList.grid()
doneButton = tk.Button(sizeWin, text='Done', command=sizeWin.withdraw)
doneButton.grid()
for num in range(8,25):
self.sizeList.insert('end', num)
sizeWin.withdraw()
sizeWin.overrideredirect(True) # No outerframe!
# Below is another way to prevent a TopLevel window from being destroyed.
# sizeWin.protocol("WM_DELETE_WINDOW", self.callback)
# Bindings
# Double click a font size in the Listbox
self.sizeList.bind("<Double-Button-1>", self.choose_size)
self.bind_class("Text", "<Control-a>", self.select_all)
## def callback(self):
## self.sizeWin.withdraw()
def select_all(self, event):
self.mText.tag_add("sel","1.0","end-1c")
def choose_size(self, event=None):
size_retrieved = self.sizeList.get('active')
self.defaultFont.configure(size=size_retrieved)
self.bold_font.configure(size=size_retrieved)
def show_sizeWin(self):
self.sizeWin.deiconify()
xpos = self.winfo_rootx() - self.sizeWin.winfo_width() - 8
ypos = self.winfo_rooty()
self.sizeWin.geometry('{0}x{1}+{2}+{3}'.format(self.sizeWin.winfo_width(),
self.sizeWin.winfo_height(), xpos, ypos))
def make_bold(self):
try:
current_tags = self.mText.tag_names("sel.first")
if "bt" in current_tags:
self.mText.tag_remove("bt", "sel.first", "sel.last")
else:
self.mText.tag_add("bt", "sel.first", "sel.last")
except tk.TclError:
pass
def close_app(self):
self.destroy()
app = Application()
app.mainloop()
A note to others with this issue: your command for your button may not be at the proper indentation level! Check that it is inline with your other class methods before digging any further. I ran into this issue myself not long ago, and re-checking my indentations fixed everything.
Related
i've a tkinter toplevel widget were you can type some text, and then press ok. it is created in a function: if you call that funtion the program should wait for a click on the button and than the function should return that text, so that my function have the same use a the input function in python 3. now my problem is: how can i pause my script until the user clicked on a button, so that all other files in my project pause (there happens nothing if the user clicks on a button in another tkinter window) are paused? i tried to use button.wait_variable(chosen) and when the user clicks on the button the variable "chosen" will change.
def choosefile():
root = tk.Tk()
root.geometry("275x50")
chosen = tk.BooleanVar()
chosen.set(False)
label = tk.Label(root, text="enter name of file (without extention)")
label.pack(fill="both")
entry = tk.Entry(root)
entry.pack(side="left", expand=True, fill="x")
button = tk.Button(root, text="Ok", command=lambda: [chosen.set(True), output(chosen.get())])
button.pack(side="right")
output("waiting")
button.wait_variable(chosen)
output("not waiting")
result = entry.get() + ".py"
root.quit()
return result
this works ok if you only run this file, but when i this run with main.py(below) the window don't destroy and also not return.
main.py
from tkinter import Tk, Button, Entry, StringVar
import tkinter.font as tkfont
from customtext import CustomText
from execute import Execute
from coloring import color
from files import create, openfile, choosefile
from alert import alert
import keyboard
class App(Tk):
def __init__(self):
super(App, self).__init__()
self.width = self.winfo_screenwidth() - 1
self.height = self.winfo_screenheight() - 1
self.attributes('-fullscreen', True)
self.state = StringVar()
self.state.set("saved")
self.input = CustomText(self)
self.input.place(x=10, y=10, width=self.width - 20, height=self.height - 20)
self.input.bind("<<TextModified>>", lambda a: [color(self.input), self.state.set("unsaved")])
self.run = Button(self, text="run", command=lambda: [create(self.name.get("1.0", "end-1c"), ".py", self.input.get("1.0", "end-1c")), Execute(self.name.get("1.0", "end-1c")), self.state.set("saved")])
self.run.place(x=self.width - 70, y=20, width=50, height=30)
self.exit = Button(self, text="exit", command=lambda: self.destroy())
self.exit.place(x=self.width - 130, y=20, width=50, height=30)
self.save = Button(self, text="save", command=lambda: [create(self.name.get("1.0", "end-1c"), ".py", self.input.get("1.0", "end-1c")), self.state.set("saved")])
self.save.place(x=self.width - 190, y=20, width=50, height=30)
self.open = Button(self, text="open", command=lambda: self.openfile())
self.open.place(x=self.width - 250, y=20, width=50, height=30)
self.name = CustomText(self, border=3, font=('Arial', 13))
self.name.place(x=self.width - 360, y=21, width=100, height=30)
def openfile(self):
if self.state.get() == "unsaved":
choise = alert("your code is unsaved, save now?")
if choise == "Ok":
create(self.name.get("1.0", "end-1c"), ".py", self.input.get("1.0", "end-1c"))
self.state.set("saved")
self.input.delete("1.0", "end")
self.input.insert("end-1c", openfile(choosefile())) # here choosefile is called.
if __name__ == "__main__":
app = App()
keyboard.add_hotkey("ctrl+enter", lambda: [keyboard.write("\b"), Execute(app.input.get("1.0", "end"))])
app.mainloop()
can someone help? thanks in advance!
EDIT:
i tried to implemant what acw1668 commented and it works! what i've done: in all my functions, i added a parameter master, so that i at any place in my project can use tk.Toplevel(master). now, since i only one instance of tk.Tk() have, it is working correctly.
I am trying to use a tkinter Toplevel window with a Text box to post to a Text box in the main window, but I keep getting the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\someone\ouch\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "<ipython-input-14-df46ff30dac4>", line 21, in GetFromDIALOG
X = littletext.get("1.0","end")
NameError: name 'littletext' is not defined
In my script below, I call the Text widget in the Toplevel "littletext" and the Text widget in the main window "BIGTEXT". I also tried setting the Text boxes as StringVars, as most tutorials and scripts seem to suggest is right, but StringVars seem to work more on Entry (not Text) boxes.
I am wondering if I should use a custom class instead of a Toplevel, although I am not sure how I would handle that would work either.
My code is below.
import tkinter as tk
def openDIALOG(window):
DIALOG = tk.Toplevel(window)
DIALOG.title("DIALOG")
DIALOG.geometry("400x300+100+100")
littletext = tk.Text(DIALOG, height=15)
littletext.pack()
btn1 = tk.Button(DIALOG, text ="POST TO MAIN WINDOW", command=GetFromDIALOG)
btn1.pack(side="left")
btn2 = tk.Button(DIALOG, text ="EXIT", command = DIALOG.destroy)
btn2.pack(side="left")
def GetFromDIALOG ():
X = littletext.get("1.0","end")
print(X)
window = tk.Tk()
window.title("main")
menubar = tk.Menu(window)
filemenu = tk.Menu(menubar, tearoff=0)
filemenu.add_command(label="RUN A DIALOG", command=lambda:openDIALOG(window))
filemenu.add_command(label="Exit", command=window.destroy)
menubar.add_cascade(label="FILE", menu=filemenu)
window.config(menu=menubar)
BIGTEXT = tk.Text(window)
BIGTEXT.pack()
BUTTON = tk.Button(window, text="Post", command=openDIALOG)
BUTTON.pack()
window.mainloop()
One way to fix the NameError, which is due it it being a local variable in the openDIALOG() function in your code, is to make it an attribute of instances of the class. This is better than using a global variable, which is another option, but global variables are bad.
Stackoverflow is not intended to replace existing tutorials or documentation, so I'll describe only very briefly what the demo code below it doing. This topic would be covered much more thoroughly in most of the Python tutorials that are available online, if you took time to for them.
So, here's how that might be done based on the code in your question. Notice how littletext is now self.littletext. This is because it has been turned into a class attribute of the class MY_DIALOG that's been defined, so it can be access through the self argument which will automatically be passed as the first argument of all the class' functions (which are know as class "methods").
import tkinter as tk
from tkinter.constants import *
class MyDialog:
def __init__(self, window):
DIALOG = tk.Toplevel(window)
DIALOG.title("DIALOG")
DIALOG.geometry("400x300+100+100")
self.littletext = tk.Text(DIALOG, height=15)
self.littletext.pack()
btn1 = tk.Button(DIALOG, text="POST TO MAIN WINDOW", command=self.GetFromDIALOG)
btn1.pack(side="left")
btn2 = tk.Button(DIALOG, text="EXIT", command=DIALOG.destroy)
btn2.pack(side="left")
def GetFromDIALOG(self):
X = self.littletext.get("1.0", END)
print(X)
if __name__ == '__main__':
window = tk.Tk()
window.title("main")
menubar = tk.Menu(window)
filemenu = tk.Menu(menubar, tearoff=0)
filemenu.add_command(label="RUN A DIALOG", command=lambda: MyDialog(window))
filemenu.add_command(label="Exit", command=window.destroy)
menubar.add_cascade(label="FILE", menu=filemenu)
window.config(menu=menubar)
BIGTEXT = tk.Text(window)
BIGTEXT.pack()
BUTTON = tk.Button(window, text="Post", command=lambda: MyDialog(window))
BUTTON.pack()
window.mainloop()
Thanks martineau! :)
There was a few small problems I cleaned up.
The dialog you coded didnt post back to BIGTEXT in the main window, so I completed the callback aspect of it.
The BUTTON in the footer of the main window was actually a mistake on my part. I accidentally forgot to delete it in the original post, so I deleted it here and let the menu work the call.
Final code is below.
import tkinter as tk
from tkinter.constants import *
class MY_DIALOG:
def __init__(self, window):
DIALOG = tk.Toplevel(window)
DIALOG.title("DIALOG")
DIALOG.geometry("400x300+100+100")
self.littletext = tk.Text(DIALOG, height=15)
self.littletext.pack()
btn1 = tk.Button(DIALOG, text="POST TO MAIN WINDOW", command=self.GetFromDIALOG)
btn1.pack(side="left")
btn2 = tk.Button(DIALOG, text="EXIT", command=DIALOG.destroy)
btn2.pack(side="left")
def GetFromDIALOG(self):
X = self.littletext.get("1.0","end")
BIGTEXT.insert("end", X)
self.littletext.delete("1.0","end")
if __name__ == '__main__':
window = tk.Tk()
window.title("main")
menubar = tk.Menu(window)
filemenu = tk.Menu(menubar, tearoff=0)
filemenu.add_command(label="RUN A DIALOG", command=lambda: MY_DIALOG(window))
filemenu.add_command(label="Exit", command=window.destroy)
menubar.add_cascade(label="FILE", menu=filemenu)
window.config(menu=menubar)
BIGTEXT = tk.Text(window)
BIGTEXT.pack()
window.mainloop()
I'm trying to implement a TicTacToe program. I am an absolute beginner in python. After viewing many tutorials and reading a few books, I have understood the basics of Python. I'm trying to get the buttons to display in a frame, but all I get is a blank window.
link for image of the resultant window
This is the code I have so far:
from Tkinter import *
class Buttons(object):
def __init__(self,master):
frame = Frame(master)
frame.pack()
self.button1= Button(frame,text="1",height=4,width=8,command=self.move)
self.button1.pack(side=LEFT)
self.button2= Button(frame,text="2",height=4,width=8,command=self.move)
self.button2.pack(side=LEFT)
self.button3= Button(frame,text="3",height=4,width=8,command=self.move)
self.button3.pack(side=LEFT)
root = Tk()
root=mainloop()
You defined your Buttons class but you didn't create an instance of that class, so no buttons were actually constructed. Also, you had a typo / syntax error:
root=mainloop()
should be
root.mainloop()
Also, you didn't define the move callback method.
Here's a repaired version of your code:
from Tkinter import *
class Buttons(object):
def __init__(self,master):
frame = Frame(master)
frame.pack()
self.button1 = Button(frame, text="1", height=4, width=8, command=self.move)
self.button1.pack(side=LEFT)
self.button2 = Button(frame, text="2", height=4, width=8, command=self.move)
self.button2.pack(side=LEFT)
self.button3 = Button(frame, text="3", height=4, width=8, command=self.move)
self.button3.pack(side=LEFT)
def move(self):
print "click!"
root = Tk()
Buttons(root)
root.mainloop()
However, this still has a problem: The move method has no way of knowing which button called it. Here's one way to fix that. I've also changed
from Tkinter import *
to
import tkinter as tk
It's not a good idea to use "star" imports. They make code harder to read and they pollute your namespace with all the names defined in the imported module (that's 175 names in the case of Tkinter), which can lead to name collisions.
import Tkinter as tk
class Buttons(object):
def __init__(self,master):
frame = tk.Frame(master)
frame.pack()
self.buttons = []
for i in range(1, 4):
button = tk.Button(
frame, text=i, height=4, width=8,
command=lambda n=i:self.move(n)
)
button.pack(side=tk.LEFT)
self.buttons.append(button)
def move(self, n):
print "click", n
root = tk.Tk()
Buttons(root)
root.mainloop()
Okay the problem was I needed to add a variable at the end of the code. Something like b=Buttons(root). It's working now.
My code worked fine without OOP,
Now I am trying to implement it in OOP, but somehow cause some errors.
Could someone help please?
BTW the error is:
File "C:\Python27\lib\lib-tk\Tkinter.py", line 2059, in _setup
self.tk = master.tk
AttributeError: class MainInterface has no attribute 'tk'"
from Tkinter import *
import tkFont
class MainInterface:
def __init__(self, master):
self.master = master
# Main interface created
master.title("Dijkstra's Algorithm Solution Demonstrator")
# Title for the main interface
self.TNR24 = tkFont.Font(family="Times New Roman", size=24, weight="bold")
self.TNR28 = tkFont.Font(family="Times New Roman", size=28, weight="bold")
# Two font types that can be used later
master.overrideredirect(True)
master.geometry("{0}x{1}+0+0".format(master.winfo_screenwidth(), master.winfo_screenheight()))
# Formats the interface so that it would be in full screen
self.MainTitle = Label(MainInterface, text="Dijkstra's Algorithm Solution Demonstrator", bg="white", font=self.TNR28)
self.InterfaceMenu = Menu(MainInterface)
self.MainInterface.config(menu=self.InterfaceMenu)
self.submenu1 = Menu(self.InterfaceMenu)
self.InterfaceMenu.add_cascade(label="File", menu=self.submenu1)
self.submenu2 = Menu(self.InterfaceMenu)
self.InterfaceMenu.add_cascade(label="Help", menu=self.submenu2)
self.submenu2.add_command(label="details")
self.MainInterfaceButton1 = Button(MainInterface, text="Input New Question", font=self.TNR24, command=create_window)
self.MainInterfaceButton2 = Button(MainInterface, text="Load Up Existing Question", font=self.TNR24)
self.MainInterfaceButton3 = Button(MainInterface, text="Identify Incorrectly Applied Algorithms", font=self.TNR24)
# Three main buttons created for that interface
self.ExitButton = Button(MainInterface, text="Exit", command=self.exit_window)
# Exit button created
self.MainTitle.pack()
# Packs the main title onto the interface
self.MainInterfaceButton1.place(x=340, y=200, width=600, height=100)
self.MainInterfaceButton2.place(x=340, y=350, width=600, height=100)
self.MainInterfaceButton3.place(x=340, y=500, width=600, height=100)
self.ExitButton.place(x=1240, y=670, width=40, height=30)
# Places the buttons at desirable places
self.MainInterface.configure(background="white")
# Makes the background colour white
def exit_window(self):
self.quit()
# Function for existing the interface
def main():
root=Tk()
Lookatthis=MainInterface(root)
root.mainloop
if __name__ == "__main__":
main()
Probably you have problem because you have to use master instead of MainInterface in Label, Button, etc.
... = Label(master, ... )
... = Button(master, ... )
... = Menu(master, ... )
-
MainInterface is not Tkinter widget and it can't be parent (master) for other widgets.
I am making a program using Tkinter that will create a list of events. I used a class to make an event. If I click the Add Event button (second script shown), it creates a new instance of the class, lengthening the list. However, I also want to be able to remove events from the list. I am trying to attach a remove button to each class that, when clicked, will delete the class. This is my code from the class script (classes.py):
from Tkinter import *
class agendaEvent:
def __init__(self, master):
self.frame = Frame(master, padx=10, pady=10)
self.frame.pack(side=TOP)
self.name = Entry(self.frame)
self.name.grid(row=1, column=0)
self.time = Entry(self.frame, width=10)
self.time.grid(row=1, column=1, padx=5)
self.label1 = Label(self.frame, text="Event Name")
self.label1.grid(row=0, column=0)
self.label2 = Label(self.frame, text="Minutes")
self.label2.grid(row=0, column=1)
self.remove = Button(self.frame, text="Remove", command=agendaEvent.remove)
self.remove.grid(row=1, column=3)
def remove(agendaEvent):
del agendaEvent
When I press the remove button, I get the error
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1532, in __call__
return self.func(*args)
TypeError: unbound method remove() must be called with agendaEvent instance as first argument (got nothing instead)
How can I call the instance of agendaEvent? Or is there a better way of going about this? This is the code for the main script (main.py):
from Tkinter import *
import classes
def addEvent():
classes.agendaEvent(root)
root = Tk()
addEventButton = Button(root, text="Add Event", command=addEvent)
addEventButton.pack(side=BOTTOM)
root.mainloop()
You can remove labels and other widgets (not sure all) with .destroy(). If you store the instances of the class in some way you can forget or destroy them, but if the instance is just an instance then I don't see your problem (read, I can't help you). If you want to learn how to build GUIs with Tkinter you should check out sentdex's videos on the subject on youtube.
This is how I make tkinter widgets with buttons like I think you are trying to do:
I make a counter variable that counts how many objects my list contains.
I make a button that point to a method or a function that makes a label that contains the info I want it to contain, e.g. .get()
something from an entry box.
The function then adds one entry to the counter variable.
Then I make a button that points to a method or function that destroys the last label, I use the counter variable as index to know
what label to destroy.
I then subtract one from the counter variable
I use a dictionary to keep my labels and other widgets in order, e.g. by using a counter variable as key.
Example:
from tkinter import *
class MyEvent("something tkinter"):
def __init__(self, parent):
"here you need some tkinter code to make a frame or something to put your widgets in"
self.mycounter = 0
self.myEventLabel = {}
addButton = Button(parent, text="add event", command=addEvent).pack()
destroyButton = Button(parent, text="remove last event", command=removeEvent).pack()
def addEvent(self):
self.myEventLabel[self.mycounter] = Label("Here goes the options you want)
self.mycounter+=1
def removeEvent(self):
self.mycounter-=1
self.myEventLabel[self.mycounter].destroy()
Hope this helped, if I missed the point, then in my defense I'm not a programmer, just someone who needs to use programming as a means to an end. Again, Sentdex's videos covers alot of tkinter, check them out.
You have to save the class instance to remove it. Below, addEvent() has been changed to catch the return from calling the class, and that instance is attached to the button so the button knows which instance to destroy. This would better if all of the code was wrapped in classes but I am following the code you posted above which is likely just a simple example.
from Tkinter import *
from functools import partial
class agendaEvent:
def __init__(self, master):
self.frame = Frame(master, padx=10, pady=10)
self.frame.grid()
self.name = Entry(self.frame)
self.name.grid(row=1, column=0)
self.time = Entry(self.frame, width=10)
self.time.grid(row=1, column=1, padx=5)
self.label1 = Label(self.frame, text="Event Name")
self.label1.grid(row=0, column=0)
self.label2 = Label(self.frame, text="Minutes")
self.label2.grid(row=0, column=1)
def addEvent(master):
this_instance=agendaEvent(master)
## add the button to the frame created by this function call
## and not to the root, so it also is destroyed
rem = Button(this_instance.frame, text="Remove", bg="lightblue",
command=partial(remove_class, this_instance))
rem.grid(row=6, column=0)
def remove_class(instance):
## destroys the frame created by the instance
## i.e. self.frame in agendaEvent
instance.frame.destroy()
instance="" ## reassigns "instance" so the class is garbage collected
root = Tk()
addEventButton = Button(root, text="Add Event", command=partial(addEvent, root))
addEventButton.grid(row=5, column=0)
Button(root, text="Exit", bg="orange", command=root.quit).grid(row=99, column=0)
root.mainloop()
Changing your class as shown below will fix the error you're currently getting. It's caused by the fact that the handler specified with a command keyword argument passed to the Button constructor is always called without any arguments. Fixing that with a lambda function then exposes another problem, which is that you've named both the Button instance and the method you want to associated it with the same thing – remove – so I renamed it to remove_button.
from Tkinter import *
class agendaEvent:
def __init__(self, master):
self.frame = Frame(master, padx=10, pady=10)
self.frame.pack(side=TOP)
self.name = Entry(self.frame)
self.name.grid(row=1, column=0)
self.time = Entry(self.frame, width=10)
self.time.grid(row=1, column=1, padx=5)
self.label1 = Label(self.frame, text="Event Name")
self.label1.grid(row=0, column=0)
self.label2 = Label(self.frame, text="Minutes")
self.label2.grid(row=0, column=1)
self.remove_button = Button(self.frame, text="Remove",
command=lambda: self.remove())
self.remove_button.grid(row=1, column=3)
def remove(self): # by convention the first argument to a class
del self # method is usually named "self"