358 lines
13 KiB
Python
358 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
AzA WhatsApp - WhatsApp Integration für Arztpraxis
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import json
|
||
import tkinter as tk
|
||
from tkinter import ttk, messagebox, scrolledtext
|
||
from datetime import datetime
|
||
|
||
|
||
# Konfigurationsdatei
|
||
CONFIG_FILENAME = "aza_whatsapp_config.json"
|
||
|
||
def _config_path():
|
||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), CONFIG_FILENAME)
|
||
|
||
def load_whatsapp_config():
|
||
"""Lädt WhatsApp Konfiguration."""
|
||
try:
|
||
path = _config_path()
|
||
if os.path.isfile(path):
|
||
with open(path, "r", encoding="utf-8") as f:
|
||
return json.load(f)
|
||
except Exception:
|
||
pass
|
||
return {}
|
||
|
||
def save_whatsapp_config(data):
|
||
"""Speichert WhatsApp Konfiguration."""
|
||
try:
|
||
path = _config_path()
|
||
with open(path, "w", encoding="utf-8") as f:
|
||
json.dump(data, f, indent=2, ensure_ascii=False)
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
def _load_font_sizes():
|
||
try:
|
||
p = os.path.join(os.path.dirname(os.path.abspath(__file__)), "aza_font_sizes.json")
|
||
if os.path.isfile(p):
|
||
with open(p, "r", encoding="utf-8") as f:
|
||
return json.load(f)
|
||
except Exception:
|
||
pass
|
||
return {}
|
||
|
||
def _save_font_size(key, size):
|
||
try:
|
||
d = _load_font_sizes()
|
||
d[key] = size
|
||
p = os.path.join(os.path.dirname(os.path.abspath(__file__)), "aza_font_sizes.json")
|
||
with open(p, "w", encoding="utf-8") as f:
|
||
json.dump(d, f, indent=2, ensure_ascii=False)
|
||
except Exception:
|
||
pass
|
||
|
||
def add_text_font_size_control(parent_frame, text_widget, initial_size=10, label="Aa", bg_color="#F5FCFF", save_key=None):
|
||
"""▲▼-Pfeile für Textfeld-Schriftgröße (5–20pt), unauffällig im Hintergrund."""
|
||
if save_key:
|
||
saved = _load_font_sizes().get(save_key)
|
||
if saved is not None:
|
||
initial_size = int(saved)
|
||
_size = [max(5, min(20, initial_size))]
|
||
_fg = "#8AAFC0"
|
||
_fg_hover = "#1a4d6d"
|
||
cf = tk.Frame(parent_frame, bg=bg_color, highlightthickness=0, bd=0)
|
||
cf.pack(side="right", padx=4)
|
||
tk.Label(cf, text=label, font=("Segoe UI", 8), bg=bg_color, fg=_fg).pack(side="left", padx=(0, 1))
|
||
size_lbl = tk.Label(cf, text=str(_size[0]), font=("Segoe UI", 8), bg=bg_color, fg=_fg, width=2, anchor="center")
|
||
size_lbl.pack(side="left")
|
||
def _apply(ns):
|
||
ns = max(5, min(20, ns))
|
||
_size[0] = ns
|
||
size_lbl.configure(text=str(ns))
|
||
text_widget.configure(font=("Segoe UI", ns))
|
||
if save_key:
|
||
_save_font_size(save_key, ns)
|
||
text_widget.configure(font=("Segoe UI", _size[0]))
|
||
btn_up = tk.Label(cf, text="\u25B2", font=("Segoe UI", 7), bg=bg_color, fg=_fg, cursor="hand2", bd=0, highlightthickness=0)
|
||
btn_up.pack(side="left", padx=(2, 0))
|
||
btn_down = tk.Label(cf, text="\u25BC", font=("Segoe UI", 7), bg=bg_color, fg=_fg, cursor="hand2", bd=0, highlightthickness=0)
|
||
btn_down.pack(side="left")
|
||
btn_up.bind("<Button-1>", lambda e: _apply(_size[0] + 1))
|
||
btn_down.bind("<Button-1>", lambda e: _apply(_size[0] - 1))
|
||
for w in (btn_up, btn_down):
|
||
w.bind("<Enter>", lambda e, ww=w: ww.configure(fg=_fg_hover))
|
||
w.bind("<Leave>", lambda e, ww=w: ww.configure(fg=_fg))
|
||
return _size
|
||
|
||
|
||
class WhatsAppApp:
|
||
def __init__(self, root):
|
||
self.root = root
|
||
self.root.title("AzA WhatsApp")
|
||
self.root.geometry("1000x600")
|
||
self.root.minsize(800, 500)
|
||
|
||
# Farben (WhatsApp-ähnlich)
|
||
self.bg_dark = "#075E54" # WhatsApp Dunkelgrün
|
||
self.bg_medium = "#128C7E" # WhatsApp Mittelgrün
|
||
self.bg_light = "#DCF8C6" # WhatsApp Hellgrün (eigene Nachricht)
|
||
self.bg_white = "#FFFFFF"
|
||
self.bg_chat = "#ECE5DD" # WhatsApp Chat-Hintergrund
|
||
self.fg_dark = "#2C3E50"
|
||
self.fg_light = "#FFFFFF"
|
||
|
||
self.root.configure(bg=self.bg_dark)
|
||
|
||
# Dummy-Daten
|
||
self.contacts = [
|
||
{"name": "Dr. Müller", "phone": "+41 79 123 45 67", "last_msg": "Termin bestätigt"},
|
||
{"name": "Patient Hans", "phone": "+41 78 234 56 78", "last_msg": "Vielen Dank!"},
|
||
{"name": "Apotheke Linden", "phone": "+41 44 123 45 67", "last_msg": "Rezept bereit"},
|
||
]
|
||
self.current_contact = None
|
||
self.messages = []
|
||
|
||
# Gespeicherte Geometrie laden
|
||
config = load_whatsapp_config()
|
||
saved_geom = config.get("geometry", "")
|
||
if saved_geom:
|
||
try:
|
||
self.root.geometry(saved_geom)
|
||
except:
|
||
pass
|
||
|
||
self.root.protocol("WM_DELETE_WINDOW", self._on_close)
|
||
|
||
self._build_ui()
|
||
|
||
def _on_close(self):
|
||
"""Speichert Geometrie beim Schließen."""
|
||
config = load_whatsapp_config()
|
||
config["geometry"] = self.root.geometry()
|
||
save_whatsapp_config(config)
|
||
self.root.destroy()
|
||
|
||
def _build_ui(self):
|
||
"""Baut die Benutzeroberfläche."""
|
||
# Toolbar
|
||
self._create_toolbar()
|
||
|
||
# Hauptbereich: 2 Spalten (Kontakte, Chat)
|
||
main_paned = ttk.PanedWindow(self.root, orient="horizontal")
|
||
main_paned.pack(fill="both", expand=True)
|
||
|
||
# Linke Spalte: Kontaktliste
|
||
left_frame = tk.Frame(main_paned, bg="#FFFFFF", width=300)
|
||
main_paned.add(left_frame, weight=1)
|
||
|
||
# Rechte Spalte: Chat
|
||
right_frame = tk.Frame(main_paned, bg=self.bg_chat)
|
||
main_paned.add(right_frame, weight=3)
|
||
|
||
self._create_contact_list(left_frame)
|
||
self._create_chat_area(right_frame)
|
||
|
||
# Statusleiste
|
||
self.status_var = tk.StringVar(value="Bereit")
|
||
status_bar = tk.Label(
|
||
self.root, textvariable=self.status_var,
|
||
bg=self.bg_dark, fg=self.fg_light,
|
||
font=("Segoe UI", 9), anchor="w", padx=10
|
||
)
|
||
status_bar.pack(side="bottom", fill="x")
|
||
|
||
def _create_toolbar(self):
|
||
"""Erstellt die Toolbar."""
|
||
toolbar = tk.Frame(self.root, bg=self.bg_dark, height=50)
|
||
toolbar.pack(side="top", fill="x")
|
||
toolbar.pack_propagate(False)
|
||
|
||
tk.Label(
|
||
toolbar, text="💬 WhatsApp", bg=self.bg_dark, fg=self.fg_light,
|
||
font=("Segoe UI", 14, "bold")
|
||
).pack(side="left", padx=20, pady=10)
|
||
|
||
btn_frame = tk.Frame(toolbar, bg=self.bg_dark)
|
||
btn_frame.pack(side="right", padx=20, pady=10)
|
||
|
||
self._create_toolbar_button(btn_frame, "➕ Neuer Chat", self._new_chat).pack(side="left", padx=2)
|
||
self._create_toolbar_button(btn_frame, "🔍 Suchen", self._search_contacts).pack(side="left", padx=2)
|
||
|
||
def _create_toolbar_button(self, parent, text, command):
|
||
"""Erstellt einen Toolbar-Button."""
|
||
btn = tk.Button(
|
||
parent, text=text, command=command,
|
||
bg=self.bg_medium, fg=self.fg_light,
|
||
font=("Segoe UI", 10), relief="flat",
|
||
padx=12, pady=6, cursor="hand2"
|
||
)
|
||
|
||
def on_enter(e):
|
||
btn.configure(bg="#0E7A6E")
|
||
|
||
def on_leave(e):
|
||
btn.configure(bg=self.bg_medium)
|
||
|
||
btn.bind("<Enter>", on_enter)
|
||
btn.bind("<Leave>", on_leave)
|
||
|
||
return btn
|
||
|
||
def _create_contact_list(self, parent):
|
||
"""Erstellt die Kontaktliste."""
|
||
tk.Label(
|
||
parent, text="Chats", bg="#FFFFFF", fg=self.fg_dark,
|
||
font=("Segoe UI", 12, "bold"), anchor="w", padx=10
|
||
).pack(fill="x", pady=(10, 5))
|
||
|
||
# Listbox für Kontakte
|
||
list_frame = tk.Frame(parent, bg="#FFFFFF")
|
||
list_frame.pack(fill="both", expand=True, padx=5, pady=5)
|
||
|
||
scrollbar = tk.Scrollbar(list_frame)
|
||
scrollbar.pack(side="right", fill="y")
|
||
|
||
self.contact_listbox = tk.Listbox(
|
||
list_frame, font=("Segoe UI", 10),
|
||
relief="flat", bd=0, highlightthickness=0,
|
||
yscrollcommand=scrollbar.set, selectmode="single"
|
||
)
|
||
self.contact_listbox.pack(side="left", fill="both", expand=True)
|
||
scrollbar.config(command=self.contact_listbox.yview)
|
||
|
||
# Kontakte einfügen
|
||
for contact in self.contacts:
|
||
display = f"{contact['name']}\n{contact['last_msg']}"
|
||
self.contact_listbox.insert("end", display)
|
||
self.contact_listbox.insert("end", "") # Trenner
|
||
|
||
self.contact_listbox.bind("<<ListboxSelect>>", self._on_contact_select)
|
||
|
||
def _create_chat_area(self, parent):
|
||
"""Erstellt den Chat-Bereich."""
|
||
# Header
|
||
header_frame = tk.Frame(parent, bg="#EDEDED", height=60)
|
||
header_frame.pack(fill="x")
|
||
header_frame.pack_propagate(False)
|
||
|
||
self.chat_header_label = tk.Label(
|
||
header_frame, text="Wählen Sie einen Chat aus",
|
||
bg="#EDEDED", fg=self.fg_dark,
|
||
font=("Segoe UI", 12, "bold"), anchor="w", padx=15
|
||
)
|
||
self.chat_header_label.pack(fill="both", expand=True)
|
||
|
||
# Chat-Verlauf
|
||
chat_scroll_frame = tk.Frame(parent, bg=self.bg_chat)
|
||
chat_scroll_frame.pack(fill="both", expand=True, padx=10, pady=10)
|
||
|
||
chat_header = tk.Frame(chat_scroll_frame, bg=self.bg_chat)
|
||
chat_header.pack(fill="x", anchor="w")
|
||
|
||
self.chat_display = scrolledtext.ScrolledText(
|
||
chat_scroll_frame, wrap="word", font=("Segoe UI", 10),
|
||
relief="flat", bd=0, bg=self.bg_chat, state="disabled"
|
||
)
|
||
self.chat_display.pack(fill="both", expand=True)
|
||
add_text_font_size_control(chat_header, self.chat_display, initial_size=10, bg_color=self.bg_chat, save_key="whatsapp_chat")
|
||
|
||
# Eingabe-Bereich
|
||
input_frame = tk.Frame(parent, bg="#F0F0F0", height=60)
|
||
input_frame.pack(fill="x", padx=10, pady=(0, 10))
|
||
input_frame.pack_propagate(False)
|
||
|
||
self.message_entry = tk.Entry(
|
||
input_frame, font=("Segoe UI", 11),
|
||
relief="solid", bd=1
|
||
)
|
||
self.message_entry.pack(side="left", fill="both", expand=True, padx=(5, 5), pady=10)
|
||
self.message_entry.bind("<Return>", lambda e: self._send_message())
|
||
|
||
send_btn = tk.Button(
|
||
input_frame, text="📤", command=self._send_message,
|
||
bg=self.bg_medium, fg=self.fg_light,
|
||
font=("Segoe UI", 14), relief="flat",
|
||
width=3, cursor="hand2"
|
||
)
|
||
send_btn.pack(side="right", padx=(0, 5), pady=10)
|
||
|
||
def _on_contact_select(self, event):
|
||
"""Wird aufgerufen, wenn ein Kontakt ausgewählt wird."""
|
||
selection = self.contact_listbox.curselection()
|
||
if not selection:
|
||
return
|
||
|
||
idx = selection[0] // 2
|
||
if idx < len(self.contacts):
|
||
self.current_contact = self.contacts[idx]
|
||
self.chat_header_label.config(text=self.current_contact["name"])
|
||
self._load_messages()
|
||
|
||
def _load_messages(self):
|
||
"""Lädt Nachrichten für den aktuellen Kontakt."""
|
||
# Dummy-Nachrichten
|
||
self.messages = [
|
||
{"from": "me", "text": "Hallo, wie geht es Ihnen?", "time": "10:30"},
|
||
{"from": "them", "text": "Danke, gut! Ich hätte gerne einen Termin.", "time": "10:32"},
|
||
{"from": "me", "text": "Gerne, wann passt es Ihnen?", "time": "10:35"},
|
||
]
|
||
|
||
self.chat_display.configure(state="normal")
|
||
self.chat_display.delete("1.0", "end")
|
||
|
||
for msg in self.messages:
|
||
if msg["from"] == "me":
|
||
self.chat_display.insert("end", f"[{msg['time']}] Sie: {msg['text']}\n\n")
|
||
else:
|
||
self.chat_display.insert("end", f"[{msg['time']}] {self.current_contact['name']}: {msg['text']}\n\n")
|
||
|
||
self.chat_display.configure(state="disabled")
|
||
self.chat_display.see("end")
|
||
|
||
def _send_message(self):
|
||
"""Sendet eine Nachricht."""
|
||
if not self.current_contact:
|
||
messagebox.showinfo("Hinweis", "Bitte wählen Sie zuerst einen Chat aus.")
|
||
return
|
||
|
||
text = self.message_entry.get().strip()
|
||
if not text:
|
||
return
|
||
|
||
# Nachricht hinzufügen
|
||
now = datetime.now().strftime("%H:%M")
|
||
self.messages.append({"from": "me", "text": text, "time": now})
|
||
|
||
self.chat_display.configure(state="normal")
|
||
self.chat_display.insert("end", f"[{now}] Sie: {text}\n\n")
|
||
self.chat_display.configure(state="disabled")
|
||
self.chat_display.see("end")
|
||
|
||
self.message_entry.delete(0, "end")
|
||
self.status_var.set("Nachricht gesendet.")
|
||
|
||
def _new_chat(self):
|
||
"""Startet einen neuen Chat."""
|
||
messagebox.showinfo("Neuer Chat", "Neue Chat-Funktion wird in einer zukünftigen Version implementiert.")
|
||
|
||
def _search_contacts(self):
|
||
"""Durchsucht Kontakte."""
|
||
messagebox.showinfo("Suchen", "Suchfunktion wird in einer zukünftigen Version implementiert.")
|
||
|
||
|
||
def main():
|
||
root = tk.Tk()
|
||
app = WhatsAppApp(root)
|
||
root.mainloop()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|