How to end Tkinter propbably if not using the Quit button - python

I tried to find out a solution for my problem, but I couldn't find one.
I am using Python27 on Windows 7.
I have an easy Tkinter GUI with a button:
import Tkinter
import sys
def close_window():
root.destroy()
sys.exit()
root = Tkinter.Tk()
#exit button
draw_button = Tkinter.Button(root, text="Quit", command = close_window)
draw_button.grid(row=1, column=1)
root.mainloop()
Now if I use the Quit button the program closes and there is not task left of the program. The problem is if someone uses the X-Button to close the Windows or for examples uses Alt+F4, the task is still running.
For later use I freeze the script to make an executable and if someone uses some method to close the program except the Quit button the task is still running. And if the task is still running he or she can't open the program again, because it is still running in the background and Windows raise an error that the program is still running.
I tried to add some commands after the mainloop but they are all ignored. How can I solve this problem?
Thanks for your help! Max

What about using WM_DELETE_WINDOW. For example:
import tkinter
import sys
def close_window():
root.destroy()
sys.exit()
def win_deleted():
print("closed");
close_window();
root = tkinter.Tk()
#exit button
draw_button = tkinter.Button(root, text="Quit", command = close_window)
draw_button.grid(row=1, column=1)
root.protocol("WM_DELETE_WINDOW", win_deleted)
root.mainloop()
This will close app with ctr+F4.

Related

Exit Python Tkinter app cleanly while also using "wait_variable" function on a button in an "after" loop

I have a problem similar to this post: Exit program within a tkinter class
My variation on the problem involves the wait_variable being used on a button to control "stepping forward" in an app, but also allowing the app to close cleanly.
See my code below:
# To see output unbuffered:
# python -u delete_win_test.py
import tkinter as tk
from tkinter import *
class GUI(Tk):
def __init__(self):
super().__init__()
# Close the app when the window's X is pressed
self.protocol("WM_DELETE_WINDOW", self.closing)
# When this var is set to 1, the move function can continue
self.var = tk.IntVar()
# Close the app if the button is pressed
button = tk.Button(self, text="Exit",
command=self.destroy)
button.place(relx=.5, rely=.5, anchor="c")
# Step forward
self.step_button = tk.Button(self, text="Step",
command=lambda: self.var.set(1))
self.step_button.place(relx=.5, rely=.75, anchor="c")
def move(self):
print("doing stuff") # simulates stuff being done
self.step_button.wait_variable(self.var)
self.after(0, self.move)
def closing(self):
self.destroy()
app = GUI()
app.move()
app.mainloop()
The window shows correctly
"Stepping forward" works because "doing stuff" prints to terminal on button click
Exiting the window by both pressing X or using the "exit" button both work
The problem: the Python app never exits from the terminal and requires a closing of the terminal.
How can I make the Python program exit cleanly so the user does not need to close and re-open a new terminal window?
Related references for animation, etc:
Animation using self.after: moving circle using tkinter
Button wait: Making Tkinter wait untill button is pressed
The original "exit" code: Exit program within a tkinter class
UPDATE (the solution):
(Credit to both response answers below)
# Close the app if the button is pressed
button = tk.Button(self, text="Exit",
- command=self.destroy)
+ command=self.closing)
button.place(relx=.5, rely=.5, anchor="c")
# Step forward
...
def closing(self):
self.destroy()
+ self.var.set("")
+ exit(0)
This allows the native window's "X" to close the window and the Tk button to close the window while still closing the Python app cleanly in the terminal.
Your closing function needs to set the variable to cause the app to stop waiting.
def closing(self):
self.destroy()
self.var.set("")
in the closing function, you need to call exit to exit the program.
def closing(self):
self.destroy() #closes tkinkter window
exit(0) #exits program

Open window on button click, close current window?

I have been searching for a solution for this issue, but I can't seem to find a viable answer.
I have the following code to open another tkinter script on button click:
# Program to Open on Button Click
def tkinter1():
ret = os.system('python C:\filepath\new_script.py"')
if ret:
# record updated, reload data
treeview_preview()
b1 = Button(master, text="New Window", command=tkinter1)
My problem I am facing is that I want the current window to close on the button click and only keep the new window open.
I know it is possible. I have this instance with many different windows and I seem to be stuck.
The unfortunate thing is that I have an entire different script for different parts of the beta software and the only way I could successfully run all of them is to access them as stated above.
I tried using the if ret: exit() command at the end with the same result. I have windows opening over and over again.
It seems simple, but I haven't been programming tkinter script for too long.(I still have a lot to learn)
All help is appreciated.
You can use master.withdraw(). I
made your code runnable with a few lines. Ok lets say your fixed main code is this:
import tkinter as tk
from tkinter import *
from seccond import *
from multiprocessing import Process
import os
def main():
master = tk.Tk()
# Program to Open on Button Click
def tkinter1():
master.withdraw()
master.destroy()
ret = Process(target=treeview_preview())
ret.start()
#if ret:
# # record updated, reload data
# treeview_preview()
#commented out bc i don't know what it is
b1 = Button(master, text="New Window", command=tkinter1)
b1.pack()
master.mainloop()
if __name__ == '__main__':
main()
the secondary code name seccond.py in the same folder as the main code is called as an external function but also as a separate process.
import tkinter as tk
def treeview_preview():
root = tk.Tk()
S = tk.Scrollbar(root)
T = tk.Text(root, height=4, width=50)
S.pack(side=tk.RIGHT, fill=tk.Y)
T.pack(side=tk.LEFT, fill=tk.Y)
S.config(command=T.yview)
T.config(yscrollcommand=S.set)
quote = """this is the seccond window."""
T.insert(tk.END, quote)
tk.mainloop()
The code will remove the window but not the terminal box as it will use it for the second script.

tkinter user non closeable window

I am trying to write a program where i have removed the main window close options and providing a exit button to the user to close the program.
After pressing i need to do some processing in the background which would be time consuming, i don't want user to close the program while that is going on accidentally. Is there a way to remove all buttons from the messagebox which is presented ?
import tkinter as tk
from win32api import GetSystemMetrics
from tkinter import messagebox
def on_closing():
pass
def exit():
messagebox.showinfo("Wait", "Please wait for background process to complete")
root.destroy()
root = tk.Tk()
root.resizable(width=False, height=False)
root.protocol("WM_DELETE_WINDOW", on_closing)
width = GetSystemMetrics(0)
height = GetSystemMetrics(1)
root.geometry('{}x{}'.format(width,height))
exitButton = tk.Button(root,text="Exit",width=15,command=exit)
exitButton.grid(row=0,column=1,padx=6,pady=6)
root.overrideredirect(True)
root.mainloop()
In the Background : There are some files generated on user's machine and i would like to archive them using python library. The files can go maybe sometime at 1GB so i think it would take more amount of time, if the laptop on which it is run is having very less computing power. And this would be the case for my base hence i want them just to wait until that popup is closed. This i can define in user manual.
I am not sure what work you want to do, but for this example I'm doing a work of printing something and then sleeping and then printing it. So this takes about 20 seconds. And in those 20 seconds you wont be able to exit the GUI.
import tkinter as tk
from win32api import GetSystemMetrics
from tkinter import messagebox
import time
import threading
def on_closing():
if started == False: #if work is not going on, then quit
root.destroy()
else: # else show the message.
messagebox.showinfo("Wait", "Please wait for background process to complete")
def work():
global started
started = True #mentioning that the work started
print('Hey')
time.sleep(5)
print('There')
time.sleep(5)
print('Whats Up')
time.sleep(5)
print('Cool?')
time.sleep(5)
started = False #mentioning that the work stopped
started = False #initially work is not started
root = tk.Tk()
root.resizable(width=False, height=False)
root.protocol("WM_DELETE_WINDOW", on_closing)
width = GetSystemMetrics(0)
height = GetSystemMetrics(1)
root.geometry('{}x{}'.format(width,height))
exitButton = tk.Button(root,text="Exit",width=15,command=on_closing)
exitButton.grid(row=0,column=1,padx=6,pady=6)
Button = tk.Button(root,text="Work",width=15,command=threading.Thread(target=work).start)
Button.grid(row=1,column=1,padx=6,pady=6)
# root.overrideredirect(True)
root.mainloop()
Here, started acts like a flag. You have to set it to True before starting your work and set it to False after it ends.
You can ignore the fact that I created a new button and used threading, it was just to simulate to you an example of work done. Threading helps the GUI to not freeze. Though I'm not sure if this will work with root.overrideredirect(True), but I think you can get rid of it.

Program not running when put through pyinstaller

I'm having a problem with pyinstaller when I use exit, an error appears and the program cannot run. When I use quit or sys.exit(), the program does not run. If I leave all of these out and use the X button, the threads keep running in the task manager. What function should I use to fix this?
Needed Code
def exitgame():
sys.exit()
main = Tk()
main.title("Fruit Clicker")
main.geometry("400x350+300+100")
Button(main, text="Play", command=rootopen).pack()
Button(main, text="Quit Game", command=exitgame).pack()
Thanks in advance
Instead of writing function to quit a application, you can close application directly without a function, just replace "command=exitgame" to "command=main.destroy" for 'Quit game button'.
Sample code-
from tkinter import *
from tkinter.ttk import *
main = Tk()
main.title("Fruit Clicker")
main.geometry("400x350+300+100")
Button(main, text="Quit Game", command=main.destroy).pack()
mainloop()

Invalid command name "exit" while executing "exit"

When I was executing some code on my computer (macOS Sierra, 10.12.3,) I couldn't quit the program in the second window using the 'quit' in the dock or with command-Q. I got this feedback
invalid command name "exit"
while executing
"exit"
from PyCharm.
I tried running the same script in IDLE (Python 3.6) and Terminal (Python 2.7), and they all produced the same problem (although there was no print out from IDLE).
No matter how many times I press command-Q, the window was still there, although I can still quit the window using window manager. Can someone please tell me why was the problem? The following code is the simplest version that can reproduce the problem:
#!/usr/bin/env python
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
first = tk.Tk()
first_frame = tk.Frame(first, height=10, width=200)
first.wm_title("")
frame = tk.Frame(first)
tk.Button(frame, text="Next", command=lambda: first.destroy()).pack(side=tk.RIGHT)
tk.Button(frame,text="Quit",command=lambda: quit()).pack(side=tk.LEFT)
frame.pack(fill=tk.X)
first.protocol("WM_DELETE_WINDOW", lambda: quit())
first.mainloop()
root = tk.Tk()
root.attributes('-topmost', True)
root.title('')
test=tk.Label(root,text='test')
test.pack()
root.mainloop()
I believe the Cmd-Q (i.e. the "Python" menu) gets instantiated by the first mainloop.
I have the following code snippet that induces the same same behavior:
def x_out():
if tkMessageBox.askokcancel("Quit", "Do you really wish to quit?"):
root.destroy()
menu = Menu(root)
root.config(menu=menu)
filemenu = Menu(menu)
menu.add_cascade(label="File", menu=filemenu)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=x_out)
If I File -> Exit then Cmd-Q, I get the same behavior as you. If I just Cmd-Q, no problem. It seems the solution would be to make sure that your first.mainloop() is closed LAST. i.e. don't call first.destroy() until after root.destroy()

Categories

Resources