Tkinter tkMessageBox disables Tkinter key bindings - python

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.

Related

Tkinter mainloop() not quitting after closing window

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')

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 .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).

tkinter: KeyboardInterrupt taking a while

Using Tkinter with Python on Linux, I'm trying to make Ctrl+C stop execution by using the KeyboardInterrupt Exception, but when I press it nothing happens for a while. Eventually it "takes" and exits. Example program:
import sys
from Tkinter import *
try:
root = Tk()
root.mainloop()
except:
print("you pressed control c")
sys.exit(0)
How can the program react quicker?
That is a little problematic because, in a general way, after you invoke the mainloop method you are relying on Tcl to handle events. Since your application is doing nothing, there is no reason for Tcl to react to anything, although it will eventually handle other events (as you noticed, this may take some time). One way to circumvent this is to make Tcl/Tk do something, scheduling artificial events as in:
from Tkinter import Tk
def check():
root.after(50, check) # 50 stands for 50 ms.
root = Tk()
root.after(50, check)
root.mainloop()
According to Guido van Rossum, this is because you are stuck in the Tcl/Tk main loop, while the signal handlers are only handled by the Python interpreter.
You can work around the problem, by binding Ctrl-c to a callback function:
import sys
import Tkinter as tk
def quit(event):
print "you pressed control c"
root.quit()
root = tk.Tk()
root.bind('<Control-c>', quit)
root.mainloop()

How to know whether a window with a given title is already open in Tk?

I’ve writen a little python script that just pops up a message box containing the text passed on the command line. I want to pop it up only when the window —resulting from a previous call— is not open.
from Tkinter import *
import tkMessageBox
root = Tk()
root.withdraw()
# TODO not if a window with this title exists
tkMessageBox.showinfo("Key you!", " ".join(sys.argv[1:]))
Any idea how to check that?
I believe you want:
if 'normal' != root.state():
tkMessageBox.showinfo("Key you!", " ".join(sys.argv[1:]))
The previous answer works accordingly to the code you have provided. You say it does not work because the answerer complies with "sois bête et discipliné" rule in that he did not add root.mainloop() to his code since your question does not either.
By adding the later line, for some reason caused by the event loop, you should test the exact string "withdrawn" as follows:
import tkinter as tk
from tkinter import messagebox
import sys
root = tk.Tk()
root.withdraw()
if 'withdrawn' != root.state():
messagebox.showinfo("Key you!", sys.argv[1:])
root.mainloop()
Note: do not run this code otherwise your Terminal session will hang up. To circumvent this discomfort, you will have to reset the window state using either root.state("normal") which will lead to the message box to disappear as if a click on the Ok button occurred, or root.iconify() through which you can stop the Terminal session to hang up by right clicking on the tkinter icon appearing on your OS taskbar.

Categories