-1

I'd like to be able to handle signals whilst a Tkinter messagebox (or similar) is open and awaiting user input.

How can I make it so that the handler is called and the program exits?

Here is what I've tried. The messagebox remains open when the signal is triggered.

import signal
import sys
from tkinter import messagebox

def handler(signum, frame):
    print("STOP!")
    sys.exit(1)

signal.signal(signal.SIGINT, handler)

messagebox.showinfo("This is a message box", "This is a message")
adambro
  • 310
  • 4
  • 16

3 Answers3

2

Use the Toplevel window to create your own message box and use bind to handle the case.

from tkinter import *
from tkinter import messagebox

def handler(frame):
    print("STOP!")
    sys.exit(1)

root = Tk()

top = Toplevel(root)
top.title("About this application...")
top.bind('<Control-c>', handler)
msg = Message(top, text="###################")
msg.pack()

button = Button(top, text="Dismiss", command=top.destroy)
button.pack()

root.mainloop()

event name list to bind!

from tkinter import *
from tkinter import messagebox

def handler(frame):
    print("STOP!")
    sys.exit(1)

root = Tk()
root.geometry("{0}x{1}".format(root.winfo_screenwidth()-3, root.winfo_screenheight()-3))

top = Toplevel(root, takefocus=True)
top.title("This is a message box")

w = top.winfo_reqwidth()
h = top.winfo_reqheight()
ws = top.winfo_screenwidth()
hs = top.winfo_screenheight()
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
top.geometry('%dx%d+%d+%d' % (300, 100, x, y))

top.attributes("-topmost", True)

top.bind('<Control-c>', handler)

lbl = Label(top, text="This is a message...")
lbl.pack(expand=True, fill='x')

button = Button(top, text="OK", command=top.destroy)
button.focus_set()
button.pack(pady=2)

root.mainloop()
Varada
  • 736
  • 7
  • 17
  • Thanks. This didn't directly resolve my problem but the suggestion to create my own message box has pointed me in the right direction. I'll post an answer with the solution I've come up with. – adambro Mar 28 '19 at 14:01
2

As suggested by @varadaraju-g, the best approach here seems to be to create a message box from scratch. Binding to keyboard events doesn't meet my requirements so I've needed to use signal.

The crucial thing here is then ensuring those signals actually get handled whilst we're in the mainloop. This answer quoting Guido is the key, explaining how using after() to periodically call a dummy function enables the signal handling.

import signal
from tkinter import *

def handler(sig, frame):
    print("STOP!")
    sys.exit(1)

def show_message_box(title, text):

    root = Tk()

    root.withdraw()
    top = Toplevel(root)
    top.title(title)
    msg = Message(top, text=text)
    msg.pack()
    button = Button(top, text="Dismiss", command=root.destroy)
    button.pack()

    def signal_check():
        root.after(50, signal_check)

    root.after(50, signal_check)
    top.protocol("WM_DELETE_WINDOW", root.quit)
    root.mainloop()

    print("End of dialog")

signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)

show_message_box(title="message box", text="hello world")
adambro
  • 310
  • 4
  • 16
0

If your program have reference to the Tk() instance, for example root = Tk(), then call root.destroy() instead of sys.exit(1):

import signal
from tkinter import Tk, messagebox

def handler(signum, frame):
    print("STOP!")
    #sys.exit(1)
    root.destroy()

signal.signal(signal.SIGINT, handler)

root = Tk()
messagebox.showinfo("This is a message box", "This is a message")
acw1668
  • 40,144
  • 5
  • 22
  • 34
  • This still doesn't work as I'm hoping for it to. When I run this, the messagebox is displayed but if I send a SIGINT signal, the handler is only run if I actually first close the message box. If I replace the Tkinter messagebox with just a command line input (e.g. `input("Enter some data")`) then this does work as expected. Instead of continuing to wait for user input, the handler is called and the program exits. – adambro Mar 28 '19 at 10:58
  • Try using `os._exit(1)`. – acw1668 Mar 28 '19 at 12:11