Tkinter mainloop() not quitting after closing window - python

This is NOT a duplicate of Python tkinter mainloop not quitting on closing the window
I have an app that builds on tkinter. I observed at sometimes after I close the window using the X button, the code will not execute past the mainloop() line. This happens completely randomly about 10% of chance. Rest of the time, it works like a charm. I would like to ask if there are any way to force it. As I said, the code blocks on the mainloop() line, thus calling sys.exit() after it does not help.
I am using Python 3.9.8.
This is not 100% reproducible, but here is something that might trigger the problem:
from tkinter import *
root = Tk()
Label(root, 'hi').pack()
mainloop()
print('exited')

My first thought is to use root.mainloop() instead of tkinter.mainloop(). This makes a difference if you are using several windows.
That said, I did see this a long time ago on some old OSes. Never figured out the reason, so I just wrote my own quit function, like this:
import tkinter as tk
def _quit():
root.quit()
root.destroy()
root = tk.Tk()
root.protocol("WM_DELETE_WINDOW", _quit)
tk.Label(root, 'hi').pack()
root.mainloop()
print('exited')

Related

Tkinter hangs after using `destroy` and `quit`

I'm using Tkinter to show a login dialog and then run my main logic.
I intend for the following snippet to close the window (finish the main loop of tk) after clicking the button and just print indefinitely (it is wrapped in while True is in order for the whole script to continue executing, which simulates a real program logic).
But instead, the following snippet hangs the window and fails to close in macOS Ventura with Python 3.10:
from time import sleep
from tkinter import Tk, Button
def quit():
root.quit()
root = Tk()
Button(root, text="Quit", command=quit).pack()
root.mainloop()
while True:
sleep(1)
print("Running a program logic...")
I've tried to run a functional version of this code (which fails the same) and a threaded version of it (which just crashes since an NSWindow must be created on the main thread).
I just really can't wrap my head around it!
EDIT: Fully working example
EDIT 2: Clarify the intention of this code
EDIT 3: Even more minimal code
Try this:
from Tkinter import *
def quit():
global root
root.quit()
root = Tk()
while True:
Button(root, text="Quit", command=quit).pack()
root.mainloop()

Tkinter tkMessageBox disables Tkinter key bindings

Here's a very simple example:
from Tkinter import *
import tkMessageBox
def quit(event):
exit()
root = Tk()
root.bind("<Escape>", quit)
#tkMessageBox.showinfo("title", "message")
root.mainloop()
If I run the code exactly as it is, the program will terminate when Esc is hit. Now, if I un-comment the tkMessageBox line, the binding is "lost" after closing the message box, i.e. pressing Esc won't do anything anymore. This is happening in Python 2.7. Can you please verify if this is happening also to you? And let me know about your Python version.
Here is a way to "by-pass" the problem. It's a different approach, but it might help:
from Tkinter import *
import tkMessageBox
def msg_test():
tkMessageBox.showinfo("title", "message")
def quit(event):
exit()
root = Tk()
root.bind("<Escape>", quit)
btn = Button(root, text="Check", command=msg_test); btn.pack()
root.mainloop()
Using tkMessageBox via a button click, doesn't affect key binding, i.e. pressing Esc continues to work.
If I understand the problem, you get the bad behavior if you call tkMessageBox.showInfo() before calling mainloop. If that is so, I think this is a known bug in tkinter on windows.
The solution is simple: don't do that. If you need a dialog to show at the very start of your program, use after to schedule it to appear after mainloop has started, or call update before displaying the dialog.
For example:
root = Tk()
root.after_idle(msg_test)
root.mainloop()
The original bug was reported quite some time ago, and the tk bug database has moved once or twice so I'm having a hard time finding a link to the original issue. Here's one issue from 2000/2001 that mentions it: https://core.tcl.tk/tk/tktview?name=220431ffff (see the comments at the very bottom of the bug report).
The report claims it was fixed, but maybe it has shown up again, or maybe your version of tkinter is old enough to still have the bug.

Combining Tkinter mainloop with another event listener

I am trying to build a program that listens for certain key combinations and then shows information to the user in a Tkinter window. To do this, I'm using a keylogger like so (simplified for this example):
from pyHook import HookManager
from pythoncom import PumpMessages
import Tkinter as tk
def on_keyboard_event(event):
label.config(text=event.Key)
root.update()
return True
hm = HookManager()
hm.KeyDown = on_keyboard_event
hm.HookKeyboard()
root = tk.Tk()
label = tk.Label(root, text='Hello world')
label.pack()
PumpMessages()
As expected, the window pops up and shows the user what key they pressed. However, I would like to integrate functionality to show other messages by interacting with the Tkinter window, such as by pressing a button. However, it seems I need Tkinter's mainloop to do this, which I can't figure out how to run alongside PumpMessages(), since it also halts the code similar to mainloop().
I tried running root.mainloop() in a root.after(), and I tried recreating root.mainloop like so:
def mainloop():
root.update()
root.after(50, mainloop)
and then running it right before PumpMessages, but neither of these solutions worked. It also doesn't seem like you can run PumpMessages or root.mainloop in a thread, though I could just not be doing it right. If this is not possible with Tkinter, is there an alternate Python GUI I could use that would make it possible?
You don't need to create a function to use mainloop() so just simply place the mainloop() at the bottom of your code. If you want a delay on it, use root.after(milliseconds, function)
Also, remember to put mainloop() before PumpMessages()
e.g.
def mainloopfunction():
mainloop()
root.after(5000, mainloopfunction)
Hope I could help!

Tkinter dummy window crashes

I'm using tkinter to display a simple yesno messagebox in python 3.2.
The code is as follows:
x = tkinter.messagebox.askyesno("New Process", "New Process:\n" + e[2:-7] + "\n\nKill?")
Althought there is nothing wrong with the code(it functions as I want it to), there is a window in the background that appears and does not respond.
This window will crash after about a few seconds or after killing the host process.
What might cause this?
A couple of things:
It looks like you're not running it as a root window.
root = Tk()
app = Frame(root)
app.grid()
my_example = Label(app, "text")
my_example.grid()
root.mainloop()
You should put it in a bat file with pause and you'll be able to see the error

Tkinter .pack() window not showing?

I've been working on a simple program that make a button output something. But when i run it,
this
(I got this from the internet btw) does not show up. Is somethoing wrong with the code or something?
Please help me so the window above can appear :)
Code:
from Tkinter import *
def asdf():
print('test')
tk = Tk()
b = Button(tk, text="test", command=asdf)
b.pack()
You forgot to call the Tk.mainloop method at the end of your program:
from Tkinter import *
def asdf():
print('test')
tk = Tk()
b = Button(tk, text="test", command=asdf)
b.pack()
##############
tk.mainloop()
##############
Doing so starts Tkinter's main event loop and creates the window.
It seems you are using Python3, as there are parentheses after print, so from Tkinter import * should be from tkinter import *. Python is case-sensitive. You also forgot to call root.mainloop() at the end of your code as #user2555451 mentioned, although a window should appear all the same, but stop responding when any event occurs (e.g., clicks, key presses, focus changes).

Categories