Tkinter messagebox reappears after dismissal

Viewed 240

I'm using Tkinter 8.6 and macOS 10.15.4

The following code gives a button which pops up a messagebox, which can be dismissed:

import tkinter as tk
from tkinter import messagebox

root = tk.Tk()
button = tk.Button(root, text = 'click here', command = messagebox.askquestion)
button.pack()
root.mainloop()

After dismissing the messagebox, if I change focus to another window and then use command-tab to change focus back to the tk window, the dismissed messagebox reappears but cannot be interacted with. This does not happen if I change focus back to the tk window with a mouse. If I click the button again, the ghost messagebox disappears and a new one appears.

Here's a short video clip. https://www.youtube.com/watch?v=BT1c_8YwBMY At about 9s I command-tab away from the tkinter window and at 10s command-tab back in. You can see that the ghost messagebox can be moved but not dismissed.

1 Answers

This is a known problem with Tkinter on mac and is very annoying. We can solve this problem by getting the focus back to the window as soon as the message box closes by using root.grab_set() and then releasing it with root.grab_release().

Now here what you can do is make a function askquestion() like so,

def askquestion(title=None, message=None, **options):
    r = messagebox.askquestion(title, message, **options)
    tk._default_root.grab_set()
    tk._default_root.grab_release()
    return r

which will work fine when you just wanna use askquestion messagebox, but if you wanna use other message boxes you have to create a new function for each different message box. So, in my opinion, if you just modify messagebox.Message() class show() method then it will do the trick for all. Like so,

import tkinter as tk
from tkinter import messagebox

class Message(messagebox.Message):    
    def show(self, **options):
        r = super().show(**options)
        tk._default_root.grab_set()
        tk._default_root.grab_release()
        return r
messagebox.Message = Message

And that's all you need to put at the beginning of your code. Hopefully, this solves your issue.

Related