如何在程序退出(含X按钮关闭、退出码-1)时执行函数并保存变量至Pickle?
atexit Not Triggering on Window Close (Exit Code -1) Got it, let’s break down why your atexit hook isn’t firing when you click the window’s "X" button, and how to fix it so your variables get saved every time.
The core issue here is that atexit only runs during clean exits—like when you call sys.exit() manually, your script finishes naturally, or you use Ctrl+C (in most cases). But when you click the window close button, your GUI framework (or the OS, for console apps) sends a termination signal that bypasses the normal atexit flow, hence the exit code -1.
Here’s how to handle this scenario for different types of apps:
1. GUI Apps (Tkinter Example)
For Tkinter, you can override the window’s WM_DELETE_WINDOW protocol to intercept the close event, run your save logic, then exit cleanly.
import tkinter as tk import pickle # Variables you want to save user_session = {"username": "piotr_g", "preferences": {"auto_save": True}} def save_to_pickle(): """Save variables to a pickle file""" print("Saving session data...") with open("session_data.pkl", "wb") as f: pickle.dump(user_session, f) def handle_window_close(): # Run save logic first save_to_pickle() # Close the app properly (this will trigger any atexit hooks too, if you still have them) root.destroy() # Set up the Tkinter window root = tk.Tk() root.title("My App") # Override the default close behavior root.protocol("WM_DELETE_WINDOW", handle_window_close) # Add sample GUI content tk.Label(root, text="Click the 'X' to close and save data").pack(pady=20) root.mainloop()
2. GUI Apps (PyQt/PySide Example)
For PyQt/PySide, reimplement the closeEvent method of your main window to catch the close event:
from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel import sys import pickle # Variables to persist app_state = {"last_opened_file": "report.txt", "window_size": (800, 600)} def save_state(): print("Saving app state...") with open("app_state.pkl", "wb") as f: pickle.dump(app_state, f) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("PyQt App") self.setCentralWidget(QLabel("Close the window to save state")) def closeEvent(self, event): # Execute save logic before exiting save_state() # Allow the window to close event.accept() if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_())
3. Console Apps (Closing Terminal Window)
If you’re dealing with a console app and the user closes the terminal window (instead of using Ctrl+C), you can catch the SIGTERM signal (sent by the OS in this case) to run your save code:
import atexit import pickle import signal import sys # Data to save counter_data = {"total_runs": 15, "last_run": "2024-05-20"} def save_counter(): print("Saving counter data...") with open("counter.pkl", "wb") as f: pickle.dump(counter_data, f) # Register atexit for clean exits (still useful!) atexit.register(save_counter) def handle_termination(signum, frame): # Run save logic when SIGTERM/SIGINT is received save_counter() # Exit cleanly sys.exit(0) # Listen for termination signals signal.signal(signal.SIGTERM, handle_termination) signal.signal(signal.SIGINT, handle_termination) # Simulate your app's main loop print("App running... Close the terminal window or press Ctrl+C to exit.") try: while True: # Your app logic here pass except KeyboardInterrupt: pass
Important Notes
- SIGKILL can’t be caught: If the OS force-terminates your process (e.g., via Task Manager on Windows), there’s no way to run save logic—this is unavoidable.
- Always test: Make sure to verify that your save code runs in all exit scenarios you care about.
内容的提问来源于stack exchange,提问作者Piotr Gołdyś

