closing tkmessagebox after some time in python
Asked Answered
S

5

10

I am developing an automated attendance system where when a student scans his RFID tag, his attendance is recorded while showing a welcoming message using a tkmessagebox pop-up. The user will not have control of a mouse or keyboard and I would like to keep the message showing for 2 seconds and delete the message box. Is there a way I can close the tkmessagebox pop-up like proposed?

Selfconsequence answered 14/5, 2015 at 10:57 Comment(0)
P
4

I don't think that it can be done with tkMessageBox because this creates a modal dialog, and you do not have access to the widget id (so that it can be programmatically destroyed).

But it's not hard to create your own top level window, add some welcome message to it, then close it after a time period. Something like this:

from Tkinter import *

WELCOME_MSG = '''Welcome to this event.

Your attendance has been registered.

Don't forget your free lunch.'''
WELCOME_DURATION = 2000

def welcome():
    top = Toplevel()
    top.title('Welcome')
    Message(top, text=WELCOME_MSG, padx=20, pady=20).pack()
    top.after(WELCOME_DURATION, top.destroy)

root = Tk()
Button(root, text="Click to register", command=welcome).pack()

root.mainloop()

You need to hook up an event handler to the RFID detection. This is simulated by a button in the above code, and the event handler is the welcome() function. In welcome() a top level widget with a message is created. The top level widget is destroyed after 2000 milliseconds (2 seconds) using .after() which registers a callback function to be called after a delay.

Pintle answered 14/5, 2015 at 12:9 Comment(0)
D
4

I have tried many solutions I have found online, none worked as I would have expected. Finally, I have found an easy solution:

from tkinter import Tk
from tkinter.messagebox import Message 
from _tkinter import TclError

TIME_TO_WAIT = 2000 # in milliseconds 
root = Tk() 
root.withdraw()
try:
    root.after(TIME_TO_WAIT, root.destroy) 
    Message(title="your title", message="your message", master=root).show()
except TclError:
    pass

I know it's not optimal because I ignored the TclError, but it's the only thing that worked for me. BTW I'm working with python 3.7

Dodder answered 18/10, 2020 at 17:49 Comment(2)
When I try this, it works but I get an error objc[49016]: autorelease pool page 0x7f818195e000 corrupted magic 0x00000000 0x00000000 0x00000000 0x00000000 should be 0xa1a1a1a1 0x4f545541 0x454c4552 0x21455341 pthread 0x10a55fe00 should be 0x10a55fe00 How do I avoid the error?Myrna
I tried to get your error but couldn't get it, so I would recommend you to try empty except instead of TclError except.Dodder
G
2

The small function below will do the job. By setting the type you can choose for: info, warning or error message box, the default is 'Info'. You can set also the timeout, the default is 2.5 seconds.

def showMessage(message, type='info', timeout=2500):
    import tkinter as tk
    from tkinter import messagebox as msgb

    root = tk.Tk()
    root.withdraw()
    try:
        root.after(timeout, root.destroy)
        if type == 'info':
            msgb.showinfo('Info', message, master=root)
        elif type == 'warning':
            msgb.showwarning('Warning', message, master=root)
        elif type == 'error':
            msgb.showerror('Error', message, master=root)
    except:
        pass

Call the function as follow: For message type 'Info' and timeout of 2.5 seconds:

showMessage('Your message')

Or by your own settings for type message 'Error' and timeout 4 seconds:

showMessage('Your message', type='error', timeout=4000)
Germinant answered 5/3, 2022 at 16:16 Comment(0)
J
0

With Python3, you have to call the Toplevel() with the import name, Like :

import tkinter

top = tkinter.Toplevel()
Jhelum answered 10/11, 2018 at 11:40 Comment(0)
F
0

edit See my 'final' messagebox decorator 'solution' to cancel open messageboxes 'at will' here:

https://mcmap.net/q/1165885/-is-there-a-way-to-destroy-the-tkinter-messagebox-without-clicking-ok

original To enhance MarcusAurelius's marvelous answer, add some bits to the end of the function. My revision:

def showMessage(message, type='info', timeout=2500):
    import tkinter as tk
    from tkinter import messagebox as msgb

    root = tk.Tk()
    root.withdraw()
    try:
        root.after(timeout, root.destroy)
        if type == 'info':
            msgb.showinfo('Info', message, master=root)
        elif type == 'warning':
            msgb.showwarning('Warning', message, master=root)
        elif type == 'error':
            msgb.showerror('Error', message, master=root)
    except:
        pass

    if root is not None:
        try:
            root.destroy()
        except TclError:
            pass

        root = None

These additions are beneficial if you're calling from different threads. In my case, I am calling from the main thread as well as a system tray icon created with:

icoTray = pystray.Icon(...)
icoTray.run_detached()

Failure to include:

try:
    root.destroy()
except TclError:
    pass

root = None

gives:

RuntimeError: main thread is not in main loop 

in subsequent calls from the "other" thread"

Failure to include the

root = None

bit alone gave something like the following in calls from the "other" thread but I didn't try to isolate why:

invalid command name "..."
    while executing
"..."
    ("after" script)

Include both bits for increased happiness.

Felonry answered 12/6 at 19:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.