Closing a window in python 3.7.2 with tkinter - python

I've made a program in python with Tkinter that allows you to free draw and choose different colors. I decided to make a button that would close the window instead of clicking the exit button in the top right corner. My question is how do I make the window close when the button is pressed?

If you are using a main loop for your application, then you can use the .destroy() method to release all the resources associated with the window and close the application. You call this method within the command function for your button like so:
from tkinter import *
root = Tk()
frame = Frame(root)
frame.pack(side=LEFT)
button = Button(frame, text="Exit", command=exit)
button.pack()
root.mainloop()
def exit():
root.destroy()
That should close your window. Optionally, the destroy() method may also be used at the end of your main loop if the X button of your application won't close the window immediately.
See these examples for more info:
http://effbot.org/tkinterbook/widget.htm#Tkinter.Widget.destroy-method
http://effbot.org/tkinterbook/tkinter-hello-again.htm

Related

tkinter wiget.bind('<Activate>', callback) not working

I've been trying to create a message box when a button turns from disabled to active but when the button actually turns from disabled to active for some reason my callback is not even being called I've tried to get it working so for quite a bit of time now and I'm stuck.
Here is an example of the problem:
from tkinter import *
from tkinter import Tk
def disable_and_activate():
b.config(state = DISABLED)
b.config(state = ACTIVE)
def is_working(event):
print('working')
root = Tk()
b = Button (root, text = 'click me', command = disable_and_activate)
b.pack()
b.bind('<Activate>', is_working)
root.mainloop()
Console:
the button is clicked but there's nothing printed on the console
The <Activate> event is not triggered when you set the state of the button to "active". The event is triggered when the window becomes the active window.
For example, when I run your code on my OSX machine, if I click on some other application to give it focus and then I click back to the tkinter window, the event will fire when the tkinter window becomes the active window.
This is explained in the canonical tcl/tk documentation which says this:
Activate, Deactivate
These two events are sent to every sub-window of a toplevel when they change state. In addition to the focus Window, the Macintosh platform and Windows platforms have a notion of an active window (which often has but is not required to have the focus). On the Macintosh, widgets in the active window have a different appearance than widgets in deactive windows. The Activate event is sent to all the sub-windows in a toplevel when it changes from being deactive to active. Likewise, the Deactive event is sent when the window's state changes from active to deactive. There are no useful percent substitutions you would make when binding to these events.
Here the problem was just the code inside the function , It seems like you needed to call EventGenerate('<<Activate>>') I also recommend adding 2 << and 2 >>
So I rewrote the code and its now working perfectly fine:
from tkinter import *
from tkinter import Tk
import tkinter
def disable_and_activate():
b.configure(state=tkinter.DISABLED)
b.configure(state=tkinter.ACTIVE)
b.event_generate("<<Activate>>")
def is_working(event):
print('working')
root = Tk()
b = Button (root, text = 'click me', command = disable_and_activate)
b.pack()
b.bind('<<Activate>>', is_working)
root.mainloop()

Is there a way to make a custom askopenfilenames dialog window or at least edit its title bar, close button, etc in tkinter?

I'm making an app in tkinter which uses the ttk.Scale widget to show the process of an mp3 song.
I have a function that I want to add buttons with the names of which (the buttons) should be relied on filenames. Therefore I've made this example:
from tkinter import Tk, Button
from tkinter.filedialog import askopenfilenames
from tkinter.ttk import Scale
from threading import Timer
root = Tk()
slider = Scale(root, from_=0, to=100, orient='horizontal')
slider.pack()
# slider is continuously set to a bigger number so that it keeps going
def update_slider(num):
slider.set(num)
num += 1
root.after(50, update_slider, num)
update_slider(num=0)
# this function creates buttons based on the files opened
def add_buttons():
# the 'X' button of this particular window slows down execution of update_slider function
files = askopenfilenames(title='Add Buttons')
for i in list(files):
Button(root, text=i).pack()
button = Button(root, text='Browse', command=lambda: Timer(0.1, add_buttons).start())
button.pack()
root.mainloop()
The problem I'm facing is that when I open the askopenfilenames dialog box or when I press its 'X' button, my slider which is running continuously in the background gets stuck, and as a result doesn't show the process correctly.
Here is a picture where I hold down the 'X' button and the ttk.Scale stops moving:
I've tried using threading to run the add_buttons function but the behavior of the program remains the same.
Can I edit the askopenfilenames dialog box with something similar like overrideredirect(True) so that I can make my own title bar and 'X' button and the events generated not to slow down my Scale?
Replying to:
I cannot reproduce the issue in Linux, the scale keeps moving no matter what I do with the filedialog window. So this may be an OS specific issue.
I'm aware that this problem doesn't appear on Linux. I faced the same problem with the root's close button and other Toplevels' close button, but I fixed it by replacing the title bar using overrideredirect(True).
Is there anything similar I can do with this askopenfilenames window?

tkinter: immediately selecting newly opened window

The code is simple. In tkinter I create a button which opens a new window. The difference in the pictures below might be hard to see but if you look closely you can see that in the first picture the root window is selected even though it's behind the new opened window.
In my actual program I use keybindings to operate the second window so it would be nice to instantly select this window so you don't have to click on it to use keys to operate it. How can I select the Toplevel window as soon as it opens?
from tkinter import *
def open_new_window():
top = Toplevel(root)
root = Tk()
Button(root, text="open new window", command=open_new_window).pack()
root.mainloop()
Possible duplicate of this question
Like acw1668 said, simply add top.focus() at the end of your function open_new_window
Your new open_new_window function will look like this:
def open_new_window():
top = Toplevel(root)
top.focus()

Find Out When A Tkinter Window Quits [duplicate]

How do I handle the window close event (user clicking the 'X' button) in a Python Tkinter program?
Tkinter supports a mechanism called protocol handlers. Here, the term protocol refers to the interaction between the application and the window manager. The most commonly used protocol is called WM_DELETE_WINDOW, and is used to define what happens when the user explicitly closes a window using the window manager.
You can use the protocol method to install a handler for this protocol (the widget must be a Tk or Toplevel widget):
Here you have a concrete example:
import tkinter as tk
from tkinter import messagebox
root = tk.Tk()
def on_closing():
if messagebox.askokcancel("Quit", "Do you want to quit?"):
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()
Matt has shown one classic modification of the close button.
The other is to have the close button minimize the window.
You can reproduced this behavior by having the iconify method
be the protocol method's second argument.
Here's a working example, tested on Windows 7 & 10:
# Python 3
import tkinter
import tkinter.scrolledtext as scrolledtext
root = tkinter.Tk()
# make the top right close button minimize (iconify) the main window
root.protocol("WM_DELETE_WINDOW", root.iconify)
# make Esc exit the program
root.bind('<Escape>', lambda e: root.destroy())
# create a menu bar with an Exit command
menubar = tkinter.Menu(root)
filemenu = tkinter.Menu(menubar, tearoff=0)
filemenu.add_command(label="Exit", command=root.destroy)
menubar.add_cascade(label="File", menu=filemenu)
root.config(menu=menubar)
# create a Text widget with a Scrollbar attached
txt = scrolledtext.ScrolledText(root, undo=True)
txt['font'] = ('consolas', '12')
txt.pack(expand=True, fill='both')
root.mainloop()
In this example we give the user two new exit options:
the classic File → Exit, and also the Esc button.
Depending on the Tkinter activity, and especially when using Tkinter.after, stopping this activity with destroy() -- even by using protocol(), a button, etc. -- will disturb this activity ("while executing" error) rather than just terminate it. The best solution in almost every case is to use a flag. Here is a simple, silly example of how to use it (although I am certain that most of you don't need it! :)
from Tkinter import *
def close_window():
global running
running = False # turn off while loop
print( "Window closed")
root = Tk()
root.protocol("WM_DELETE_WINDOW", close_window)
cv = Canvas(root, width=200, height=200)
cv.pack()
running = True;
# This is an endless loop stopped only by setting 'running' to 'False'
while running:
for i in range(200):
if not running:
break
cv.create_oval(i, i, i+1, i+1)
root.update()
This terminates graphics activity nicely. You only need to check running at the right place(s).
If you want to change what the x button does or make it so that you cannot close it at all try this.
yourwindow.protocol("WM_DELETE_WINDOW", whatever)
then defy what "whatever" means
def whatever():
# Replace this with your own event for example:
print("oi don't press that button")
You can also make it so that when you close that window you can call it back like this
yourwindow.withdraw()
This hides the window but does not close it
yourwindow.deiconify()
This makes the window visible again
I'd like to thank the answer by Apostolos for bringing this to my attention. Here's a much more detailed example for Python 3 in the year 2019, with a clearer description and example code.
Beware of the fact that destroy() (or not having a custom window closing handler at all) will destroy the window and all of its running callbacks instantly when the user closes it.
This can be bad for you, depending on your current Tkinter activity, and especially when using tkinter.after (periodic callbacks). You might be using a callback which processes some data and writes to disk... in that case, you obviously want the data writing to finish without being abruptly killed.
The best solution for that is to use a flag. So when the user requests window closing, you mark that as a flag, and then react to it.
(Note: I normally design GUIs as nicely encapsulated classes and separate worker threads, and I definitely don't use "global" (I use class instance variables instead), but this is meant to be a simple, stripped-down example to demonstrate how Tk abruptly kills your periodic callbacks when the user closes the window...)
from tkinter import *
import time
# Try setting this to False and look at the printed numbers (1 to 10)
# during the work-loop, if you close the window while the periodic_call
# worker is busy working (printing). It will abruptly end the numbers,
# and kill the periodic callback! That's why you should design most
# applications with a safe closing callback as described in this demo.
safe_closing = True
# ---------
busy_processing = False
close_requested = False
def close_window():
global close_requested
close_requested = True
print("User requested close at:", time.time(), "Was busy processing:", busy_processing)
root = Tk()
if safe_closing:
root.protocol("WM_DELETE_WINDOW", close_window)
lbl = Label(root)
lbl.pack()
def periodic_call():
global busy_processing
if not close_requested:
busy_processing = True
for i in range(10):
print((i+1), "of 10")
time.sleep(0.2)
lbl["text"] = str(time.time()) # Will error if force-closed.
root.update() # Force redrawing since we change label multiple times in a row.
busy_processing = False
root.after(500, periodic_call)
else:
print("Destroying GUI at:", time.time())
try: # "destroy()" can throw, so you should wrap it like this.
root.destroy()
except:
# NOTE: In most code, you'll wanna force a close here via
# "exit" if the window failed to destroy. Just ensure that
# you have no code after your `mainloop()` call (at the
# bottom of this file), since the exit call will cause the
# process to terminate immediately without running any more
# code. Of course, you should NEVER have code after your
# `mainloop()` call in well-designed code anyway...
# exit(0)
pass
root.after_idle(periodic_call)
root.mainloop()
This code will show you that the WM_DELETE_WINDOW handler runs even while our custom periodic_call() is busy in the middle of work/loops!
We use some pretty exaggerated .after() values: 500 milliseconds. This is just meant to make it very easy for you to see the difference between closing while the periodic call is busy, or not... If you close while the numbers are updating, you will see that the WM_DELETE_WINDOW happened while your periodic call "was busy processing: True". If you close while the numbers are paused (meaning that the periodic callback isn't processing at that moment), you see that the close happened while it's "not busy".
In real-world usage, your .after() would use something like 30-100 milliseconds, to have a responsive GUI. This is just a demonstration to help you understand how to protect yourself against Tk's default "instantly interrupt all work when closing" behavior.
In summary: Make the WM_DELETE_WINDOW handler set a flag, and then check that flag periodically and manually .destroy() the window when it's safe (when your app is done with all work).
PS: You can also use WM_DELETE_WINDOW to ask the user if they REALLY want to close the window; and if they answer no, you don't set the flag. It's very simple. You just show a messagebox in your WM_DELETE_WINDOW and set the flag based on the user's answer.
You should use destroy() to close a tkinter window.
from Tkinter import *
root = Tk()
Button(root, text="Quit", command=root.destroy).pack()
root.mainloop()
Explanation:
root.quit()
The above line just Bypasses the root.mainloop() i.e root.mainloop() will still be running in background if quit() command is executed.
root.destroy()
While destroy() command vanish out root.mainloop() i.e root.mainloop() stops.
So as you just want to quit the program so you should use root.destroy() as it will it stop the mainloop()`.
But if you want to run some infinite loop and you don't want to destroy your Tk window and want to execute some code after root.mainloop() line then you should use root.quit().
Ex:
from Tkinter import *
def quit():
global root
root.quit()
root = Tk()
while True:
Button(root, text="Quit", command=quit).pack()
root.mainloop()
#do something
The easiest code is:
from tkinter import *
window = Tk()
For hiding the window : window.withdraw()
For appearing the window : window.deiconify()
For exiting from the window : exit()
For exiting from the window(If you've made a .exe file) :
from tkinter import *
import sys
window = Tk()
sys.exit()
And of course you have to place a button and use the codes above in a function so you can type the function's name in the command part of the button
Try The Simple Version:
import tkinter
window = Tk()
closebutton = Button(window, text='X', command=window.destroy)
closebutton.pack()
window.mainloop()
Or If You Want To Add More Commands:
import tkinter
window = Tk()
def close():
window.destroy()
#More Functions
closebutton = Button(window, text='X', command=close)
closebutton.pack()
window.mainloop()
you can use:
root = Tk()
def func():
print('not clossed')
root.protocol('wm_delete_window', func)
root.mainloop()
def on_closing():
if messagebox.askokcancel("Quit", "would you like to quit"):
window.destroy()
window.protocol("WM_DELETE_WINDOW", on_closing)
you can handle a window close event like this, if you wanna do something else just change the things that happen in the on_closing() function.
i say a lot simpler way would be using the break command, like
import tkinter as tk
win=tk.Tk
def exit():
break
btn= tk.Button(win, text="press to exit", command=exit)
win.mainloop()
OR use sys.exit()
import tkinter as tk
import sys
win=tk.Tk
def exit():
sys.exit
btn= tk.Button(win, text="press to exit", command=exit)
win.mainloop()

Run function from Python 2 Tkinter button when pressed then run another when released

Hello I am trying to make a simple recorder in Python 2.7 using Tkinter as the GUI, I want to be able to record when the button is pressed then save the recording when the button is released, I know how to make the button and have already done so, but I don't know how to make it run a program when pressed and another when released, is it possible?
Also I'm not sure how to actually record from the microphone and save it using pyaudio, any help with this is appreciated but I'm sure I can figure this out myself when I have overcome the main issue.
You can bind an event to the click of the left mouse button <Button-1> and to the release of the left mouse button <ButtonRelease-1>. Here's an example:
import Tkinter as tk
root = tk.Tk()
def clicked(event):
var.set('Clicked the button')
def released(event):
var.set('Released the button')
var = tk.StringVar()
var.set('Nothing to see here')
label = tk.Label(root, textvar=var)
label.pack()
but = tk.Button(root, text='Button')
but.bind("<Button-1>", clicked)
but.bind("<ButtonRelease-1>", released)
but.pack()
root.mainloop()

Categories

Resources