update
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Auto-generated by aza_build_stamp.py – DO NOT EDIT
|
# Auto-generated by aza_build_stamp.py – DO NOT EDIT
|
||||||
BUILD_TIME = "2026-05-03 19:14:17"
|
BUILD_TIME = "2026-05-04 23:40:27"
|
||||||
BUILD_TIMESTAMP = "20260503_191417"
|
BUILD_TIMESTAMP = "20260504_234027"
|
||||||
GIT_COMMIT = "d4822fc8"
|
GIT_COMMIT = "03d188d1"
|
||||||
GIT_BRANCH = "main"
|
GIT_BRANCH = "main"
|
||||||
GIT_DIRTY = True
|
GIT_DIRTY = True
|
||||||
|
|||||||
@@ -13,10 +13,9 @@ Aufbauend auf V1.1:
|
|||||||
``_send_to_empfang``).
|
``_send_to_empfang``).
|
||||||
* **Erscheinungsbild**: dieselbe **Transparenz-Logik** wie in der klassischen
|
* **Erscheinungsbild**: dieselbe **Transparenz-Logik** wie in der klassischen
|
||||||
Hauptfenster-Kopfzeile (``_opacity_var_main``, ``MIN_OPACITY``, ``save_opacity``)
|
Hauptfenster-Kopfzeile (``_opacity_var_main``, ``MIN_OPACITY``, ``save_opacity``)
|
||||||
sowie Zugriff auf **alle Einstellungen** (``_open_settings``). Zusätzlich
|
sowie Zugriff auf **alle Einstellungen** (``_open_settings``). Die Farbpalette
|
||||||
weiterhin **Hell/Dunkel** für die Office-Hülle-Farbpalette (persistiert),
|
der Office-Hülle folgt der beim Start geladenen Hell/Dunkel-Präferenz;
|
||||||
da das klassische ``basis14``-Hauptfenster keine globale Farb-Umschaltung hat — nur
|
Umschalten nur noch über das klassische Einstellungsfenster, falls dort angeboten.
|
||||||
Transparenz und das separate Einstellungsfenster.
|
|
||||||
* Footer-Branding und Logo ca. **30 % größer** als in V1.1.
|
* Footer-Branding und Logo ca. **30 % größer** als in V1.1.
|
||||||
|
|
||||||
Technische Strategie
|
Technische Strategie
|
||||||
@@ -738,7 +737,7 @@ class _OfficeShellV12:
|
|||||||
pad = dict(
|
pad = dict(
|
||||||
bg=acc, fg="white", font=FONT_DEFAULT,
|
bg=acc, fg="white", font=FONT_DEFAULT,
|
||||||
activebackground=acc, activeforeground="white",
|
activebackground=acc, activeforeground="white",
|
||||||
selectcolor="#E2EEF6", highlightthickness=0,
|
selectcolor="#1a4d6d", highlightthickness=0,
|
||||||
bd=0, anchor="w",
|
bd=0, anchor="w",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1015,6 +1014,7 @@ class _OfficeShellV12:
|
|||||||
app = self.app
|
app = self.app
|
||||||
acc = self._palette["ACCENT"]
|
acc = self._palette["ACCENT"]
|
||||||
bar = self._sidebar
|
bar = self._sidebar
|
||||||
|
self._theme_switch_pop = None
|
||||||
for w in list(bar.winfo_children()):
|
for w in list(bar.winfo_children()):
|
||||||
try:
|
try:
|
||||||
w.destroy()
|
w.destroy()
|
||||||
@@ -1024,7 +1024,7 @@ class _OfficeShellV12:
|
|||||||
cb_pad = dict(
|
cb_pad = dict(
|
||||||
bg=acc, fg="white", font=FONT_DEFAULT,
|
bg=acc, fg="white", font=FONT_DEFAULT,
|
||||||
activebackground=acc, activeforeground="white",
|
activebackground=acc, activeforeground="white",
|
||||||
selectcolor="#E2EEF6", highlightthickness=0,
|
selectcolor="#1a4d6d", highlightthickness=0,
|
||||||
bd=0, anchor="w",
|
bd=0, anchor="w",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1147,30 +1147,6 @@ class _OfficeShellV12:
|
|||||||
lbl_full.pack(side="left")
|
lbl_full.pack(side="left")
|
||||||
lbl_full.bind("<Button-1>", lambda e: self._apply_opacity_percent_str("100"))
|
lbl_full.bind("<Button-1>", lambda e: self._apply_opacity_percent_str("100"))
|
||||||
|
|
||||||
tk.Label(
|
|
||||||
self._sec_ersch_body, text="Office-Hülle hell / dunkel",
|
|
||||||
bg=acc, fg="#E2EEF6", font=FONT_DEFAULT,
|
|
||||||
).pack(anchor="w", padx=14, pady=(8, 4))
|
|
||||||
|
|
||||||
row_th = tk.Frame(self._sec_ersch_body, bg=acc)
|
|
||||||
row_th.pack(fill="x", padx=12)
|
|
||||||
|
|
||||||
tk.Label(row_th, text="Hell", bg=acc, fg="white",
|
|
||||||
font=FONT_DEFAULT).pack(side="left", padx=(0, 6))
|
|
||||||
|
|
||||||
def _flip():
|
|
||||||
self._toggle_theme_main()
|
|
||||||
if self._theme_switch_pop is not None:
|
|
||||||
self._theme_switch_pop.set_dark(self._dark_mode)
|
|
||||||
|
|
||||||
self._theme_switch_pop = PopoverThemeSwitch(
|
|
||||||
row_th, is_dark=self._dark_mode, command=_flip, bg_accent=acc,
|
|
||||||
)
|
|
||||||
self._theme_switch_pop.pack(side="left")
|
|
||||||
|
|
||||||
tk.Label(row_th, text="Dunkel", bg=acc, fg="white",
|
|
||||||
font=FONT_DEFAULT).pack(side="left", padx=(6, 0))
|
|
||||||
|
|
||||||
tk.Frame(self._sec_ersch_body, bg=acc, height=8).pack()
|
tk.Frame(self._sec_ersch_body, bg=acc, height=8).pack()
|
||||||
|
|
||||||
link = tk.Label(
|
link = tk.Label(
|
||||||
|
|||||||
@@ -2066,7 +2066,8 @@ class KGDesktopApp(tk.Tk, TodoMixin, TextWindowsMixin, AzaDiktatMixin, AzaSettin
|
|||||||
self._main_hidden = True
|
self._main_hidden = True
|
||||||
self.withdraw()
|
self.withdraw()
|
||||||
return
|
return
|
||||||
self._return_to_launcher = True
|
# Normales Schliessen (X): Programm beenden. Launcher nur wenn
|
||||||
|
# vorher explizit _go_to_launcher() gesetzt hat (_return_to_launcher True).
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
def _go_to_launcher(self):
|
def _go_to_launcher(self):
|
||||||
@@ -5612,11 +5613,15 @@ WICHTIG unbedingt einhalten:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def _sync_empfang_after_kg_change(self, *, context: str = "Korrektur") -> None:
|
def _sync_empfang_after_kg_change(self, *, context: str = "Korrektur") -> None:
|
||||||
"""Empfang-Dialog: Felder aus aktueller KG fuellen und Auto-Sektionen
|
"""Empfang-Dialog: Felder aus aktueller KG fuellen.
|
||||||
in den Chat unten einfuegen sowie Hinweis im Chat-Verlauf posten.
|
|
||||||
|
Bei aktiviertem Autocopy (Therapie / Procedere) wird nur die untere
|
||||||
|
Nachrichten-Box befuellt — nicht der Chat-Verlauf.
|
||||||
|
|
||||||
Wird nach «Korrigieren», neuem Transkript und neuer KG aufgerufen.
|
Wird nach «Korrigieren», neuem Transkript und neuer KG aufgerufen.
|
||||||
|
Das Argument ``context`` bleibt fuer bestehende Aufrufer erhalten.
|
||||||
"""
|
"""
|
||||||
|
_ = context
|
||||||
dlg = getattr(self, "_empfang_dlg", None)
|
dlg = getattr(self, "_empfang_dlg", None)
|
||||||
if dlg is None:
|
if dlg is None:
|
||||||
return
|
return
|
||||||
@@ -5660,77 +5665,44 @@ WICHTIG unbedingt einhalten:
|
|||||||
for k, v in section_values.items():
|
for k, v in section_values.items():
|
||||||
_push_field(k, v)
|
_push_field(k, v)
|
||||||
|
|
||||||
|
def _sync_fmt_ther(val: str) -> str:
|
||||||
|
t = (val or "").strip()
|
||||||
|
if not t:
|
||||||
|
return ""
|
||||||
|
lines = t.split("\n")
|
||||||
|
first = lines[0].strip().lower().rstrip(":").rstrip(".")
|
||||||
|
if first in ("therapie", "therapieplan"):
|
||||||
|
rest = "\n".join(lines[1:]).strip()
|
||||||
|
return f"Therapie\n{rest}" if rest else "Therapie"
|
||||||
|
return f"Therapie\n{t}"
|
||||||
|
|
||||||
|
def _sync_fmt_proc(val: str) -> str:
|
||||||
|
t = (val or "").strip()
|
||||||
|
if not t:
|
||||||
|
return ""
|
||||||
|
lines = t.split("\n")
|
||||||
|
first = lines[0].strip().lower().rstrip(":").rstrip(".")
|
||||||
|
if first == "procedere":
|
||||||
|
rest = "\n".join(lines[1:]).strip()
|
||||||
|
return f"Procedere\n{rest}" if rest else "Procedere"
|
||||||
|
return f"Procedere\n{t}"
|
||||||
|
|
||||||
push_fn = copy_fns.get("push") if isinstance(copy_fns, dict) else None
|
push_fn = copy_fns.get("push") if isinstance(copy_fns, dict) else None
|
||||||
auto_pushed: list[str] = []
|
|
||||||
if callable(push_fn):
|
if callable(push_fn):
|
||||||
for key, label in (("ther", "Therapieplan"),
|
for key in ("ther", "proc"):
|
||||||
("proc", "Procedere"),
|
|
||||||
("patient", "Nr.")):
|
|
||||||
var = auto_vars.get(key)
|
var = auto_vars.get(key)
|
||||||
if var is None or not var.get():
|
if var is None or not var.get():
|
||||||
continue
|
continue
|
||||||
if key == "patient":
|
val = (section_values.get(key) or "").strip()
|
||||||
w = fw.get("patient")
|
if not val:
|
||||||
val = w.get().strip() if isinstance(w, tk.StringVar) else ""
|
continue
|
||||||
else:
|
try:
|
||||||
val = (section_values.get(key) or "").strip()
|
if key == "ther":
|
||||||
if val:
|
push_fn(_sync_fmt_ther(val), source=key)
|
||||||
try:
|
else:
|
||||||
push_fn(f"{label}:\n{val}", source=key)
|
push_fn(_sync_fmt_proc(val), source=key)
|
||||||
auto_pushed.append(label)
|
except Exception:
|
||||||
except Exception:
|
pass
|
||||||
pass
|
|
||||||
|
|
||||||
tp = section_values["ther"].strip()
|
|
||||||
pc = section_values["proc"].strip()
|
|
||||||
parts: list[str] = []
|
|
||||||
if tp:
|
|
||||||
parts.append(f"Therapieplan:\n{tp}")
|
|
||||||
if pc:
|
|
||||||
parts.append(f"Procedere:\n{pc}")
|
|
||||||
if not parts:
|
|
||||||
try:
|
|
||||||
kg = self.txt_output.get("1.0", "end").strip()
|
|
||||||
except Exception:
|
|
||||||
kg = ""
|
|
||||||
if kg:
|
|
||||||
excerpt = kg[:1200] + ("…" if len(kg) > 1200 else "")
|
|
||||||
parts.append(f"Krankengeschichte:\n{excerpt}")
|
|
||||||
suffix = ""
|
|
||||||
if auto_pushed:
|
|
||||||
suffix = (
|
|
||||||
"\n\n[Auto-Kopie in Chat unten: "
|
|
||||||
+ ", ".join(auto_pushed) + "]"
|
|
||||||
)
|
|
||||||
body = (
|
|
||||||
f"Krankengeschichte wurde aktualisiert ({context}).\n\n"
|
|
||||||
+ ("\n\n".join(parts) if parts else "(Kein Text)")
|
|
||||||
+ suffix
|
|
||||||
)
|
|
||||||
|
|
||||||
overlay = getattr(self, "_empfang_local_chat_overlay", None)
|
|
||||||
if overlay is None:
|
|
||||||
overlay = []
|
|
||||||
self._empfang_local_chat_overlay = overlay
|
|
||||||
name = (self._user_profile.get("name") or "").strip() or "Benutzer"
|
|
||||||
overlay.append({
|
|
||||||
"id": f"local-{time.time_ns()}",
|
|
||||||
"absender": f"{name} (lokal)",
|
|
||||||
"zeitstempel": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
||||||
"kommentar": body,
|
|
||||||
})
|
|
||||||
if len(overlay) > 25:
|
|
||||||
del overlay[:-25]
|
|
||||||
|
|
||||||
updater = getattr(self, "_empfang_update_chat_fn", None)
|
|
||||||
if callable(updater):
|
|
||||||
try:
|
|
||||||
base = getattr(self, "_empfang_last_thread_messages", None)
|
|
||||||
if base is None:
|
|
||||||
base = []
|
|
||||||
updater(base)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _empfang_background_poll(self):
|
def _empfang_background_poll(self):
|
||||||
"""Prueft im Hintergrund auf neue Empfangs-Nachrichten und zeigt Toast."""
|
"""Prueft im Hintergrund auf neue Empfangs-Nachrichten und zeigt Toast."""
|
||||||
@@ -5882,6 +5854,97 @@ WICHTIG unbedingt einhalten:
|
|||||||
outer = tk.Frame(dlg, bg="#E8F4FA")
|
outer = tk.Frame(dlg, bg="#E8F4FA")
|
||||||
outer.pack(fill="both", expand=True)
|
outer.pack(fill="both", expand=True)
|
||||||
|
|
||||||
|
def _compact_nr(s: str) -> str:
|
||||||
|
return re.sub(r"\s+", "", (s or "").strip())
|
||||||
|
|
||||||
|
# --- Smart-Pick (Pinsel): vor Patienten-Zeile benoetigt ---
|
||||||
|
_pick_listener = [None]
|
||||||
|
|
||||||
|
def _do_smart_pick(btn, on_result):
|
||||||
|
if _pick_listener[0] is not None:
|
||||||
|
try:
|
||||||
|
_pick_listener[0].stop()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
_pick_listener[0] = None
|
||||||
|
btn.configure(text="\U0001f58c Pinsel", bg="#dde8f0", fg="#1a4d6d")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
_old_clip = ""
|
||||||
|
if sys.platform == "win32":
|
||||||
|
_old_clip = (_win_clipboard_get() or "").strip()
|
||||||
|
if not _old_clip:
|
||||||
|
_old_clip = dlg.clipboard_get().strip()
|
||||||
|
except tk.TclError:
|
||||||
|
_old_clip = ""
|
||||||
|
|
||||||
|
btn.configure(text="\u23f9 Text markieren...", bg="#f8d7da", fg="#721c24")
|
||||||
|
_started = [time.time()]
|
||||||
|
_mouse_was_pressed = [False]
|
||||||
|
|
||||||
|
_press_time = [0.0]
|
||||||
|
|
||||||
|
def _on_click(x, y, button, pressed):
|
||||||
|
if button != MouseButton.left:
|
||||||
|
return
|
||||||
|
if pressed:
|
||||||
|
if time.time() - _started[0] > 0.3:
|
||||||
|
_mouse_was_pressed[0] = True
|
||||||
|
_press_time[0] = time.time()
|
||||||
|
return
|
||||||
|
if not _mouse_was_pressed[0]:
|
||||||
|
return
|
||||||
|
_mouse_was_pressed[0] = False
|
||||||
|
hold_duration = time.time() - _press_time[0]
|
||||||
|
was_drag = hold_duration > 0.20
|
||||||
|
|
||||||
|
time.sleep(0.05)
|
||||||
|
try:
|
||||||
|
kbd = KbdController()
|
||||||
|
if not was_drag:
|
||||||
|
from pynput.mouse import Controller as MController
|
||||||
|
mc = MController()
|
||||||
|
mc.click(MouseButton.left, 2)
|
||||||
|
time.sleep(0.1)
|
||||||
|
with kbd.pressed(Key.ctrl):
|
||||||
|
kbd.tap(KeyCode.from_char('c'))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(0.38)
|
||||||
|
|
||||||
|
def _grab():
|
||||||
|
cur = ""
|
||||||
|
try:
|
||||||
|
for _attempt in range(8):
|
||||||
|
cur = ""
|
||||||
|
if sys.platform == "win32":
|
||||||
|
cur = (_win_clipboard_get() or "").strip()
|
||||||
|
if not cur:
|
||||||
|
try:
|
||||||
|
cur = dlg.clipboard_get().strip()
|
||||||
|
except tk.TclError:
|
||||||
|
cur = ""
|
||||||
|
if cur and cur != _old_clip:
|
||||||
|
break
|
||||||
|
time.sleep(0.06)
|
||||||
|
if cur and cur != _old_clip:
|
||||||
|
on_result(cur)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
btn.configure(text="\U0001f58c Pinsel", bg="#dde8f0", fg="#1a4d6d")
|
||||||
|
_pick_listener[0] = None
|
||||||
|
|
||||||
|
self.after(0, _grab)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if _HAS_PYNPUT_MOUSE:
|
||||||
|
ml = MouseListener(on_click=_on_click)
|
||||||
|
_pick_listener[0] = ml
|
||||||
|
ml.start()
|
||||||
|
else:
|
||||||
|
btn.configure(text="\U0001f58c Pinsel", bg="#dde8f0", fg="#1a4d6d")
|
||||||
|
|
||||||
# --- Header mit Titel und Schriftgröße ---
|
# --- Header mit Titel und Schriftgröße ---
|
||||||
hdr = tk.Frame(outer, bg="#E8F4FA")
|
hdr = tk.Frame(outer, bg="#E8F4FA")
|
||||||
hdr.pack(fill="x", padx=14, pady=(8, 0))
|
hdr.pack(fill="x", padx=14, pady=(8, 0))
|
||||||
@@ -5889,11 +5952,142 @@ WICHTIG unbedingt einhalten:
|
|||||||
tk.Label(hdr, text="An Empfang senden", font=("Segoe UI", 12, "bold"),
|
tk.Label(hdr, text="An Empfang senden", font=("Segoe UI", 12, "bold"),
|
||||||
bg="#E8F4FA", fg="#1a4d6d").pack(side="left")
|
bg="#E8F4FA", fg="#1a4d6d").pack(side="left")
|
||||||
|
|
||||||
|
toggle_vars: dict[str, tk.BooleanVar] = {}
|
||||||
|
field_widgets: dict = {}
|
||||||
|
|
||||||
|
# Patienten-Nr. nur in dieser Kopfzeile (kein eigener Nr.-Abschnitt im Scrollbereich)
|
||||||
|
nr_banner_row = tk.Frame(outer, bg="#E8F4FA")
|
||||||
|
nr_banner_row.pack(fill="x", padx=14, pady=(2, 2))
|
||||||
|
tk.Label(nr_banner_row, text="Patienten-Nr.:",
|
||||||
|
font=("Segoe UI", 9), bg="#E8F4FA", fg="#5B8DB3").pack(side="left")
|
||||||
|
|
||||||
|
_pat_ph = "(keine)"
|
||||||
|
patient_sv = tk.StringVar()
|
||||||
|
_lp_init = (prefs.get("last_patient") or "").strip()
|
||||||
|
if _lp_init:
|
||||||
|
patient_sv.set(_lp_init)
|
||||||
|
else:
|
||||||
|
patient_sv.set(_pat_ph)
|
||||||
|
|
||||||
|
patient_ent = tk.Entry(
|
||||||
|
nr_banner_row,
|
||||||
|
textvariable=patient_sv,
|
||||||
|
font=("Consolas", 11),
|
||||||
|
bg="white",
|
||||||
|
fg="#aaa" if not _lp_init else "#1a4d6d",
|
||||||
|
relief="solid",
|
||||||
|
bd=1,
|
||||||
|
width=24,
|
||||||
|
)
|
||||||
|
patient_ent.pack(side="left", padx=(8, 4), fill="x", expand=True)
|
||||||
|
|
||||||
|
field_widgets["patient"] = patient_sv
|
||||||
|
toggle_vars["patient"] = tk.BooleanVar(value=True)
|
||||||
|
copy_to_chat_fns: dict = {}
|
||||||
|
|
||||||
|
def _pat_focus_in(_e=None):
|
||||||
|
if patient_sv.get().strip() == _pat_ph:
|
||||||
|
patient_sv.set("")
|
||||||
|
patient_ent.configure(fg="#1a4d6d")
|
||||||
|
|
||||||
|
def _pat_focus_out(_e=None):
|
||||||
|
if not patient_sv.get().strip():
|
||||||
|
patient_sv.set(_pat_ph)
|
||||||
|
patient_ent.configure(fg="#aaa")
|
||||||
|
else:
|
||||||
|
patient_ent.configure(fg="#1a4d6d")
|
||||||
|
|
||||||
|
patient_ent.bind("<FocusIn>", _pat_focus_in)
|
||||||
|
patient_ent.bind("<FocusOut>", _pat_focus_out)
|
||||||
|
|
||||||
|
def _patient_entry_copy_compact(_e=None):
|
||||||
|
raw = patient_sv.get().strip()
|
||||||
|
if raw == _pat_ph:
|
||||||
|
return "break"
|
||||||
|
nn = _compact_nr(raw)
|
||||||
|
if nn:
|
||||||
|
dlg.clipboard_clear()
|
||||||
|
dlg.clipboard_append(nn)
|
||||||
|
dlg.update_idletasks()
|
||||||
|
try:
|
||||||
|
self.set_status("Nr. kopiert (ohne Leerzeichen).")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
patient_ent.bind("<Double-Button-1>", _patient_entry_copy_compact)
|
||||||
|
|
||||||
|
def _patient_paste_from_clipboard(_e=None):
|
||||||
|
try:
|
||||||
|
txt = ""
|
||||||
|
if sys.platform == "win32":
|
||||||
|
txt = (_win_clipboard_get() or "").strip()
|
||||||
|
if not txt:
|
||||||
|
txt = dlg.clipboard_get().strip()
|
||||||
|
nn = _compact_nr(txt)
|
||||||
|
if len(nn) < 2:
|
||||||
|
return None
|
||||||
|
patient_sv.set(nn)
|
||||||
|
patient_ent.configure(fg="#1a4d6d")
|
||||||
|
return "break"
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
patient_ent.bind("<Control-v>", _patient_paste_from_clipboard)
|
||||||
|
patient_ent.bind("<Control-V>", _patient_paste_from_clipboard)
|
||||||
|
|
||||||
|
def _clear_nr_top(_e=None):
|
||||||
|
patient_sv.set(_pat_ph)
|
||||||
|
patient_ent.configure(fg="#aaa")
|
||||||
|
|
||||||
|
tk.Label(nr_banner_row, text="\u2715", font=("Segoe UI", 9),
|
||||||
|
bg="#E8F4FA", fg="#aaa", cursor="hand2").pack(side="left", padx=(2, 2))
|
||||||
|
nr_banner_row.winfo_children()[-1].bind("<Button-1>", _clear_nr_top)
|
||||||
|
|
||||||
|
pick_btn_nr_top = tk.Button(
|
||||||
|
nr_banner_row,
|
||||||
|
text="\U0001f58c Pinsel",
|
||||||
|
font=("Segoe UI", 8),
|
||||||
|
bg="#dde8f0",
|
||||||
|
fg="#1a4d6d",
|
||||||
|
relief="flat",
|
||||||
|
cursor="hand2",
|
||||||
|
bd=0,
|
||||||
|
padx=6,
|
||||||
|
pady=1,
|
||||||
|
)
|
||||||
|
pick_btn_nr_top.pack(side="left", padx=(2, 0))
|
||||||
|
|
||||||
|
def _start_pick_nr_top():
|
||||||
|
def _on_pick(t):
|
||||||
|
nn = _compact_nr(t)
|
||||||
|
if nn:
|
||||||
|
patient_sv.set(nn)
|
||||||
|
patient_ent.configure(fg="#1a4d6d")
|
||||||
|
|
||||||
|
def _follow():
|
||||||
|
fn = copy_to_chat_fns.get("push")
|
||||||
|
if callable(fn):
|
||||||
|
fn(f"Nr.: {nn}", source="patient")
|
||||||
|
|
||||||
|
dlg.after(120, _follow)
|
||||||
|
|
||||||
|
_do_smart_pick(pick_btn_nr_top, _on_pick)
|
||||||
|
|
||||||
|
pick_btn_nr_top.configure(command=_start_pick_nr_top)
|
||||||
|
add_tooltip(
|
||||||
|
pick_btn_nr_top,
|
||||||
|
"Text woanders markieren – Nr. erscheint hier und oben in der Nachrichten-Box.",
|
||||||
|
)
|
||||||
|
add_tooltip(
|
||||||
|
patient_ent,
|
||||||
|
"Patienten-Nr.; Doppelklick: ohne Leerzeichen kopieren.",
|
||||||
|
)
|
||||||
|
|
||||||
# Section-Sichtbarkeit (immer alle an, ohne Zahnrad-Menue)
|
# Section-Sichtbarkeit (immer alle an, ohne Zahnrad-Menue)
|
||||||
gear_vis = {
|
gear_vis = {
|
||||||
"patient": tk.BooleanVar(value=True),
|
"ther": tk.BooleanVar(value=True),
|
||||||
"ther": tk.BooleanVar(value=True),
|
"proc": tk.BooleanVar(value=True),
|
||||||
"proc": tk.BooleanVar(value=True),
|
|
||||||
}
|
}
|
||||||
_section_rows: dict[str, tk.Frame] = {}
|
_section_rows: dict[str, tk.Frame] = {}
|
||||||
|
|
||||||
@@ -6005,81 +6199,6 @@ WICHTIG unbedingt einhalten:
|
|||||||
_w.bind("<Enter>", lambda e, ww=_w: ww.configure(fg="#1a4d6d"))
|
_w.bind("<Enter>", lambda e, ww=_w: ww.configure(fg="#1a4d6d"))
|
||||||
_w.bind("<Leave>", lambda e, ww=_w: ww.configure(fg="#8AAFC0"))
|
_w.bind("<Leave>", lambda e, ww=_w: ww.configure(fg="#8AAFC0"))
|
||||||
|
|
||||||
# --- Smart-Pick: markieren + loslassen = uebernehmen ---
|
|
||||||
# Benutzer klickt Lupe → wechselt zu fremdem Fenster →
|
|
||||||
# markiert Text mit Maus → laesst los → Text wird uebernommen.
|
|
||||||
# KEIN manuelles Ctrl+C noetig.
|
|
||||||
_pick_listener = [None]
|
|
||||||
|
|
||||||
def _do_smart_pick(btn, on_result):
|
|
||||||
if _pick_listener[0] is not None:
|
|
||||||
try:
|
|
||||||
_pick_listener[0].stop()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
_pick_listener[0] = None
|
|
||||||
btn.configure(text="\U0001f58c Pinsel", bg="#dde8f0", fg="#1a4d6d")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
_old_clip = dlg.clipboard_get()
|
|
||||||
except tk.TclError:
|
|
||||||
_old_clip = ""
|
|
||||||
|
|
||||||
btn.configure(text="\u23f9 Text markieren...", bg="#f8d7da", fg="#721c24")
|
|
||||||
_started = [time.time()]
|
|
||||||
_mouse_was_pressed = [False]
|
|
||||||
|
|
||||||
_press_time = [0.0]
|
|
||||||
|
|
||||||
def _on_click(x, y, button, pressed):
|
|
||||||
if button != MouseButton.left:
|
|
||||||
return
|
|
||||||
if pressed:
|
|
||||||
if time.time() - _started[0] > 0.3:
|
|
||||||
_mouse_was_pressed[0] = True
|
|
||||||
_press_time[0] = time.time()
|
|
||||||
return
|
|
||||||
if not _mouse_was_pressed[0]:
|
|
||||||
return
|
|
||||||
_mouse_was_pressed[0] = False
|
|
||||||
hold_duration = time.time() - _press_time[0]
|
|
||||||
was_drag = hold_duration > 0.20
|
|
||||||
|
|
||||||
time.sleep(0.05)
|
|
||||||
try:
|
|
||||||
kbd = KbdController()
|
|
||||||
if not was_drag:
|
|
||||||
from pynput.mouse import Controller as MController
|
|
||||||
mc = MController()
|
|
||||||
mc.click(MouseButton.left, 2)
|
|
||||||
time.sleep(0.1)
|
|
||||||
with kbd.pressed(Key.ctrl):
|
|
||||||
kbd.tap(KeyCode.from_char('c'))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
time.sleep(0.2)
|
|
||||||
|
|
||||||
def _grab():
|
|
||||||
try:
|
|
||||||
cur = dlg.clipboard_get().strip()
|
|
||||||
if cur and cur != _old_clip:
|
|
||||||
on_result(cur)
|
|
||||||
except tk.TclError:
|
|
||||||
pass
|
|
||||||
btn.configure(text="\U0001f58c Pinsel", bg="#dde8f0", fg="#1a4d6d")
|
|
||||||
_pick_listener[0] = None
|
|
||||||
|
|
||||||
self.after(0, _grab)
|
|
||||||
return False
|
|
||||||
|
|
||||||
if _HAS_PYNPUT_MOUSE:
|
|
||||||
ml = MouseListener(on_click=_on_click)
|
|
||||||
_pick_listener[0] = ml
|
|
||||||
ml.start()
|
|
||||||
else:
|
|
||||||
btn.configure(text="\U0001f58c Pinsel", bg="#dde8f0", fg="#1a4d6d")
|
|
||||||
|
|
||||||
# --- Feste Knopfleiste am unteren Rand ---
|
# --- Feste Knopfleiste am unteren Rand ---
|
||||||
bottom_bar = tk.Frame(outer, bg="#E8F4FA")
|
bottom_bar = tk.Frame(outer, bg="#E8F4FA")
|
||||||
bottom_bar.pack(side="bottom", fill="x", padx=14, pady=(4, 8))
|
bottom_bar.pack(side="bottom", fill="x", padx=14, pady=(4, 8))
|
||||||
@@ -6107,20 +6226,38 @@ WICHTIG unbedingt einhalten:
|
|||||||
dlg.bind("<Destroy>", lambda e: canvas.unbind_all("<MouseWheel>") if e.widget is dlg else None)
|
dlg.bind("<Destroy>", lambda e: canvas.unbind_all("<MouseWheel>") if e.widget is dlg else None)
|
||||||
|
|
||||||
_field_defs_raw = [
|
_field_defs_raw = [
|
||||||
("patient", "Nr.", prefs.get("last_patient", "")),
|
("ther", "Therapieplan", extracted["therapieplan"]),
|
||||||
("ther", "Therapieplan", extracted["therapieplan"]),
|
("proc", "Procedere", extracted["procedere"]),
|
||||||
("proc", "Procedere", extracted["procedere"]),
|
|
||||||
]
|
]
|
||||||
field_defs = list(_field_defs_raw)
|
field_defs = list(_field_defs_raw)
|
||||||
|
|
||||||
toggle_vars: dict[str, tk.BooleanVar] = {}
|
|
||||||
content_frames: dict[str, tk.Frame] = {}
|
content_frames: dict[str, tk.Frame] = {}
|
||||||
field_widgets: dict = {}
|
|
||||||
auto_copy_vars: dict[str, tk.BooleanVar] = {
|
auto_copy_vars: dict[str, tk.BooleanVar] = {
|
||||||
k: tk.BooleanVar(value=bool(prefs.get(f"auto_copy_{k}", False)))
|
k: tk.BooleanVar(value=bool(prefs.get(f"auto_copy_{k}", False)))
|
||||||
for k, _, _ in _field_defs_raw
|
for k, _, _ in _field_defs_raw
|
||||||
}
|
}
|
||||||
copy_to_chat_fns: dict = {}
|
|
||||||
|
def _format_ther_for_push(raw: str) -> str:
|
||||||
|
t = (raw or "").strip()
|
||||||
|
if not t:
|
||||||
|
return ""
|
||||||
|
lines = t.split("\n")
|
||||||
|
first = lines[0].strip().lower().rstrip(":").rstrip(".")
|
||||||
|
if first in ("therapie", "therapieplan"):
|
||||||
|
rest = "\n".join(lines[1:]).strip()
|
||||||
|
return f"Therapie\n{rest}" if rest else "Therapie"
|
||||||
|
return f"Therapie\n{t}"
|
||||||
|
|
||||||
|
def _format_proc_for_push(raw: str) -> str:
|
||||||
|
t = (raw or "").strip()
|
||||||
|
if not t:
|
||||||
|
return ""
|
||||||
|
lines = t.split("\n")
|
||||||
|
first = lines[0].strip().lower().rstrip(":").rstrip(".")
|
||||||
|
if first == "procedere":
|
||||||
|
rest = "\n".join(lines[1:]).strip()
|
||||||
|
return f"Procedere\n{rest}" if rest else "Procedere"
|
||||||
|
return f"Procedere\n{t}"
|
||||||
|
|
||||||
for key, label, initial_val in field_defs:
|
for key, label, initial_val in field_defs:
|
||||||
has_content = bool(initial_val and initial_val.strip())
|
has_content = bool(initial_val and initial_val.strip())
|
||||||
@@ -6154,98 +6291,97 @@ WICHTIG unbedingt einhalten:
|
|||||||
if not callable(fn):
|
if not callable(fn):
|
||||||
return
|
return
|
||||||
val = _get_text(_k)
|
val = _get_text(_k)
|
||||||
if val:
|
if not val:
|
||||||
fn(val, source=_k)
|
return
|
||||||
|
if _k == "ther":
|
||||||
|
ft = _format_ther_for_push(val)
|
||||||
|
if ft:
|
||||||
|
fn(ft, source=_k)
|
||||||
|
return
|
||||||
|
fp = _format_proc_for_push(val)
|
||||||
|
if fp:
|
||||||
|
fn(fp, source=_k)
|
||||||
|
|
||||||
copy_btn = tk.Button(
|
def _on_copy_link_click(_e, _k=k):
|
||||||
header, text="Kopieren", font=("Segoe UI", 9),
|
_copy_section_to_chat(_k)
|
||||||
bg="#dde8f0", fg="#1a4d6d",
|
return "break"
|
||||||
activebackground="#c8d8e6", relief="flat", bd=0,
|
|
||||||
cursor="hand2", padx=10, pady=2, width=9,
|
copy_lbl = tk.Label(
|
||||||
command=_copy_section_to_chat,
|
header,
|
||||||
|
text="Kopieren",
|
||||||
|
font=("Segoe UI", 9, "underline"),
|
||||||
|
bg="#E8F4FA",
|
||||||
|
fg="#2563ab",
|
||||||
|
cursor="hand2",
|
||||||
|
)
|
||||||
|
copy_lbl.pack(side="left", padx=(8, 6))
|
||||||
|
copy_lbl.bind("<Button-1>", _on_copy_link_click)
|
||||||
|
add_tooltip(
|
||||||
|
copy_lbl,
|
||||||
|
"Inhalt in die Nachrichten-Box unten einfuegen (nicht in den Chat-Verlauf).",
|
||||||
)
|
)
|
||||||
copy_btn.pack(side="left", padx=(8, 6))
|
|
||||||
add_tooltip(copy_btn, "Inhalt unten in den Chat einfuegen")
|
|
||||||
|
|
||||||
def _on_auto_change(_k=k):
|
def _on_auto_change(_k=k):
|
||||||
prefs[f"auto_copy_{_k}"] = auto_copy_vars[_k].get()
|
acv = auto_copy_vars.get(_k)
|
||||||
|
if acv is None:
|
||||||
|
return
|
||||||
|
prefs[f"auto_copy_{_k}"] = acv.get()
|
||||||
self._autotext_data["empfang_prefs"] = prefs
|
self._autotext_data["empfang_prefs"] = prefs
|
||||||
save_autotext(self._autotext_data)
|
save_autotext(self._autotext_data)
|
||||||
if auto_copy_vars[_k].get():
|
if acv.get():
|
||||||
_copy_section_to_chat(_k)
|
_copy_section_to_chat(_k)
|
||||||
|
|
||||||
auto_cb = tk.Checkbutton(
|
auto_cb = tk.Checkbutton(
|
||||||
header, text="Auto", font=("Segoe UI", 9),
|
header,
|
||||||
variable=auto_copy_vars[k], bg="#E8F4FA", fg="#1a4d6d",
|
text="Autocopy",
|
||||||
activebackground="#E8F4FA", activeforeground="#1a4d6d",
|
font=("Segoe UI", 9),
|
||||||
selectcolor="#FFFFFF", highlightthickness=0, bd=0,
|
variable=auto_copy_vars[k],
|
||||||
cursor="hand2", padx=4,
|
bg="#E8F4FA",
|
||||||
|
fg="#1a4d6d",
|
||||||
|
activebackground="#E8F4FA",
|
||||||
|
activeforeground="#1a4d6d",
|
||||||
|
selectcolor="#FFFFFF",
|
||||||
|
highlightthickness=0,
|
||||||
|
bd=0,
|
||||||
|
cursor="hand2",
|
||||||
|
padx=4,
|
||||||
command=_on_auto_change,
|
command=_on_auto_change,
|
||||||
)
|
)
|
||||||
auto_cb.pack(side="left")
|
auto_cb.pack(side="left")
|
||||||
add_tooltip(
|
add_tooltip(
|
||||||
auto_cb,
|
auto_cb,
|
||||||
"Automatisch in den Chat unten einfuegen, wenn KG aktualisiert wird",
|
"Wenn aktiv: Abschnitt bei KG-Updates automatisch in die Nachrichten-Box.",
|
||||||
)
|
)
|
||||||
|
|
||||||
body = tk.Frame(r, bg="#E8F4FA")
|
body = tk.Frame(r, bg="#E8F4FA")
|
||||||
content_frames[k] = body
|
content_frames[k] = body
|
||||||
|
|
||||||
if k == "patient":
|
t = tk.Text(body, height=3, wrap="word",
|
||||||
sv = tk.StringVar(value=init)
|
font=("Segoe UI", _empfang_font_size[0]),
|
||||||
pat_row = tk.Frame(body, bg="#E8F4FA")
|
bg="white", fg="#1a2a3a", relief="solid", bd=1,
|
||||||
pat_row.pack(fill="x", padx=4, pady=(2, 4))
|
insertbackground="#1a4d6d", padx=4, pady=4)
|
||||||
e = ttk.Entry(pat_row, textvariable=sv, width=40)
|
t.pack(fill="x", padx=4, pady=(2, 4))
|
||||||
e.pack(side="left", fill="x", expand=True)
|
_ph = "Text eingeben oder diktieren..."
|
||||||
|
if init:
|
||||||
def _start_pick_nr():
|
t.insert("1.0", init)
|
||||||
_do_smart_pick(pick_btn_nr, lambda t: sv.set(t))
|
|
||||||
|
|
||||||
def _clear_nr():
|
|
||||||
sv.set("")
|
|
||||||
|
|
||||||
tk.Label(pat_row, text="\u2715", font=("Segoe UI", 9),
|
|
||||||
bg="#E8F4FA", fg="#aaa", cursor="hand2").pack(
|
|
||||||
side="left", padx=(4, 0))
|
|
||||||
pat_row.winfo_children()[-1].bind(
|
|
||||||
"<Button-1>", lambda e: _clear_nr())
|
|
||||||
|
|
||||||
pick_btn_nr = tk.Button(
|
|
||||||
pat_row, text="\U0001f58c Pinsel",
|
|
||||||
font=("Segoe UI", 8), bg="#dde8f0",
|
|
||||||
fg="#1a4d6d", relief="flat", cursor="hand2",
|
|
||||||
command=_start_pick_nr, bd=0, padx=6, pady=1)
|
|
||||||
pick_btn_nr.pack(side="left", padx=(4, 0))
|
|
||||||
add_tooltip(pick_btn_nr,
|
|
||||||
"Text in anderer App markieren - wird automatisch uebernommen")
|
|
||||||
field_widgets[k] = sv
|
|
||||||
else:
|
else:
|
||||||
t = tk.Text(body, height=3, wrap="word",
|
t.insert("1.0", _ph)
|
||||||
font=("Segoe UI", _empfang_font_size[0]),
|
t.configure(fg="#aaa")
|
||||||
bg="white", fg="#1a2a3a", relief="solid", bd=1,
|
|
||||||
insertbackground="#1a4d6d", padx=4, pady=4)
|
|
||||||
t.pack(fill="x", padx=4, pady=(2, 4))
|
|
||||||
_ph = "Text eingeben oder diktieren..."
|
|
||||||
if init:
|
|
||||||
t.insert("1.0", init)
|
|
||||||
else:
|
|
||||||
t.insert("1.0", _ph)
|
|
||||||
t.configure(fg="#aaa")
|
|
||||||
|
|
||||||
def _ph_focus_in(e, _t=t, _p=_ph):
|
def _ph_focus_in(e, _t=t, _p=_ph):
|
||||||
if _t.get("1.0", "end").strip() == _p:
|
if _t.get("1.0", "end").strip() == _p:
|
||||||
_t.delete("1.0", "end")
|
_t.delete("1.0", "end")
|
||||||
_t.configure(fg="#1a2a3a")
|
_t.configure(fg="#1a2a3a")
|
||||||
|
|
||||||
def _ph_focus_out(e, _t=t, _p=_ph):
|
def _ph_focus_out(e, _t=t, _p=_ph):
|
||||||
if not _t.get("1.0", "end").strip():
|
if not _t.get("1.0", "end").strip():
|
||||||
_t.insert("1.0", _p)
|
_t.insert("1.0", _p)
|
||||||
_t.configure(fg="#aaa")
|
_t.configure(fg="#aaa")
|
||||||
|
|
||||||
t.bind("<FocusIn>", _ph_focus_in, add="+")
|
t.bind("<FocusIn>", _ph_focus_in, add="+")
|
||||||
t.bind("<FocusOut>", _ph_focus_out, add="+")
|
t.bind("<FocusOut>", _ph_focus_out, add="+")
|
||||||
field_widgets[k] = t
|
field_widgets[k] = t
|
||||||
_empfang_text_widgets.append(t)
|
_empfang_text_widgets.append(t)
|
||||||
|
|
||||||
if v.get():
|
if v.get():
|
||||||
body.pack(fill="x")
|
body.pack(fill="x")
|
||||||
@@ -6282,7 +6418,10 @@ WICHTIG unbedingt einhalten:
|
|||||||
if w is None:
|
if w is None:
|
||||||
return ""
|
return ""
|
||||||
if isinstance(w, tk.StringVar):
|
if isinstance(w, tk.StringVar):
|
||||||
return w.get().strip()
|
tt = w.get().strip()
|
||||||
|
if tt == _pat_ph:
|
||||||
|
return ""
|
||||||
|
return tt
|
||||||
val = w.get("1.0", "end").strip()
|
val = w.get("1.0", "end").strip()
|
||||||
if val in _empty_placeholders:
|
if val in _empty_placeholders:
|
||||||
return ""
|
return ""
|
||||||
@@ -6378,13 +6517,14 @@ WICHTIG unbedingt einhalten:
|
|||||||
def _flash_bg(success):
|
def _flash_bg(success):
|
||||||
color = "#e8f5e9" if success else "#fce4ec"
|
color = "#e8f5e9" if success else "#fce4ec"
|
||||||
orig = "#E8F4FA"
|
orig = "#E8F4FA"
|
||||||
for w in [outer, hdr] + list(hdr.winfo_children()):
|
_flash_widgets = [outer, hdr, nr_banner_row]
|
||||||
|
for w in _flash_widgets + list(hdr.winfo_children()):
|
||||||
try:
|
try:
|
||||||
w.configure(bg=color)
|
w.configure(bg=color)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
def _restore():
|
def _restore():
|
||||||
for w2 in [outer, hdr] + list(hdr.winfo_children()):
|
for w2 in _flash_widgets + list(hdr.winfo_children()):
|
||||||
try:
|
try:
|
||||||
w2.configure(bg=orig)
|
w2.configure(bg=orig)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -6419,13 +6559,26 @@ WICHTIG unbedingt einhalten:
|
|||||||
font=("Segoe UI", max(7, _empfang_font_size[0] - 2)))
|
font=("Segoe UI", max(7, _empfang_font_size[0] - 2)))
|
||||||
chat_display.tag_configure("new_msg", background="#e8f5e9")
|
chat_display.tag_configure("new_msg", background="#e8f5e9")
|
||||||
|
|
||||||
reply_entry = tk.Text(inner, height=4, wrap="word",
|
reply_holder = tk.Frame(inner, bg="#E8F4FA")
|
||||||
|
reply_holder.pack(fill="x", pady=(0, 2))
|
||||||
|
|
||||||
|
reply_entry = tk.Text(reply_holder, height=8, wrap="word",
|
||||||
font=("Segoe UI", _empfang_font_size[0]),
|
font=("Segoe UI", _empfang_font_size[0]),
|
||||||
bg="white", fg="#1a2a3a", relief="solid", bd=1,
|
bg="white", fg="#1a2a3a", relief="solid", bd=1,
|
||||||
padx=4, pady=4)
|
padx=4, pady=4)
|
||||||
reply_entry.pack(fill="x", pady=(0, 2))
|
reply_sb = ttk.Scrollbar(reply_holder, orient="vertical",
|
||||||
|
command=reply_entry.yview)
|
||||||
|
reply_entry.configure(yscrollcommand=reply_sb.set)
|
||||||
|
reply_entry.pack(side="left", fill="both", expand=True)
|
||||||
|
reply_sb.pack(side="right", fill="y")
|
||||||
_empfang_text_widgets.append(reply_entry)
|
_empfang_text_widgets.append(reply_entry)
|
||||||
|
|
||||||
|
def _reply_wheel(_e):
|
||||||
|
reply_entry.yview_scroll(int(-1 * (_e.delta / 120)), "units")
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
reply_entry.bind("<MouseWheel>", _reply_wheel)
|
||||||
|
|
||||||
_reply_placeholder = "Antwort eingeben oder diktieren..."
|
_reply_placeholder = "Antwort eingeben oder diktieren..."
|
||||||
reply_entry.insert("1.0", _reply_placeholder)
|
reply_entry.insert("1.0", _reply_placeholder)
|
||||||
reply_entry.configure(fg="#aaa")
|
reply_entry.configure(fg="#aaa")
|
||||||
@@ -6489,14 +6642,27 @@ WICHTIG unbedingt einhalten:
|
|||||||
def _push_to_chat_input(text: str, *, source: str = "") -> None:
|
def _push_to_chat_input(text: str, *, source: str = "") -> None:
|
||||||
"""Fuegt Text in das Chat-Eingabefeld unten ein (Platzhalter wird ersetzt)."""
|
"""Fuegt Text in das Chat-Eingabefeld unten ein (Platzhalter wird ersetzt)."""
|
||||||
try:
|
try:
|
||||||
|
chunk = text.strip()
|
||||||
|
if source == "patient":
|
||||||
|
line = chunk if chunk.lower().startswith("nr.:") else f"Nr.: {_compact_nr(chunk)}"
|
||||||
|
cur = reply_entry.get("1.0", "end").strip()
|
||||||
|
empty = cur == _reply_placeholder or not cur
|
||||||
|
if empty:
|
||||||
|
reply_entry.delete("1.0", "end")
|
||||||
|
reply_entry.insert("1.0", line)
|
||||||
|
else:
|
||||||
|
reply_entry.insert("1.0", line + "\n")
|
||||||
|
reply_entry.configure(fg="#1a2a3a")
|
||||||
|
reply_entry.see("1.0")
|
||||||
|
return
|
||||||
cur = reply_entry.get("1.0", "end").strip()
|
cur = reply_entry.get("1.0", "end").strip()
|
||||||
if cur == _reply_placeholder or not cur:
|
if cur == _reply_placeholder or not cur:
|
||||||
reply_entry.delete("1.0", "end")
|
reply_entry.delete("1.0", "end")
|
||||||
reply_entry.insert("1.0", text.strip())
|
reply_entry.insert("1.0", chunk)
|
||||||
else:
|
else:
|
||||||
if not cur.endswith("\n"):
|
if not cur.endswith("\n"):
|
||||||
reply_entry.insert("end", "\n")
|
reply_entry.insert("end", "\n")
|
||||||
reply_entry.insert("end", text.strip())
|
reply_entry.insert("end", chunk)
|
||||||
reply_entry.configure(fg="#1a2a3a")
|
reply_entry.configure(fg="#1a2a3a")
|
||||||
reply_entry.see("end")
|
reply_entry.see("end")
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -6513,8 +6679,7 @@ WICHTIG unbedingt einhalten:
|
|||||||
except Exception:
|
except Exception:
|
||||||
base_msgs = []
|
base_msgs = []
|
||||||
self._empfang_last_thread_messages = base_msgs
|
self._empfang_last_thread_messages = base_msgs
|
||||||
local_extra = list(getattr(self, "_empfang_local_chat_overlay", None) or [])
|
merged = base_msgs
|
||||||
merged = base_msgs + local_extra
|
|
||||||
chat_display.configure(state="normal")
|
chat_display.configure(state="normal")
|
||||||
chat_display.delete("1.0", "end")
|
chat_display.delete("1.0", "end")
|
||||||
for msg in merged:
|
for msg in merged:
|
||||||
|
|||||||
@@ -48,19 +48,22 @@
|
|||||||
"dermatology"
|
"dermatology"
|
||||||
],
|
],
|
||||||
"ui_font_delta": 0,
|
"ui_font_delta": 0,
|
||||||
"global_right_click_paste": true,
|
"global_right_click_paste": false,
|
||||||
"todo_auto_open": false,
|
"todo_auto_open": false,
|
||||||
"autocopy_after_diktat": true,
|
"autocopy_after_diktat": true,
|
||||||
"kommentare_auto_open": true,
|
"kommentare_auto_open": true,
|
||||||
"empfang_auto_open": true,
|
"empfang_auto_open": true,
|
||||||
"empfang_was_open": false,
|
"empfang_was_open": false,
|
||||||
"empfang_prefs": {
|
"empfang_prefs": {
|
||||||
"show_patient": false,
|
"show_patient": true,
|
||||||
"show_ther": false,
|
"show_ther": true,
|
||||||
"show_proc": false,
|
"show_proc": true,
|
||||||
"show_kom": false,
|
"show_kom": true,
|
||||||
"last_patient": "",
|
"last_patient": "24227",
|
||||||
"geometry": "776x779+2208+366"
|
"geometry": "888x974+442+450",
|
||||||
|
"auto_copy_ther": true,
|
||||||
|
"auto_copy_patient": false,
|
||||||
|
"auto_copy_proc": true
|
||||||
},
|
},
|
||||||
"medikament_quelle": "compendium.ch",
|
"medikament_quelle": "compendium.ch",
|
||||||
"diagnose_quelle": "",
|
"diagnose_quelle": "",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"⏺ Start": 16
|
"⏺ Start": 12,
|
||||||
|
"⏺ Aufnahme starten": 2
|
||||||
}
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
420x380+2034+450
|
420x380+1187+399
|
||||||
@@ -1 +1 @@
|
|||||||
{"A": 0, "S": 0, "O": 0, "B": 0, "D": 3, "T": 0, "P": 0}
|
{"A": 0, "S": 0, "O": 0, "B": 0, "D": 0, "T": 0, "P": 0}
|
||||||
@@ -1 +1 @@
|
|||||||
{"used": 534202, "total": 1000000, "budget_dollars": 0, "used_dollars": 0}
|
{"used": 551259, "total": 1000000, "budget_dollars": 0, "used_dollars": 0}
|
||||||
@@ -1 +1 @@
|
|||||||
1123 1249 2580 227 0 0
|
1165 1249 1337 455 0 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.0",
|
"version": "1.2.0",
|
||||||
"channel": "stable",
|
"channel": "stable",
|
||||||
"release_date": "2026-03-14",
|
"release_date": "2026-03-14",
|
||||||
"minimum_supported_version": "1.0.0",
|
"minimum_supported_version": "1.0.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user