Updater: Prüfung beim Start und unter Hilfe, Setup herunterladen, DB sichern, still installieren, Neustart
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
968c68ac1a
commit
b85a75857e
6 files changed
+260
-2
No files matched your search
+95
-1
@@ -5,14 +5,17 @@ import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import tkinter as tk
|
||||
import traceback
|
||||
import webbrowser
|
||||
from collections import defaultdict
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
from tkinter import filedialog
|
||||
|
||||
from provtool import config
|
||||
from provtool import __version__, config, updater
|
||||
from provtool import export as exp
|
||||
from provtool import regeln
|
||||
from provtool.db import EIGEN, UNZUGEORDNET, AbrechnungErledigt, Datenbank
|
||||
@@ -235,6 +238,7 @@ class App:
|
||||
hi.add_command(label="Kurzanleitung", accelerator="F1", command=self.hilfe)
|
||||
hi.add_command(label="Rundgang durch das Programm", command=self.rundgang)
|
||||
hi.add_command(label="Tipp des Tages...", command=self.tipp_des_tages)
|
||||
hi.add_command(label="Nach Updates suchen...", command=self.updates_pruefen)
|
||||
hi.add_separator()
|
||||
hi.add_command(label="Einrichtungs-Assistent...", command=self.assistent)
|
||||
hi.add_separator()
|
||||
@@ -1247,9 +1251,95 @@ class App:
|
||||
self.db_wechseln(res["pfad"])
|
||||
except Exception as exc:
|
||||
w.fehler(self.root, "Einstellungen", f"Datenbank konnte nicht geöffnet werden:\n{exc}")
|
||||
self.settings["update_pruefen"] = res["update"]
|
||||
config.save(self.settings)
|
||||
if res["design"] != w.DESIGN:
|
||||
self.design_wechseln(res["design"])
|
||||
|
||||
# ================================================================= Updates
|
||||
@staticmethod
|
||||
def _installiert() -> bool:
|
||||
"""Läuft die per Setup installierte Windows-Version? Nur die kann sich selbst aktualisieren."""
|
||||
return (bool(getattr(sys, "frozen", False)) and sys.platform.startswith("win")
|
||||
and Path(sys.executable).name.lower() == "provisionsmanager.exe")
|
||||
|
||||
def _im_hintergrund(self, arbeit, fertig):
|
||||
"""arbeit() im Thread (nur Netzwerk/Dateien, kein Tk), danach fertig(ergebnis) im Tk-Thread."""
|
||||
erg: dict = {}
|
||||
|
||||
def lauf():
|
||||
try:
|
||||
erg["wert"] = arbeit()
|
||||
except Exception as exc:
|
||||
erg["fehler"] = exc
|
||||
threading.Thread(target=lauf, daemon=True).start()
|
||||
|
||||
def warten():
|
||||
if self._beendet or self._schliesst:
|
||||
return
|
||||
if not erg:
|
||||
self.root.after(200, warten)
|
||||
else:
|
||||
fertig(erg)
|
||||
self.root.after(200, warten)
|
||||
|
||||
def updates_pruefen(self, still: bool = False):
|
||||
"""Auf Gitea nach einer neueren Version suchen. still = automatisch beim Start: nur melden, wenn es
|
||||
eine gibt (keine Meldung bei Fehler oder wenn aktuell)."""
|
||||
if getattr(self, "_update_laeuft", False):
|
||||
return
|
||||
self._update_laeuft = True
|
||||
|
||||
def fertig(erg):
|
||||
self._update_laeuft = False
|
||||
self._update_melden(erg, still)
|
||||
self._im_hintergrund(updater.neueste, fertig)
|
||||
|
||||
def _update_melden(self, erg: dict, still: bool):
|
||||
if "fehler" in erg:
|
||||
if not still:
|
||||
w.warnung(self.root, "Updates", f"Konnte nicht nach Updates suchen:\n{erg['fehler']}")
|
||||
return
|
||||
info = erg["wert"]
|
||||
if not updater.ist_neuer(info["version"], __version__):
|
||||
if not still:
|
||||
w.info(self.root, "Updates", f"Sie haben die neueste Version ({__version__}).")
|
||||
return
|
||||
if still and modal.offen(): # z. B. Tipp des Tages offen – später noch einmal
|
||||
self.root.after(3000, lambda: self._update_melden(erg, still))
|
||||
return
|
||||
text = f"Version {info['version']} ist verfügbar (installiert: {__version__})."
|
||||
if self._installiert() and info["setup_url"]:
|
||||
if w.frage(self.root, "Update verfügbar",
|
||||
text + "\n\nJetzt herunterladen und installieren? Die Datenbank wird vorher gesichert, das "
|
||||
"Programm beendet sich kurz und startet danach von selbst wieder.",
|
||||
ja="Jetzt installieren", nein="Später"):
|
||||
self._update_installieren(info)
|
||||
elif w.frage(self.root, "Update verfügbar", text + "\n\nDownload-Seite öffnen?",
|
||||
ja="Seite öffnen", nein="Später"):
|
||||
webbrowser.open(info["seite"])
|
||||
|
||||
def _update_installieren(self, info: dict):
|
||||
self.status.set("info", f" Lade Version {info['version']} herunter ...")
|
||||
ordner = Path(tempfile.gettempdir()) / "Provisionsmanager-Update"
|
||||
|
||||
def fertig(erg):
|
||||
if "fehler" in erg:
|
||||
self.status.set("info", "")
|
||||
w.fehler(self.root, "Update", f"Download fehlgeschlagen:\n{erg['fehler']}")
|
||||
return
|
||||
try:
|
||||
self.db.backup(config.app_dir() / f"vor-update-{__version__}.db")
|
||||
except Exception as exc:
|
||||
if not w.frage(self.root, "Update", f"Die Datenbank konnte nicht gesichert werden:\n{exc}\n\n"
|
||||
"Trotzdem installieren?", ja="Installieren", nein="Abbrechen", default_ja=False):
|
||||
return
|
||||
# NEUSTART=1: das Setup startet das Programm danach wieder (installer.iss)
|
||||
subprocess.Popen([str(erg["wert"]), "/VERYSILENT", "/SUPPRESSMSGBOXES", "/NORESTART",
|
||||
"/CLOSEAPPLICATIONS", "/NEUSTART=1"], close_fds=True)
|
||||
self.beenden()
|
||||
self._im_hintergrund(lambda: updater.herunterladen(info, ordner), fertig)
|
||||
|
||||
def design_umschalten(self):
|
||||
self.design_wechseln("modern" if w.DUNKEL else "dunkel")
|
||||
|
||||
@@ -1450,12 +1540,16 @@ def main():
|
||||
except Exception:
|
||||
pass
|
||||
dateien = [a for a in sys.argv[1:] if Path(a).exists()]
|
||||
erster = True
|
||||
while True:
|
||||
root = tk.Tk(className="Provisionsmanager95")
|
||||
app = App(root)
|
||||
if dateien:
|
||||
root.after(200, lambda: app.importieren(dateien))
|
||||
dateien = []
|
||||
if erster and app.settings.get("update_pruefen", True): # nur im echten Start, nicht in Tests
|
||||
root.after(3000, lambda: app.updates_pruefen(still=True))
|
||||
erster = False
|
||||
root.mainloop()
|
||||
if not app.neustart: # sonst: Design wurde umgeschaltet – Fenster neu aufbauen
|
||||
break
|
||||
|
||||
Reference in new issue
Block a user