From what I understand, using sys.exit is not compatible with PyCharm and other IDEs, yet other exit methods that don't do garbage collection are not recommended. If I'm developing in PyCharm how can I properly shut down the program?
In the below example I am attempting to use a hot key to exit the program, yet the test statement is never reached. Is this an incorrect syntax or related to using the wrong exit command?
#Import modules
from tkinter import * #For images
from PIL import Image, ImageTk #For images
import os #For working directory
from pynput import keyboard #For hotkeys
#Set default directory
os.chdir('C:\\Users\\UserName\\Desktop\\Python\\SomeFolderName')
#Display Menu
root = Tk()
image = Image.open('menu.png')
display = ImageTk.PhotoImage(image)
root.overrideredirect(True) #Remove toolbar
label = Label(root, image=display)
label.pack()
root.mainloop()
#Functions
def ExitProgram():
print('test')
sys.exit(0)
#Define hotkeys
with keyboard.GlobalHotKeys({
'<ctrl>+w': ExitProgram,
'<ctrl>+0': ExitProgram}) as h:
h.join()
Use the quit() function to exit the program. This is a built-in function from Python in order to exit the program.
Happy Coding!
Nothing past root.mainloop() will execute because it never returns. Move the with keyboard... code before root.mainloop().
This answer from a similar question might be a useful answer for you.
Python exit commands - why so many and when should each be used?
I actually use VS code and sys.exit(0) is fine, but can also use quit() which is also fine.
Use quit() or exit() to terminate your program.
quit() and exit() are synonymous and do the exact same thing.
In addition to calling sys.exit(), using quit() does some other internal cleanup, such as closing input streams.
They are briefly discussed in the official documentation here: https://docs.python.org/3/library/constants.html#quit
Related
I have a python code that includes tkinter window and other running tasks.
I've been trying to bind "WM_DELETE_WINDOW" event to a function that exits my python code when I close the window but can't achieve that.
This is what I try:
def on_exit():
root.destroy()
sys.exit()
root.protocol('WM_DELETE_WINDOW', on_exit)
The window is destroyed successfully but the python code doesn't exit. Any possible reason for sys.exit() not to work?
What am I doing wrong? any alternative approach should I try?
Doing some testing I figured out what can be the problem.
Here's a small code that summarizes my code which is much bigger.
import tkinter as tk
import sys
root = tk.Tk()
submitted = tk.IntVar()
def on_exit():
root.destroy()
sys.exit()
root.protocol('WM_DELETE_WINDOW', on_exit)
def submit():
submitted.set(1)
print("submitted")
button= tk.Button(root, text="Submit",command=submit)
button.pack()
button.wait_variable(submitted)
root.mainloop()
I believe now that wait_variable is the source of the problem.
And the code actually exits when I added submitted.set(1) to on_exit() ( or if I clicked the button first before closing the window ) but if I tried closing the window without pressing the button, the code won't exit.
So does this mean that wait_variable not only makes tkinter app wait, but also prevents python code exiting?!
I tried os._exit(1) and it worked, but I think it's not clean.
As your updated question points out the problem is wait_variable(). Going off the documentation for this method wait_variable() enters a local event loop that wont interrupt the mainloop however it appears that until that local event loop is terminated (the variable is updated in some way) it will prevent the python instance from terminating as there is still an active loop. So in order to prevent this you have also correctly pointed out you need to update this variable right before you terminate the tk instance.
This might seam odd but it is the behavior I would expect. It is my understanding that an active loop needs to be terminated before a python instance can exit.
As Bryan has pointed out in the comments the wait_variable() method is "a function which calls the vwait command inside the embedded tcl interpreter. This tcl interpreter knows nothing about python exceptions which is likely why it doesn't recognize the python exception raised by sys.exit()"
Link to relevant documentation:
wait_variable()
Relevant text from link:
wait_variable(name)
Waits for the given Tkinter variable to
change. This method enters a local event loop, so other parts of the
application will still be responsive. The local event loop is
terminated when the variable is updated (setting it to it’s current
value also counts).
You can also set the variable to whatever it is currently set as to terminate this event loop.
This line should work for you:
submitted.set(submitted.get())
That said you do not actually need sys.exit(). You can simply use root.destroy().
You new function should look like this:
def on_exit():
submitted.set(submitted.get())
root.destroy()
The python instance will automatically close if there is no more code after the mainloop.
I started looking at some Tkinter tutorials. I always like to mess around with code before going on with the next chapter: for instance I tried to open more than one independent root.
from Tkinter import *
def main():
root1, root2 = Tk(), Tk()
root1.title("First window")
root2.title("Second window")
root1.mainloop()
root2.mainloop()
main()
What I don't understand is why this code works (i.e. it shows two different windows at the same time).
If the Tk "mainloop" method is a loop, why do I see the second window? Shouldn't I see it only after closing the first window (i.e. after breaking the first while loop)?
Please, explain me how it works
EDIT: Removed incorrect answer, added answer below
The short of why are confused is that the behavior of your code is undefined. Really, it isn't running as differently as you think. Try running this code:
from Tkinter import *
def main():
root1, root2 = Tk(), Tk()
root1.title("First window")
root2.title("Second window")
root1.mainloop()
print 'root1.mainloop() has finished running.'
root2.mainloop()
print 'root2.mainloop() has finished running.'
main()
So the first mainloop doesn't actually continue on immediately. It hangs on root1.mainloop() (as you expected) until you close both of the windows, then it continues on. I am not sure exactly why running root1.mainloop() opens both windows as I don't have access to all of the source code but I think it would be a little bit like asking about the results of 0/0. It just isn't supposed to happen.
I need to embed an interative python interpreter into my tkinter program. Could anyone help me out as to how to integrate it?
I have already looked at the main() function, but it's way to complex for my needs, but I can't seem to reduce it without breaking it.
Some details of what you must do may depend on what you want to do with IDLE's Shell once you have it running. I would like to know more about that. But let us start simple and make the minimum changes to pyshell.main needed to make it run with other code.
Note that in 3.6, which I use below, PyShell.py is renamed pyshell.py. Also note that everything here amounts to using IDLE's private internals and is 'use at your own risk'.
I presume you want to run Shell in the same process (and thread) as your tkinter code. Change the signature to
def main(tkroot=None):
Change root creation (find # setup root) to
if not tkroot:
root = Tk(className="Idle")
root.withdraw()
else:
root = tkroot
In current 3.6, there are a couple more lines to be indented under if not tkroot:
if use_subprocess and not testing:
NoDefaultRoot()
Guard mainloop and destroy (at the end) with
if not tkroot:
while flist.inversedict: # keep IDLE running while files are open.
root.mainloop()
root.destroy()
# else leave mainloop and destroy to caller of main
The above adds 'dependency injection' of a root window to the function. I might add it in 3.6 to make testing (an example of 'other code') easier.
The follow tkinter program now runs, displaying the both the root window and an IDLE shell.
from tkinter import *
from idlelib import pyshell
root = Tk()
Label(root, text='Root id is '+str(id(root))).pack()
root.update()
def later():
pyshell.main(tkroot=root)
Label(root, text='Use_subprocess = '+str(pyshell.use_subprocess)).pack()
root.after(0, later)
root.mainloop()
You should be able to call pyshell.main whenever you want.
I am trying to get keypresses in Python (2.7.10), bit I had no luck with getch(), as ord(getch()) was returning 255 constantly, so I am now using Tkinter. (I believe that Tkinter is also cross-platform so that should help as i plan to run this script on a Linux device).
I need to be able to get keypresses, even if they are not pressed while the Tkinter window is not active.
Here is my code:
from Tkinter import *
import time, threading
x = "Hi!"
def callback(event):
x = "key: " + event.char
print(x)
def doTk():
root = Tk()
root.bind_all("<Key>", callback)
root.withdraw()
root.mainloop()
thread1 = threading.Thread(target=doTk)
thread1.deamon = True
thread1.start()
I am not reveiving any errors, it is not not registering keypresses. I have also tried this without using threading, but it still does not work.
Please also note that I cannot use raw_input() as I need this to be able to run in the background and still get keypresses.
I am aware that this does not produce a frame, I do not want it to.
Thanks in advance for any help :)
PS: I have looked to other answers on StackOverflow and other sites, but they all either don't work or give solutions where keypresses are only registered when the tkinter frame is active.
I'm trying to capture key presses so that when a given combination is pressed I trigger an event.
I've searched around for tips on how to get started and the simplest code snippet I can find is in Python - I grabbed the code below for it from here. However, when I run this from a terminal and hit some keys, after the "Press a key..." statement nothing happens.
Am I being stupid? Can anyone explain why nothing happens, or suggest a better way of achieving this on Linux (any language considered!)?
import Tkinter as tk
def key(event):
if event.keysym == 'Escape':
root.destroy()
print event.char
root = tk.Tk()
print "Press a key (Escape key to exit):"
root.bind_all('<Key>', key)
# don't show the tk window
root.withdraw()
root.mainloop()
Tk does not seem to get it if you do not display the window. Try:
import Tkinter as tk
def key(event):
if event.keysym == 'Escape':
root.destroy()
print event.char
root = tk.Tk()
print "Press a key (Escape key to exit):"
root.bind_all('<Key>', key)
# don't show the tk window
# root.withdraw()
root.mainloop()
works for me...
What you're doing is reading /dev/tty in "raw" mode.
Normal Linux input is "cooked" -- backspaces and line endings have been handled for you.
To read a device like your keyboard in "raw" mode, you need to make direct Linux API calls to IOCTL.
Look at http://code.activestate.com/recipes/68397/ for some guidance on this. Yes, the recipe is in tcl, but it gives you a hint as to how to proceed.
Alternatively (a non-Python option) use XBindKeys.
Well, turns out there is a much simpler answer when using GNOME which doesn't involve any programming at all...
http://www.captain.at/howto-gnome-custom-hotkey-keyboard-shortcut.php
Archived on Wayback
Just create the script/executable to be triggered by the key combination and point the 'keybinding_commands' entry you create in gconf-editor at it.
Why didn't I think of that earlier?
tkinter 'bind' method only works when tkinter window is active.
If you want binding keystrokes combinations that works in all desktop (global key/mouse binding) you can use bindglobal (install with pip install bindglobal). It works exactly like standard tkinter 'bind'.
Example code:
import bindglobal
def callback(e):
print("CALLBACK event=" + str(e))
bg = bindglobal.BindGlobal()
bg.gbind("<Menu-1>",callback)
bg.start()