Updater: Prüfung beim Start und unter Hilfe, Setup herunterladen, DB sichern, still installieren, Neustart
Build / linux (push) Successful in 1m56s
Build / windows-exe (push) Successful in 8m6s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kerim AtilganandClaude Opus 5.5 committed 2026-11-03 15:18:22 +01:00
1 parent 968c68ac1a
commit b85a75857e
6 files changed
+260 -2

No files matched your search

+95 -1
View File
@@ -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