Adapted from https://core.tcl-lang.org/tk/info/b1876b9ebc4b

Index: generic/tkInt.h
==================================================================
--- a/generic/tkInt.h.orig
+++ b/generic/tkInt.h
@@ -1094,10 +1094,11 @@
 /*
  * Themed widget set init function:
  */
 
 MODULE_SCOPE int	Ttk_Init(Tcl_Interp *interp);
+MODULE_SCOPE void	Ttk_TkDestroyedHandler(Tcl_Interp *interp);
 
 /*
  * Internal functions shared among Tk modules but not exported to the outside
  * world:
  */

Index: generic/tkWindow.c
==================================================================
--- a/generic/tkWindow.c.orig
+++ b/generic/tkWindow.c
@@ -1619,10 +1619,11 @@
 	    TkBindFree(winPtr->mainPtr);
 	    TkDeleteAllImages(winPtr->mainPtr);
 	    TkFontPkgFree(winPtr->mainPtr);
 	    TkFocusFree(winPtr->mainPtr);
 	    TkStylePkgFree(winPtr->mainPtr);
+	    Ttk_TkDestroyedHandler(winPtr->mainPtr->interp);
 
 	    /*
 	     * When embedding Tk into other applications, make sure that all
 	     * destroy events reach the server. Otherwise the embedding
 	     * application may also attempt to destroy the windows, resulting

Index: generic/ttk/ttkTheme.c
==================================================================
--- a/generic/ttk/ttkTheme.c.orig
+++ b/generic/ttk/ttkTheme.c
@@ -415,17 +415,10 @@
     StylePackageData *pkgPtr = (StylePackageData *)clientData;
     Tcl_HashSearch search;
     Tcl_HashEntry *entryPtr;
     Cleanup *cleanup;
 
-    /*
-     * Cancel any pending ThemeChanged calls:
-     */
-    if (pkgPtr->themeChangePending) {
-	Tcl_CancelIdleCall(ThemeChangedProc, pkgPtr);
-    }
-
     /*
      * Free themes.
      */
     entryPtr = Tcl_FirstHashEntry(&pkgPtr->themeTable, &search);
     while (entryPtr != NULL) {
@@ -528,10 +521,29 @@
     if (!pkgPtr->themeChangePending) {
 	Tcl_DoWhenIdle(ThemeChangedProc, pkgPtr);
 	pkgPtr->themeChangePending = 1;
     }
 }
+
+/* Ttk_TkDestroyedHandler --
+ *	See bug [310c74ecf440]: idle calls to ThemeChangedProc()
+ *	need to be canceled when Tk is destroyed, since the interp
+ *	may still be active afterward; canceling them from
+ *	Ttk_StylePkgFree() would be too late.
+ */
+void Ttk_TkDestroyedHandler(
+    Tcl_Interp* interp)
+{
+    StylePackageData* pkgPtr = GetStylePackageData(interp);
+
+    /*
+     * Cancel any pending ThemeChanged calls:
+     */
+    if (pkgPtr->themeChangePending) {
+	Tcl_CancelIdleCall(ThemeChangedProc, pkgPtr);
+    }
+}
 
 /*
  * Ttk_CreateTheme --
  *	Create a new theme and register it in the global theme table.
  *

