tkinter locking up with sys.exit - python

I was following along in a youtube tutorial:
https://www.youtube.com/watch?v=Zf-b2JVZs7g
When we got to sys.exit(0)
The Python shell seems to be responding but the window I create locks up instead of closing. The other weird thing is that when I run the debugger, the program operates as expected.
#Python 2.7.8
#Idle 2.7.8
#Tkinter 8.5.15
import sys
import Tkinter
import turtle
from Tkinter import *
def main():
root = Tk()
root.title("Draw!")
root.geometry("800x800")
cv = Canvas(root, width=600, height=600)
cv.pack(side=LEFT)
t = turtle.RawTurtle(cv)
screen = t.getscreen()
screen.setworldcoordinates(0,0,600,600)
frame = Frame(root)
frame.pack(side = RIGHT, fill = BOTH)
screen.tracer(0)
def quitHandler():
print ("Goodbye")
sys.exit(0)
##raise SystemExit
##exit()
quitButton = Button(frame, text="Quit", command = quitHandler)
quitButton.pack(side=BOTTOM)
def clickHandler(x,y):
t.goto(x,y)
screen.update()
screen.onclick(clickHandler)
def dragHandler(x,y):
t.goto(x,y)
screen.update()
t.ondrag(dragHandler)
root.mainloop()
if __name__ == "__main__":
main()
In the comments of the video there are two things that stick out to me but I can't figure out why sys.exit(0) isn't working for me like it worked for him:
He's using Python 3
Other people are having this same problem and there aren't any answers that I can see.
My guess is that it's a version problem but (as a n00b) I don't know what the problem is.

Add the line root.destroy() to your quitHandler function. It will clean up the Tkinter process that is otherwise left behind. I tested it and that fixes the issue for me.
(Code removed)
Update
From Terry Jan Reedy's comment:
The details of Python shutdown have changed from version to version,
even within 3.x. I personally would put root.destroy() but not
sys.exit() in a quit handler, and would let Python exit normally after
mainloop() and main() call return. Since printing 'goodbye' does not
print to the gui, and could fail (if you run the app with pythonw on
windows), I would put the print after the main() call.
So it would be
def quitHandler():
root.destroy()
and print at the very end of the code.

You can do it very easily like this:
def quit():
import sys;sys.exit()
widget = Button(root,text="Quit",width=1,fg="red",command=quit).grid(row=1,column=1)
If you use this code you can customize your bottom more.

def quitHandler():
print ("Goodbye")
root.destroy()
sys.exit(0)
quitButton = Button(frame, text="Quit", command = quitHandler)
quitButton.pack(side=BOTTOM)
This will work fine.

Related

Is there a way to clear the printed results and then run a function on the same line?

I am kinda new to python and I want to print a message and then after that delete that message and run a function that displays a list.
Is there a way to do that?
To clear the Python interpreter, you can use os.system() and execute a CLI command to clear the console like that:
import system
os.system('cls' if os.name=='nt' else 'clear')
If you are on Windows, the command would be cls, on Linux it's clear
You can use this code
import os
def func():
print([1,2,3,4])
print("Hello")
os.system("cls")
func()
If You are using a Windows shell, like CMD, it is possible but it is not the best way to do so. Eg:
import os
clear = lambda: os.system('cls')
print("Hello")
input()
clear()
print("World")
This code prints hello in the first line. Then on pressing enter, it clears the terminal and prints world. Though this won't work if you are trying to clear a specific line.
A better way would be to use tkinter.
from tkinter import *
tk = Tk()
label1 = Label(tk, text="Numeric parameters")
label1.pack()
def a():
global label1
label1.configure(text="Hello")
button1 = Button(tk, command=a, text="Click Me")
button1.pack()
tk.mainloop()
This creates a GUI with a Click Me button that changes the text in the label on click.

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.

I am writing a UI for an external program using Tkinter. How could I redirect any print statements to widget in tkinter? Almost like a live log

This is the current UI script I have written...
import tkinter as ttk
import subprocess
import sys
import time
import os
import tkinter.font as font
from tkinter.ttk import *
app = ttk.Tk()
app.geometry("400x400")
app.configure(bg='gray')
photo = ttk.PhotoImage(file=r"C:\Users\ex\ex_button_active.png")
myFont = font.Font(family='Helvetica', size=20, weight='normal')
ttk.Label(app, text='Ex', bg='gray', font=(
'Verdana', 15)).pack(side=ttk.TOP, pady=10)
app.iconbitmap(r'C:\Users\ex\ex_icon.ico')
def ex_activation():
global pro
print("Running program!")
pro = subprocess.Popen("python programex.py", shell=True)
def ex_stop():
global pro
print("Stopping Program... Please Wait!")
os.kill(pro.pid, 0)
ex_activation_button = ttk.Button(app, bg='black', image=photo, width=120, height=120, command=ex_activation)
ex_stop_button = ttk.Button(app, bg='Gray', text='Stop Program', width=12, command=ex_stop, height=3)
ex_stop_button['font'] = myFont
app.title("Ex")
ex_activation_button.pack(side=ttk.TOP)
ex_stop_button.pack(side=ttk.LEFT)
# app.mainloop()
while True:
try:
app.update()
app.update_idletasks()
except KeyboardInterrupt:
pass
The main goal is to take ANY print statements from the external script and have them printed to a tkinter widget line by line. I wanted to use the stdout method. Here is a post I Had Found that May Help Thank you in advance to anyone that can help me achieve this goal.
You could use a ttk.Label() to show the last printed line on your GUI. You could try this:
# Insert a label on your window
lab = ttk.Label(root, text="Some text")
lab.pack()
# Change the label text
def update_label(print_output):
lab.configure(text=print_output)
If you prefer having all the lines, you could use a textarea instead of a Label and append the lines instead of replace them.
To get the program output you could do it in multiple ways:
Overload the python print() function, and add a line to call update_label(text). See more here, but it's a little bit tricky.
Replace all print() to your_print() so you can add the line above in a separate function, like this:
# You will need to make sure to only give one argument, and it should be a string.
def your_print(text):
update_label(text)
print(text)
The last one will come useful if you can't modify the other program, let's say it's already compiled. You could run the output of that program into a file (SomeCommand > SomeFile.txt), and then every second or so, your tkinter app could call update_label(). An example:
## REPLACE root.mainloop() for this:
# These three lines are the equivalent of mainloop()
while True:
root.update()
root.update_idletasks()
with open('somefile.txt') as filein:
last_line = filein.read().split('\n')[-1]
update_label(last_line)

Tkinter exit error [duplicate]

How do I end a Tkinter program? Let's say I have this code:
from Tkinter import *
def quit():
# code to exit
root = Tk()
Button(root, text="Quit", command=quit).pack()
root.mainloop()
How should I define the quit function to exit my application?
You should use destroy() to close a Tkinter window.
from Tkinter import *
#use tkinter instead of Tkinter (small, not capital T) if it doesn't work
#as it was changed to tkinter in newer Python versions
root = Tk()
Button(root, text="Quit", command=root.destroy).pack() #button to close the window
root.mainloop()
Explanation:
root.quit()
The above line just bypasses the root.mainloop(), i.e., root.mainloop() will still be running in the background if quit() command is executed.
root.destroy()
While destroy() command vanishes out root.mainloop(), i.e., root.mainloop() stops. <window>.destroy() completely destroys and closes the window.
So, if you want to exit and close the program completely, you should use root.destroy(), as it stops the mainloop() and destroys the window and all its widgets.
But if you want to run some infinite loop and don't want to destroy your Tkinter window and want to execute some code after the root.mainloop() line, you should use root.quit(). Example:
from Tkinter import *
def quit():
global root
root.quit()
root = Tk()
while True:
Button(root, text="Quit", command=quit).pack()
root.mainloop()
#do something
See What is the difference between root.destroy() and root.quit()?.
def quit()
root.quit()
or
def quit()
root.destroy()
import tkinter as tk
def quit(root):
root.destroy()
root = tk.Tk()
tk.Button(root, text="Quit", command=lambda root=root:quit(root)).pack()
root.mainloop()
Illumination in case of confusion...
def quit(self):
self.destroy()
exit()
A) destroy() stops the mainloop and kills the window, but leaves python running
B) exit() stops the whole process
Just to clarify in case someone missed what destroy() was doing, and the OP also asked how to "end" a tkinter program.
I think you wrongly understood the quit function of Tkinter. This function does not require you to define.
First, you should modify your function as follows:
from Tkinter import *
root = Tk()
Button(root, text="Quit", command=root.quit).pack()
root.mainloop()
Then, you should use '.pyw' suffix to save this files and double-click the '.pyw' file to run your GUI, this time, you can end the GUI with a click of the Button, and you can also find that there will be no unpleasant DOS window. (If you run the '.py' file, the quit function will fail.)
The usual method to exit a Python program:
sys.exit()
(to which you can also pass an exit status) or
raise SystemExit
will work fine in a Tkinter program.
In case anyone wants to bind their Escape button to closing the entire GUI:
master = Tk()
master.title("Python")
def close(event):
sys.exit()
master.bind('<Escape>',close)
master.mainloop()
The easiest way would be to click the red button (leftmost on macOS and rightmost on Windows).
If you want to bind a specific function to a button widget, you can do this:
class App:
def __init__(self, master)
frame = Tkinter.Frame(master)
frame.pack()
self.quit_button = Tkinter.Button(frame, text = 'Quit', command = frame.quit)
self.quit_button.pack()
Or, to make things a little more complex, use protocol handlers and the destroy() method.
import tkMessageBox
def confirmExit():
if tkMessageBox.askokcancel('Quit', 'Are you sure you want to exit?'):
root.destroy()
root = Tk()
root.protocol('WM_DELETE_WINDOW', confirmExit)
root.mainloop()
you only need to type this:
root.destroy()
and you don't even need the quit() function cause when you set that as commmand it will quit the entire program.
you dont have to open up a function to close you window, unless you're doing something more complicated:
from Tkinter import *
root = Tk()
Button(root, text="Quit", command=root.destroy).pack()
root.mainloop()
In idlelib.PyShell module, root variable of type Tk is defined to be global
At the end of PyShell.main() function it calls root.mainloop() function which is an infinite loop and it runs till the loop is interrupted by root.quit() function. Hence, root.quit() will only interrupt the execution of mainloop
In order to destroy all widgets pertaining to that idlelib window, root.destroy() needs to be called, which is the last line of idlelib.PyShell.main() function.
I normally use the default tkinter quit function, but you can do your own, like this:
from tkinter import *
from tkinter.ttk import *
window = Tk()
window.geometry('700x700') # 700p x 700p screen
def quit(self):
proceed = messagebox.askyesno('Quit', 'Quit?')
proceed = bool(proceed) # So it is a bool
if proceed:
window.quit()
else:
# You don't really need to do this
pass
btn1 = Button(window, text='Quit', command=lambda: quit(None))
window.mainloop()
For menu bars:
def quit():
root.destroy()
menubar = Menu(root)
filemenu = Menu(menubar, tearoff=0)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=quit)
menubar.add_cascade(label="menubarname", menu=filemenu)
root.config(menu=menubar)
root.mainloop()
I use below codes for the exit of Tkinter window:
from tkinter import*
root=Tk()
root.bind("<Escape>",lambda q:root.destroy())
root.mainloop()
or
from tkinter import*
root=Tk()
Button(root,text="exit",command=root.destroy).pack()
root.mainloop()
or
from tkinter import*
root=Tk()
Button(root,text="quit",command=quit).pack()
root.mainloop()
or
from tkinter import*
root=Tk()
Button(root,text="exit",command=exit).pack()
root.mainloop()
Code snippet below. I'm providing a small scenario.
import tkinter as tk
from tkinter import *
root = Tk()
def exit():
if askokcancel("Quit", "Do you really want to quit?"):
root.destroy()
menubar = Menu(root, background='#000099', foreground='white',
activebackground='#004c99', activeforeground='white')
fileMenu = Menu(menubar, tearoff=0, background="grey", foreground='black',
activebackground='#004c99', activeforeground='white')
menubar.add_cascade(label='File', menu=fileMenu)
fileMenu.add_command(label='Exit', command=exit)
root.config(bg='#2A2C2B',menu=menubar)
if __name__ == '__main__':
root.mainloop()
I have created a blank window here & add file menu option on the same window(root window), where I only add one option exit.
Then simply run mainloop for root.
Try to do it once
Of course you can assign the command to the button as follows, however, if you are making a UI, it is recommended to assign the same command to the "X" button:
def quit(self): # Your exit routine
self.root.destroy()
self.root.protocol("WM_DELETE_WINDOW", self.quit) # Sets the command for the "X" button
Button(text="Quit", command=self.quit) # No ()
There is a simple one-line answer:
Write - exit() in the command
That's it!

How to end Tkinter propbably if not using the Quit button

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.

Categories

Resources