update
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Auto-generated by aza_build_stamp.py – DO NOT EDIT
|
||||
BUILD_TIME = "2026-05-03 19:14:17"
|
||||
BUILD_TIMESTAMP = "20260503_191417"
|
||||
GIT_COMMIT = "d4822fc8"
|
||||
BUILD_TIME = "2026-05-04 23:40:27"
|
||||
BUILD_TIMESTAMP = "20260504_234027"
|
||||
GIT_COMMIT = "03d188d1"
|
||||
GIT_BRANCH = "main"
|
||||
GIT_DIRTY = True
|
||||
|
||||
@@ -13,10 +13,9 @@ Aufbauend auf V1.1:
|
||||
``_send_to_empfang``).
|
||||
* **Erscheinungsbild**: dieselbe **Transparenz-Logik** wie in der klassischen
|
||||
Hauptfenster-Kopfzeile (``_opacity_var_main``, ``MIN_OPACITY``, ``save_opacity``)
|
||||
sowie Zugriff auf **alle Einstellungen** (``_open_settings``). Zusätzlich
|
||||
weiterhin **Hell/Dunkel** für die Office-Hülle-Farbpalette (persistiert),
|
||||
da das klassische ``basis14``-Hauptfenster keine globale Farb-Umschaltung hat — nur
|
||||
Transparenz und das separate Einstellungsfenster.
|
||||
sowie Zugriff auf **alle Einstellungen** (``_open_settings``). Die Farbpalette
|
||||
der Office-Hülle folgt der beim Start geladenen Hell/Dunkel-Präferenz;
|
||||
Umschalten nur noch über das klassische Einstellungsfenster, falls dort angeboten.
|
||||
* Footer-Branding und Logo ca. **30 % größer** als in V1.1.
|
||||
|
||||
Technische Strategie
|
||||
@@ -738,7 +737,7 @@ class _OfficeShellV12:
|
||||
pad = dict(
|
||||
bg=acc, fg="white", font=FONT_DEFAULT,
|
||||
activebackground=acc, activeforeground="white",
|
||||
selectcolor="#E2EEF6", highlightthickness=0,
|
||||
selectcolor="#1a4d6d", highlightthickness=0,
|
||||
bd=0, anchor="w",
|
||||
)
|
||||
|
||||
@@ -1015,6 +1014,7 @@ class _OfficeShellV12:
|
||||
app = self.app
|
||||
acc = self._palette["ACCENT"]
|
||||
bar = self._sidebar
|
||||
self._theme_switch_pop = None
|
||||
for w in list(bar.winfo_children()):
|
||||
try:
|
||||
w.destroy()
|
||||
@@ -1024,7 +1024,7 @@ class _OfficeShellV12:
|
||||
cb_pad = dict(
|
||||
bg=acc, fg="white", font=FONT_DEFAULT,
|
||||
activebackground=acc, activeforeground="white",
|
||||
selectcolor="#E2EEF6", highlightthickness=0,
|
||||
selectcolor="#1a4d6d", highlightthickness=0,
|
||||
bd=0, anchor="w",
|
||||
)
|
||||
|
||||
@@ -1147,30 +1147,6 @@ class _OfficeShellV12:
|
||||
lbl_full.pack(side="left")
|
||||
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()
|
||||
|
||||
link = tk.Label(
|
||||
|
||||
@@ -2066,7 +2066,8 @@ class KGDesktopApp(tk.Tk, TodoMixin, TextWindowsMixin, AzaDiktatMixin, AzaSettin
|
||||
self._main_hidden = True
|
||||
self.withdraw()
|
||||
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()
|
||||
|
||||
def _go_to_launcher(self):
|
||||
@@ -5612,11 +5613,15 @@ WICHTIG unbedingt einhalten:
|
||||
return result
|
||||
|
||||
def _sync_empfang_after_kg_change(self, *, context: str = "Korrektur") -> None:
|
||||
"""Empfang-Dialog: Felder aus aktueller KG fuellen und Auto-Sektionen
|
||||
in den Chat unten einfuegen sowie Hinweis im Chat-Verlauf posten.
|
||||
"""Empfang-Dialog: Felder aus aktueller KG fuellen.
|
||||
|
||||
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.
|
||||
Das Argument ``context`` bleibt fuer bestehende Aufrufer erhalten.
|
||||
"""
|
||||
_ = context
|
||||
dlg = getattr(self, "_empfang_dlg", None)
|
||||
if dlg is None:
|
||||
return
|
||||
@@ -5660,75 +5665,42 @@ WICHTIG unbedingt einhalten:
|
||||
for k, v in section_values.items():
|
||||
_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
|
||||
auto_pushed: list[str] = []
|
||||
if callable(push_fn):
|
||||
for key, label in (("ther", "Therapieplan"),
|
||||
("proc", "Procedere"),
|
||||
("patient", "Nr.")):
|
||||
for key in ("ther", "proc"):
|
||||
var = auto_vars.get(key)
|
||||
if var is None or not var.get():
|
||||
continue
|
||||
if key == "patient":
|
||||
w = fw.get("patient")
|
||||
val = w.get().strip() if isinstance(w, tk.StringVar) else ""
|
||||
else:
|
||||
val = (section_values.get(key) or "").strip()
|
||||
if val:
|
||||
if not val:
|
||||
continue
|
||||
try:
|
||||
push_fn(f"{label}:\n{val}", source=key)
|
||||
auto_pushed.append(label)
|
||||
except Exception:
|
||||
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)
|
||||
if key == "ther":
|
||||
push_fn(_sync_fmt_ther(val), source=key)
|
||||
else:
|
||||
push_fn(_sync_fmt_proc(val), source=key)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -5882,6 +5854,97 @@ WICHTIG unbedingt einhalten:
|
||||
outer = tk.Frame(dlg, bg="#E8F4FA")
|
||||
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 ---
|
||||
hdr = tk.Frame(outer, bg="#E8F4FA")
|
||||
hdr.pack(fill="x", padx=14, pady=(8, 0))
|
||||
@@ -5889,9 +5952,140 @@ WICHTIG unbedingt einhalten:
|
||||
tk.Label(hdr, text="An Empfang senden", font=("Segoe UI", 12, "bold"),
|
||||
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)
|
||||
gear_vis = {
|
||||
"patient": tk.BooleanVar(value=True),
|
||||
"ther": tk.BooleanVar(value=True),
|
||||
"proc": tk.BooleanVar(value=True),
|
||||
}
|
||||
@@ -6005,81 +6199,6 @@ WICHTIG unbedingt einhalten:
|
||||
_w.bind("<Enter>", lambda e, ww=_w: ww.configure(fg="#1a4d6d"))
|
||||
_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 ---
|
||||
bottom_bar = tk.Frame(outer, bg="#E8F4FA")
|
||||
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)
|
||||
|
||||
_field_defs_raw = [
|
||||
("patient", "Nr.", prefs.get("last_patient", "")),
|
||||
("ther", "Therapieplan", extracted["therapieplan"]),
|
||||
("proc", "Procedere", extracted["procedere"]),
|
||||
]
|
||||
field_defs = list(_field_defs_raw)
|
||||
|
||||
toggle_vars: dict[str, tk.BooleanVar] = {}
|
||||
content_frames: dict[str, tk.Frame] = {}
|
||||
field_widgets: dict = {}
|
||||
auto_copy_vars: dict[str, tk.BooleanVar] = {
|
||||
k: tk.BooleanVar(value=bool(prefs.get(f"auto_copy_{k}", False)))
|
||||
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:
|
||||
has_content = bool(initial_val and initial_val.strip())
|
||||
@@ -6154,72 +6291,71 @@ WICHTIG unbedingt einhalten:
|
||||
if not callable(fn):
|
||||
return
|
||||
val = _get_text(_k)
|
||||
if val:
|
||||
fn(val, source=_k)
|
||||
if not val:
|
||||
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(
|
||||
header, text="Kopieren", font=("Segoe UI", 9),
|
||||
bg="#dde8f0", fg="#1a4d6d",
|
||||
activebackground="#c8d8e6", relief="flat", bd=0,
|
||||
cursor="hand2", padx=10, pady=2, width=9,
|
||||
command=_copy_section_to_chat,
|
||||
def _on_copy_link_click(_e, _k=k):
|
||||
_copy_section_to_chat(_k)
|
||||
return "break"
|
||||
|
||||
copy_lbl = tk.Label(
|
||||
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):
|
||||
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
|
||||
save_autotext(self._autotext_data)
|
||||
if auto_copy_vars[_k].get():
|
||||
if acv.get():
|
||||
_copy_section_to_chat(_k)
|
||||
|
||||
auto_cb = tk.Checkbutton(
|
||||
header, text="Auto", font=("Segoe UI", 9),
|
||||
variable=auto_copy_vars[k], bg="#E8F4FA", fg="#1a4d6d",
|
||||
activebackground="#E8F4FA", activeforeground="#1a4d6d",
|
||||
selectcolor="#FFFFFF", highlightthickness=0, bd=0,
|
||||
cursor="hand2", padx=4,
|
||||
header,
|
||||
text="Autocopy",
|
||||
font=("Segoe UI", 9),
|
||||
variable=auto_copy_vars[k],
|
||||
bg="#E8F4FA",
|
||||
fg="#1a4d6d",
|
||||
activebackground="#E8F4FA",
|
||||
activeforeground="#1a4d6d",
|
||||
selectcolor="#FFFFFF",
|
||||
highlightthickness=0,
|
||||
bd=0,
|
||||
cursor="hand2",
|
||||
padx=4,
|
||||
command=_on_auto_change,
|
||||
)
|
||||
auto_cb.pack(side="left")
|
||||
add_tooltip(
|
||||
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")
|
||||
content_frames[k] = body
|
||||
|
||||
if k == "patient":
|
||||
sv = tk.StringVar(value=init)
|
||||
pat_row = tk.Frame(body, bg="#E8F4FA")
|
||||
pat_row.pack(fill="x", padx=4, pady=(2, 4))
|
||||
e = ttk.Entry(pat_row, textvariable=sv, width=40)
|
||||
e.pack(side="left", fill="x", expand=True)
|
||||
|
||||
def _start_pick_nr():
|
||||
_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:
|
||||
t = tk.Text(body, height=3, wrap="word",
|
||||
font=("Segoe UI", _empfang_font_size[0]),
|
||||
bg="white", fg="#1a2a3a", relief="solid", bd=1,
|
||||
@@ -6282,7 +6418,10 @@ WICHTIG unbedingt einhalten:
|
||||
if w is None:
|
||||
return ""
|
||||
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()
|
||||
if val in _empty_placeholders:
|
||||
return ""
|
||||
@@ -6378,13 +6517,14 @@ WICHTIG unbedingt einhalten:
|
||||
def _flash_bg(success):
|
||||
color = "#e8f5e9" if success else "#fce4ec"
|
||||
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:
|
||||
w.configure(bg=color)
|
||||
except Exception:
|
||||
pass
|
||||
def _restore():
|
||||
for w2 in [outer, hdr] + list(hdr.winfo_children()):
|
||||
for w2 in _flash_widgets + list(hdr.winfo_children()):
|
||||
try:
|
||||
w2.configure(bg=orig)
|
||||
except Exception:
|
||||
@@ -6419,13 +6559,26 @@ WICHTIG unbedingt einhalten:
|
||||
font=("Segoe UI", max(7, _empfang_font_size[0] - 2)))
|
||||
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]),
|
||||
bg="white", fg="#1a2a3a", relief="solid", bd=1,
|
||||
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)
|
||||
|
||||
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_entry.insert("1.0", _reply_placeholder)
|
||||
reply_entry.configure(fg="#aaa")
|
||||
@@ -6489,14 +6642,27 @@ WICHTIG unbedingt einhalten:
|
||||
def _push_to_chat_input(text: str, *, source: str = "") -> None:
|
||||
"""Fuegt Text in das Chat-Eingabefeld unten ein (Platzhalter wird ersetzt)."""
|
||||
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()
|
||||
if cur == _reply_placeholder or not cur:
|
||||
reply_entry.delete("1.0", "end")
|
||||
reply_entry.insert("1.0", text.strip())
|
||||
reply_entry.insert("1.0", chunk)
|
||||
else:
|
||||
if not cur.endswith("\n"):
|
||||
reply_entry.insert("end", "\n")
|
||||
reply_entry.insert("end", text.strip())
|
||||
reply_entry.insert("end", chunk)
|
||||
reply_entry.configure(fg="#1a2a3a")
|
||||
reply_entry.see("end")
|
||||
except Exception:
|
||||
@@ -6513,8 +6679,7 @@ WICHTIG unbedingt einhalten:
|
||||
except Exception:
|
||||
base_msgs = []
|
||||
self._empfang_last_thread_messages = base_msgs
|
||||
local_extra = list(getattr(self, "_empfang_local_chat_overlay", None) or [])
|
||||
merged = base_msgs + local_extra
|
||||
merged = base_msgs
|
||||
chat_display.configure(state="normal")
|
||||
chat_display.delete("1.0", "end")
|
||||
for msg in merged:
|
||||
|
||||
@@ -48,19 +48,22 @@
|
||||
"dermatology"
|
||||
],
|
||||
"ui_font_delta": 0,
|
||||
"global_right_click_paste": true,
|
||||
"global_right_click_paste": false,
|
||||
"todo_auto_open": false,
|
||||
"autocopy_after_diktat": true,
|
||||
"kommentare_auto_open": true,
|
||||
"empfang_auto_open": true,
|
||||
"empfang_was_open": false,
|
||||
"empfang_prefs": {
|
||||
"show_patient": false,
|
||||
"show_ther": false,
|
||||
"show_proc": false,
|
||||
"show_kom": false,
|
||||
"last_patient": "",
|
||||
"geometry": "776x779+2208+366"
|
||||
"show_patient": true,
|
||||
"show_ther": true,
|
||||
"show_proc": true,
|
||||
"show_kom": true,
|
||||
"last_patient": "24227",
|
||||
"geometry": "888x974+442+450",
|
||||
"auto_copy_ther": true,
|
||||
"auto_copy_patient": false,
|
||||
"auto_copy_proc": true
|
||||
},
|
||||
"medikament_quelle": "compendium.ch",
|
||||
"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",
|
||||
"release_date": "2026-03-14",
|
||||
"minimum_supported_version": "1.0.0",
|
||||
|
||||
Reference in New Issue
Block a user