How to close a Tkinter window by pressing a Button? - python

Write a GUI application with a button labeled "Good-bye". When the
Button is clicked, the window closes.
This is my code so far, but it is not working. Can anyone help me out with my code?
from Tkinter import *
window = Tk()
def close_window (root):
root.destroy()
frame = Frame(window)
frame.pack()
button = Button (frame, text = "Good-bye.", command = close_window)
button.pack()
window.mainloop()

With minimal editing to your code (Not sure if they've taught classes or not in your course), change:
def close_window(root):
root.destroy()
to
def close_window():
window.destroy()
and it should work.
Explanation:
Your version of close_window is defined to expect a single argument, namely root. Subsequently, any calls to your version of close_window need to have that argument, or Python will give you a run-time error.
When you created a Button, you told the button to run close_window when it is clicked. However, the source code for Button widget is something like:
# class constructor
def __init__(self, some_args, command, more_args):
#...
self.command = command
#...
# this method is called when the user clicks the button
def clicked(self):
#...
self.command() # Button calls your function with no arguments.
#...
As my code states, the Button class will call your function with no arguments. However your function is expecting an argument. Thus you had an error. So, if we take out that argument, so that the function call will execute inside the Button class, we're left with:
def close_window():
root.destroy()
That's not right, though, either, because root is never assigned a value. It would be like typing in print(x) when you haven't defined x, yet.
Looking at your code, I figured you wanted to call destroy on window, so I changed root to window.

You could create a class that extends the Tkinter Button class, that will be specialised to close your window by associating the destroy method to its command attribute:
from tkinter import *
class quitButton(Button):
def __init__(self, parent):
Button.__init__(self, parent)
self['text'] = 'Good Bye'
# Command to close the window (the destory method)
self['command'] = parent.destroy
self.pack(side=BOTTOM)
root = Tk()
quitButton(root)
mainloop()
This is the output:
And the reason why your code did not work before:
def close_window ():
# root.destroy()
window.destroy()
I have a slight feeling you might got the root from some other place, since you did window = tk().
When you call the destroy on the window in the Tkinter means destroying the whole application, as your window (root window) is the main window for the application. IMHO, I think you should change your window to root.
from tkinter import *
def close_window():
root.destroy() # destroying the main window
root = Tk()
frame = Frame(root)
frame.pack()
button = Button(frame)
button['text'] ="Good-bye."
button['command'] = close_window
button.pack()
mainloop()

You can associate directly the function object window.destroy to the command attribute of your button:
button = Button (frame, text="Good-bye.", command=window.destroy)
This way you will not need the function close_window to close the window for you.

from tkinter import *
window = tk()
window.geometry("300x300")
def close_window ():
window.destroy()
button = Button ( text = "Good-bye", command = close_window)
button.pack()
window.mainloop()

You can use lambda to pass a reference to the window object as argument to close_window function:
button = Button (frame, text="Good-bye.", command = lambda: close_window(window))
This works because the command attribute is expecting a callable, or callable like object.
A lambda is a callable, but in this case it is essentially the result of calling a given function with set parameters.
In essence, you're calling the lambda wrapper of the function which has no args, not the function itself.

from tkinter import *
def close_window():
import sys
sys.exit()
root = Tk()
frame = Frame (root)
frame.pack()
button = Button (frame, text="Good-bye", command=close_window)
button.pack()
mainloop()

Related

I get the error _tkinter.TclError: bad window path name ".!button" when I destroy the button

from tkinter import *
master=Tk()
class check:
def __init__(self,root):
self.root=root
self.b1=Button(root,text="Click me",command=self.undo)
self.b2=Button(root,text="Again",command=self.click)
def click(self):
self.b1.place(relx=0.5,rely=0.5)
def undo(self):
self.b1.destroy()
self.b2.place(relx=0.2,rely=0.2)
c=check(master)
c.click()
master.mainloop()
This is my code. I get _tkinter.TclError: bad window path name ".!button" error only when I use destroy method. But I want to delete previous button when another button appears.What should I do?
What are you doing? When you click the "Click me" button (and call the self.undo method, where the self.b1 button is destroyed) and then click the "Again" button (and call the self.click method, which tries to place already destroyed self.b1 button), you get the error, that the button does not exist. Of course, it doesn't because you have destroyed it.
It looks like you meant to hide the button. If you intended to do this, then you could just use .place_forget() method (there are also .pack_forget() and .grid_forget() methods for pack and grid window managers, respectively), that hides the widget, but not destroys it, and hence you would be able to restore it again when you need.
Here is your fixed code:
from tkinter import *
master = Tk()
class check:
def __init__(self, root):
self.root = root
self.b1 = Button(root, text="Click me", command=self.undo)
self.b2 = Button(root, text="Again", command=self.click)
def click(self):
self.b2.place_forget()
self.b1.place(relx=0.5, rely=0.5)
def undo(self):
self.b1.place_forget()
self.b2.place(relx=0.2, rely=0.2)
c = check(master)
c.click()
master.mainloop()
I can also give you a piece of advice about the implementation:
1) You should write the code according to the PEP8 style; classes should be named in the CamelCase.
2) You should inherit your Tkinter app class(es) either from Tk (usage is shown below) Toplevel(the same as Tk, but use ONLY for child windows), Frame class (almost the same as for Tk, but you need to pack/grid/place that Frame in a window).
3) It's better to create the widgets in a separate function (it helps while developing complex and big apps).
4) It's recommended to write if __name__ == "__main__": condition before creating the window (if you do like this, you will be able to import this code from other modules, and the window won't open in that case).
Here is an example:
from tkinter import *
class Check(Tk):
def __init__(self):
super().__init__()
self.create_widgets()
self.click()
def create_widgets(self):
self.b1 = Button(self, text="Click me", command=self.undo)
self.b2 = Button(self, text="Again", command=self.click)
def click(self):
self.b2.place_forget()
self.b1.place(relx=0.5, rely=0.5)
def undo(self):
self.b1.place_forget()
self.b2.place(relx=0.2, rely=0.2)
if __name__ == "__main__":
Check().mainloop()
After you destroyed button b1 in the undo(self) function tkinter cannot access it anymore and will be confused when you try to place is somewhere in the click(self) function.
To make button b1 only disappear visually you could place it outside of the window instead of destroying it. To do so replace
self.b1.destroy()
with
self.b1.place(relx=-5, rely=0)
This will move the button b1 far to the left, where it cannot be seen.
When calling the click(self) function, the button will reappear, because it will be moved inside the window again.

How to close top level tkinter window with a button, if button in the window already is bound to a function

I'm somewhat new to tkinter and Python and am working on a semester long project. Basically I have a main tkinter window, then from that window, topLevel windows are called depending on the user input. In the each topLevel window I have a button that performs a function, I also want this button to close the topLevel window after performing that function. What would be the best way to approach this problem?
I have tried to destroy or close the window, but it ends up closing the main window also. I am just looking for a way to close the topLevel window and perform the function with the click of a button
class MainWindow:
# Entry box
self.entry = StringVar()
self.text_box = Entry(master, textvariable=self.entry)
self.text_box.grid(row=1, column=2)
# Displays and binds button, so when clicked, the enter_button function is called
self.input_button = Button(master, text='Enter', command=self.enter_button)
self.input_button.grid(row=1, column=3, sticky='W')
def enter_button(self):
# Get user input and perform the given command
command = self.entry.get()
# Creates a root for a toplevel window
top = Toplevel()
if command == '1':
add_gui = AddPayment(top)
top.mainloop()
elif command == '2':
#rest of classes/commands
main
def main():
root = Tk()
app = MainWindow(root)
root.mainloop()
if __name__ == '__main__':
main()
AddPayment class
class AddPayment:
def __init__(self,master):
self.master = master
self.add_label = Label(master, text='How much is the payment for?')
# payment box
self.pay = StringVar()
self.pay_box = Entry(master, textvariable=self.pay)
self.add_button = Button(master, text='Add', command=self.add_payment)
# position widgets
self.pay_box.grid(row=1, column=2)
self.add_label.grid(row=1, column=1)
self.add_button.grid(row=1, column=3)
def add_payment(self):
database.add_pay(self.pay.get())
In this example I would like something in the add_payment function to close the topLevel window after the add_pay function is performed somehow. Thanks in advance
You have a couple problems. For one, you should never call mainloop more than once. You need to remove the call to mainloop() from the enter_button function.
The other problem is that you're not saving a reference to the toplevel, so you've made it more-or-less impossible to destroy it. You simply need to save a reference and then call destroy on the reference.
self.top = Toplevel()
...
self.top.destroy()

How to only close TopLevel window in Python Tkinter?

Use the Python Tkinter , create a sub-panel (TopLevel) to show something and get user input, after user inputed, clicked the "EXIT" found the whole GUI (main panel) also destory.
How to only close the toplevel window?
from tkinter import *
lay=[]
root = Tk()
root.geometry('300x400+100+50')
def exit_btn():
top = lay[0]
top.quit()
top.destroy()
def create():
top = Toplevel()
lay.append(top)
top.title("Main Panel")
top.geometry('500x500+100+450')
msg = Message(top, text="Show on Sub-panel",width=100)
msg.pack()
btn = Button(top,text='EXIT',command=exit_btn)
btn.pack()
Button(root, text="Click me,Create a sub-panel", command=create).pack()
mainloop()
This seemed to work for me:
from tkinter import *
lay=[]
root = Tk()
root.geometry('300x400+100+50')
def create():
top = Toplevel()
lay.append(top)
top.title("Main Panel")
top.geometry('500x500+100+450')
msg = Message(top, text="Show on Sub-panel",width=100)
msg.pack()
def exit_btn():
top.destroy()
top.update()
btn = Button(top,text='EXIT',command=exit_btn)
btn.pack()
Button(root, text="Click me,Create a sub-panel", command=create).pack()
mainloop()
Your only mistake is that you're calling top.quit() in addition to calling top.destroy(). You just need to call top.destroy(). top.quit() will kill mainloop, causing the program to exit.
You can't close to root window. When you will close root window, it is close all window. Because all sub window connected to root window.
You can do hide root window.
Hide method name is withdraw(), you can use show method for deiconify()
# Hide/Unvisible
root.withdraw()
# Show/Visible
root.deiconify()
you can use lambda function with the command it's better than the normal function for your work
ex)
btn = Button(top,text='EXIT',command=exit_btn)
change the exit_btn to lambda :top.destroy()
In my case, I passed a callback function from the parent class, and once the submit button is clicked it will the callback function passing the return values.
The callback function will call the destroy method on the top-level object, thus in that way you'll close the frame and have the return value.

Tkinter: Keep from moving focus to window

I am trying to make an on screen keyboard. It works for widgets in the window but when I press a button it moves focus from the window I am trying to type in to the window that has the buttons. How do I prevent python from moving?
from tkinter import *
from pynput.keyboard import Key, Controller
keyboard = Controller()
class App:
def __init__(self, master):
self.entry = Entry()
self.buttonOne = Button(text='1')
self.buttonTwo = Button(text='2')
self.buttonThree = Button(text='3')
self.buttonOne.bind("<Button-1>", lambda event, keyPressed='1': self.pressed(event, keyPressed))
self.buttonTwo.bind("<Button-1>", lambda event, keyPressed='2': self.pressed(event, keyPressed))
self.buttonThree.bind("<Button-1>", lambda event, keyPressed='3': self.pressed(event, keyPressed))
self.entry.grid(row=0, column=0, columnspan=3)
self.buttonOne.grid(row=1, column=0)
self.buttonTwo.grid(row=1, column=1)
self.buttonThree.grid(row=1, column=2)
def pressed(self, event, keyPressed):
keyboard.press(keyPressed)
keyboard.release(keyPressed)
root = Tk()
app = App(root)
root.mainloop()
I would suggest using withdraw() and deiconify(). This will make it so the window with the button is invisible once you call it on that window. Once you use deiconify() it reverses this and makes it visible again.
More information can be found here.
Question: Keep from moving focus to window
On X11, you can set the -type attribute:
self.wm_attributes("-type", 'dock')
'dock' will working for me not to grab the focus, but are not supported by all window managers.
Reference:
wm attributes
Communicate with window manager
A list of types
-type
Requests that the window should be interpreted by the window manager as being of the specified type(s). This may cause the window to be decorated in a different way or otherwise managed differently, though exactly what happens is entirely up to the window manager.
'dock'
indicates a dock/panel feature,
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.wm_attributes("-type", 'dock')
for n in range(64, 68):
btn = tk.Button(self, text=chr(n), takefocus=0)
btn.bind("<Button-1>", self.on_click)
btn.pack()
def on_click(self, event):
w = event.widget
print(w['text'])
if __name__ == '__main__':
App().mainloop()

Efficient way to create a child window with access to main window disabled?

I'm looking for the most efficient way to create a child window and also forbid all access to the main window, in order that user will have to click on "OK" button inside the child window in order to recover the access to the main window.
Here is my code, using Toplevel class. It works, but is there a more efficient way to do ?
from tkinter import *
class MainWindow(Tk):
def __init__(self):
Tk.__init__(self)
def SetEnableStatus(self, status):
for w in self.winfo_children():
if status == False:
w.grab_set()
else:
w.grab_release()
def CreateChildWindow(self):
subWindow = Toplevel(self)
def quit_subwindow():
subWindow.destroy()
self.SetEnableStatus(True) # Enable all widgets of main window
Button(subWindow, text='Exit', command=quit_subwindow).pack()
self.SetEnableStatus(False) # Disable all widgets of main window
It should be enough to call grab_set on the Toplevel object, and when you are done with it, you can simply destroy it, and call grab_set on self (but I am not 100% sure, even if the resulting program below confirms it).
In fact, if you create a button on your Tk root window and if you associate with this button for example a lambda function that prints something, then nothing will be printed, after "setting the grab" on the child window.
See the following example where basically all events are redirected to the Toplevel window, instead of to the Tk root window:
from tkinter import *
class MainWindow(Tk):
def __init__(self):
Tk.__init__(self)
Entry(self).pack(side="left")
Button(self, text="whoami", command=lambda : print("A Tk root window.")).pack(side="left")
def CreateChildWindow(self):
subWindow = Toplevel(self)
def quit_subwindow():
subWindow.destroy()
self.grab_set()
Button(subWindow, text="whoami", command=lambda : print("A Toplevel child window.")).pack()
Button(subWindow, text='Exit', command=quit_subwindow).pack()
subWindow.grab_set()
win = MainWindow()
win.CreateChildWindow()
win.mainloop()
Check out the following article at effbot.org to know more about how to create dialogs:
http://effbot.org/tkinterbook/tkinter-dialog-windows.htm

Categories

Resources