Search code examples
pythonmultithreadingtkintersignals

Exiting a tkinter app with Ctrl-C and catching SIGINT


Ctrl-C/SIGTERM/SIGINT seem to be ignored by tkinter. Normally it can be captured again with a callback. This doesn't seem to be working, so I thought I'd run tkinter in another thread since its mainloop() is an infinite loop and blocks. I actually also want to do this to read from stdin in a separate thread. Even after this, Ctrl-C is still not processed until I close the window. Here's my MWE:

#! /usr/bin/env python
import Tkinter as tk
import threading
import signal
import sys

class MyTkApp(threading.Thread):
    def run(self):
        self.root = tk.Tk()
        self.root.mainloop()

app = MyTkApp()
app.start()

def signal_handler(signal, frame):
    sys.stderr.write("Exiting...\n")

    # think only one of these is needed, not sure
    app.root.destroy()
    app.root.quit()

signal.signal(signal.SIGINT, signal_handler)

Results:

  • Run the app
  • Ctrl-C in the terminal (nothing happens)
  • Close the window
  • "Exiting..." is printed and I get an error about the loop already having exited.

What's going on here and how can I make Ctrl-C from the terminal close the app?


Update: Adding a poll, as suggested, works in the main thread but does not help when started in another thread...

class MyTkApp(threading.Thread):
    def poll(self):
        sys.stderr.write("poll\n")
        self.root.after(50, self.poll)

    def run(self):
        self.root = tk.Tk()
        self.root.after(50, self.poll)
        self.root.mainloop()

Solution

  • Since your tkinter app is running in another thread, you do not need to set up the signal handler in the main thread and just use the following code block after the app.start() statement:

    import time
    
    while app.is_alive():
        try:
            time.sleep(0.5)
        except KeyboardInterrupt:
            app.root.destroy()
            break
    

    You can then use Ctrl-C to raise the KeyboardInterrupt exception to close the tkinter app and break the while loop. The while loop will also be terminated if you close your tkinter app.

    Note that the above code is working only in Python 2 (as you use Tkinter in your code).