Files
ware_house/fix_query.py
2026-03-31 19:15:33 +02:00

95 lines
3.5 KiB
Python

"""One-off maintenance script to patch performance issues in ``layout_window``.
The script was used during development to remove an expensive resize-triggered
refresh and to inject some lifecycle guards into the window implementation.
It is kept in the repository as an auditable patch recipe.
"""
from pathlib import Path
import re
p = Path("./layout_window.py")
src = p.read_text(encoding="utf-8")
backup = p.with_suffix(".py.bak_perf")
if not backup.exists():
# Preserve the original version so the patch can be reversed manually.
backup.write_text(src, encoding="utf-8")
# 1) Rimuovi il bind su <Configure> che innescava refresh continui.
src = src.replace(
' self.bind("<Configure>", lambda e: self.after_idle(self._refresh_stats))\n',
' # disabilitato: il refresh ad ogni <Configure> generava molte query/lag\n'
' # self.bind("<Configure>", lambda e: self.after_idle(self._refresh_stats))\n'
)
# 2) Nel metodo _refresh_stats, elimina il blocco che interroga il DB "globale".
# Riconosciamo il blocco su "sql_tot = ..." e lo neutralizziamo.
src = re.sub(
r"\n\s*# globale dal DB[\s\S]*?self\._async\.run\(.*?\)\)\n",
"\n # [patch] rimosso refresh globale da DB: calcoliamo solo dalla matrice in memoria\n",
src,
flags=re.MULTILINE
)
# 3) Aggiungi un flag di vita finestra e un destroy sicuro
# - settiamo self._alive = True in __init__
# - override destroy() per annullare timer e marcare _alive=False
src = src.replace(
" self._last_req = 0\n",
" self._last_req = 0\n"
" self._alive = True\n"
" self._stats_after_id = None # se mai userai un refresh periodico, potremo cancellarlo qui\n"
)
# aggiungi metodo destroy() se non esiste già
if "def destroy(self):" not in src:
insert_point = src.find("def open_layout_window(")
destroy_method = (
"\n def destroy(self):\n"
" # evita nuovi refresh/async dopo destroy\n"
" self._alive = False\n"
" # cancella eventuali timer\n"
" try:\n"
" if self._stats_after_id is not None:\n"
" self.after_cancel(self._stats_after_id)\n"
" except Exception:\n"
" pass\n"
" # pulizia UI leggera\n"
" try:\n"
" for w in list(self.host.winfo_children()):\n"
" w.destroy()\n"
" except Exception:\n"
" pass\n"
" try:\n"
" super().destroy()\n"
" except Exception:\n"
" pass\n\n"
)
src = src[:insert_point] + destroy_method + src[insert_point:]
# 4) Nei callback _ok/_err delle query, assicurati che non facciano nulla se la finestra è chiusa.
src = re.sub(
r"def _ok\(res\):\n",
"def _ok(res):\n"
" if not getattr(self, '_alive', True) or not self.winfo_exists():\n"
" return\n",
src
)
src = re.sub(
r"def _err\(ex\):\n",
"def _err(ex):\n"
" if not getattr(self, '_alive', True) or not self.winfo_exists():\n"
" return\n",
src
)
# 5) Piccola robustezza: prima di schedulare highlight post-ricarica controlla ancora _alive.
src = src.replace(
" if self._pending_focus and self._pending_focus[0] == corsia:\n",
" if getattr(self, '_alive', True) and self._pending_focus and self._pending_focus[0] == corsia:\n"
)
p.write_text(src, encoding="utf-8")
print(f"Patch applicata a {p} (backup in {backup}).")