Neue Rechnungen tauchen von selbst auf - das Journal hoert auf die gemeinsame SQLite
Stehen beide Programme offen und wird im Rechnungstool eine Rechnung geschrieben, erscheint sie hier ohne Zutun. Das Journal traegt sich dafuer beim Start mit seiner PID in der gemeinsamen SQLite ein (Tabelle "laeuft") und traegt sich beim Schliessen wieder aus; das Rechnungstool sieht daran, dass jemand zuhoert, und legt eine Zeile in die Tabelle "nachrichten". Nachgesehen wird nur dort - erst ein os.stat auf die eine Datei, und nur wenn sich etwas geruehrt hat, wird sie geoeffnet. Kein Absuchen des Rechnungsordners, kein Server, kein Port. Beim Zurueckklicken ins Fenster wird sofort nachgesehen statt zwei Sekunden zu warten. Die Wache faengt jeden Fehler ab und stirbt nie - ein Netzlaufwerk, das kurz weg ist, darf das Fenster nicht lahmlegen. ⚠ Ein Denkfehler daraus gleich behoben: der erste Blick nach dem Start merkt sich den Stand AUCH dann, wenn es die Datei noch gar nicht gibt. Sonst galt die erste Meldung danach als "schon gesehen" und wurde verschluckt. Pruefstand pruef_wache.py (faehrt BEIDE Programme): 23 Pruefungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GYeAfLtccFrbU3MTj1MMTx
This commit is contained in:
parent
59d2db75af
commit
31bb0c600b
3 changed files with 478 additions and 0 deletions
93
app.py
93
app.py
|
|
@ -104,6 +104,13 @@ class App(tk.Tk):
|
|||
if self.pdf_ordner and os.path.isdir(self.pdf_ordner):
|
||||
self.after(200, lambda: self.einlesen(still=True))
|
||||
self.after(400, self._frage_gemeinsam_falls_noetig)
|
||||
# Beide Programme laufen nebeneinander: schreibt das Rechnungstool eine
|
||||
# Rechnung, meldet es das hier herein - siehe _wache_jetzt().
|
||||
self._briefkasten_stand = "?" # "?" = noch nicht nachgesehen
|
||||
self._nachricht_stand = 0 # bis hierher sind Meldungen gelesen
|
||||
self.after(600, self._wache)
|
||||
# Beim Zurueckklicken sofort nachsehen statt zwei Sekunden zu warten.
|
||||
self.bind("<FocusIn>", lambda _e: self._wache_jetzt())
|
||||
|
||||
# ------------------------------------------------------------ Aufbau ----
|
||||
def _menue(self):
|
||||
|
|
@ -253,6 +260,62 @@ class App(tk.Tk):
|
|||
text=f"Rechnungen: {self.pdf_ordner}" if self.pdf_ordner
|
||||
else "kein Rechnungs-Ordner gewählt (Datei → Rechnungs-Ordner wählen…)")
|
||||
|
||||
# ------------------------------------------------------------- Wache
|
||||
# Das Rechnungstool MELDET, wenn es eine Rechnung geschrieben hat - es legt
|
||||
# eine Zeile in den Briefkasten im gemeinsamen Ordner. Hier wird nur diese
|
||||
# eine Datei angesehen (ein os.stat), nicht der ganze Rechnungsordner.
|
||||
# Damit das Rechnungstool weiss, dass jemand zuhoert, traegt sich dieses
|
||||
# Fenster beim Start mit seiner PID ein (siehe _anmelden).
|
||||
WACHE_SEKUNDEN = 2
|
||||
|
||||
def _wache_jetzt(self):
|
||||
"""Einmal nachsehen. True, wenn eine Meldung da war und eingelesen wurde.
|
||||
|
||||
Billig gehalten: erst ein os.stat auf die gemeinsame Datei. Nur wenn
|
||||
sich dort etwas geruehrt hat, wird sie ueberhaupt geoeffnet.
|
||||
"""
|
||||
if self.buch is None:
|
||||
return False
|
||||
stand = gemeinsam.datei_stand(self.buch.ordner)
|
||||
if self._briefkasten_stand == "?":
|
||||
# Erster Blick nach dem Start: nur merken, auch wenn es die Datei
|
||||
# noch gar nicht gibt - sonst gilt die erste Meldung danach als
|
||||
# "schon gesehen" und wird verschluckt.
|
||||
self._briefkasten_stand = stand
|
||||
self._nachricht_stand = self._letzte_nachricht()
|
||||
return False
|
||||
if stand is None or stand == self._briefkasten_stand:
|
||||
return False
|
||||
self._briefkasten_stand = stand
|
||||
try:
|
||||
texte, neu = self.buch.nachrichten_ab(self._nachricht_stand)
|
||||
except Exception as e: # noqa: BLE001
|
||||
log.warning("Briefkasten nicht lesbar: %s", e)
|
||||
return False
|
||||
if not texte:
|
||||
return False # das Buch hat sich geaendert, aber ohne Meldung
|
||||
self._nachricht_stand = neu
|
||||
text = texte[-1] if len(texte) == 1 else f"{len(texte)} Meldungen, zuletzt: {texte[-1]}"
|
||||
log.info("Rechnungstool meldet: %s", text)
|
||||
self.einlesen(still=True)
|
||||
self.melde(f"Rechnungstool meldet: {text}")
|
||||
return True
|
||||
|
||||
def _letzte_nachricht(self):
|
||||
try:
|
||||
return self.buch.letzte_nachricht() if self.buch else 0
|
||||
except Exception: # noqa: BLE001
|
||||
return 0
|
||||
|
||||
def _wache(self):
|
||||
"""Der Wecker. Laeuft weiter, auch wenn ein Durchgang schiefgeht."""
|
||||
try:
|
||||
self._wache_jetzt()
|
||||
except Exception as e: # noqa: BLE001 - die Wache darf nie sterben
|
||||
log.warning("Wache: %s", e)
|
||||
finally:
|
||||
self.after(self.WACHE_SEKUNDEN * 1000, self._wache)
|
||||
|
||||
def suche_leeren(self):
|
||||
self.suche.set("")
|
||||
self.suchfeld.focus_set()
|
||||
|
|
@ -262,6 +325,32 @@ class App(tk.Tk):
|
|||
log.info(text)
|
||||
|
||||
# ---- Gemeinsames Nummernbuch -------------------------------------------
|
||||
def _anmelden(self):
|
||||
"""Mit der eigenen PID im gemeinsamen Ordner eintragen.
|
||||
|
||||
Daran sieht das Rechnungstool, dass dieses Fenster offen ist - es fragt
|
||||
Windows nach der Prozessnummer, nicht nach einem Fenster.
|
||||
"""
|
||||
ordner = self.journal.hole("gemeinsam_ordner", "")
|
||||
if not ordner:
|
||||
return
|
||||
if self.buch is None:
|
||||
self._buch_oeffnen()
|
||||
if self.buch is None:
|
||||
return
|
||||
try:
|
||||
self.buch.anmelden("journal", os.getpid(), os.path.basename(sys.executable))
|
||||
except Exception as e: # noqa: BLE001
|
||||
log.warning("Anmeldung fehlgeschlagen: %s", e)
|
||||
|
||||
def _abmelden(self):
|
||||
if self.buch is None:
|
||||
return
|
||||
try:
|
||||
self.buch.abmelden("journal")
|
||||
except Exception as e: # noqa: BLE001 - beim Schliessen nichts erzwingen
|
||||
log.debug("Abmelden fehlgeschlagen: %s", e)
|
||||
|
||||
def _buch_oeffnen(self):
|
||||
ordner = self.journal.hole("gemeinsam_ordner", "")
|
||||
if not ordner:
|
||||
|
|
@ -284,6 +373,7 @@ class App(tk.Tk):
|
|||
self._buch_oeffnen()
|
||||
if self.buch is None and not TESTLAUF:
|
||||
self.dlg_gemeinsam(erststart=True)
|
||||
self._anmelden()
|
||||
self._buch_fuellen()
|
||||
|
||||
def dlg_gemeinsam(self, erststart=False):
|
||||
|
|
@ -300,6 +390,7 @@ class App(tk.Tk):
|
|||
return
|
||||
self.journal.setze("gemeinsam_ordner", ordner)
|
||||
self._buch_oeffnen()
|
||||
self._anmelden()
|
||||
self._buch_fuellen(melden=True)
|
||||
|
||||
def _buch_fuellen(self, melden=False):
|
||||
|
|
@ -669,6 +760,8 @@ class App(tk.Tk):
|
|||
os.startfile(pfad) # noqa: S606
|
||||
|
||||
def _beenden(self):
|
||||
# Austragen, damit das Rechnungstool nicht weiter meldet.
|
||||
self._abmelden()
|
||||
self.journal.setze("jahr", self.jahr.get())
|
||||
self.journal.schliessen()
|
||||
self.destroy()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue