Eigene Kennungen ausblenden, Win95-Menüs, verschiebbares Hauptfenster bei offenem Dialog u. a.
Windows-Build / windows-exe (push) Canceled after 53m28s
Windows-Build / windows-exe (push) Canceled after 53m28s
Neu: - Eigene Kennungen (eigene Filiale) ausblenden: nicht angezeigt, nicht mitgerechnet, nicht exportiert; per Rechtsklick, Händler-Menü oder Import-Bericht; Händler-Zuordnung hat Vorrang - Menüs im Win95-Stil: wechseln beim Überfahren der Menüleiste, Tastatur, Kontextmenü gleich - Dialoge sperren nur den Inhalt: Hauptfenster bleibt verschiebbar/maximierbar; Dialoge haben eigenen Taskleisten-Eintrag; Klick auf gesperrten Inhalt holt den Dialog nach vorn (blinkt) - Trennlinie zwischen Händler- und Positionsliste sichtbar und ziehbar, Position wird gemerkt - Strg+Rücktaste / Strg+Entf löschen ganze Wörter in allen Eingabefeldern Behoben: - Gedrückte Knöpfe zeigten keine Beschriftung (activeForeground gehört in Tk zur Klasse Background) - Tipp des Tages: Text lief aus dem sichtbaren Bereich - Größenänderung: breiterer Griffbereich mit Ecken, Zeiger wird zuverlässig zurückgesetzt, flüssiger; Verschieben über Windows selbst (inkl. Andocken), Minimieren über Windows - Zeichen, die MS Sans Serif nicht hat (…, ▸, →, •), durch Win95-übliche ersetzt - Menü/Tooltip/Dialog: Größe+Position zusammen setzen (Tk ignoriert sonst die Position) - Suche reagiert erst nach kurzer Tipp-Pause (flüssigeres Tippen) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
69c2a108a9
commit
807a38622c
15 files changed
+1217
-187
No files matched your search
+79
-34
@@ -183,38 +183,33 @@ class Titelleiste(tk.Frame):
|
||||
|
||||
def _start(self, e):
|
||||
self._ab = (e.x_root - self.fenster.winfo_rootx(), e.y_root - self.fenster.winfo_rooty())
|
||||
self._nativ = False
|
||||
if NATIV_VERSCHIEBEN and IS_WIN and self.ziehen_erlaubt():
|
||||
# Windows übernimmt das Ziehen: flüssig und mit Andocken am Bildschirmrand
|
||||
from provtool.gui import win32
|
||||
self._nativ = win32.nativ_verschieben(self.fenster)
|
||||
if self._nativ:
|
||||
return "break"
|
||||
|
||||
def _ziehen(self, e):
|
||||
if not self.ziehen_erlaubt():
|
||||
if self._nativ or not self.ziehen_erlaubt():
|
||||
return
|
||||
self.fenster.geometry(f"+{e.x_root - self._ab[0]}+{e.y_root - self._ab[1]}")
|
||||
|
||||
def blinken(self, mal: int = 3):
|
||||
"""Titelleiste kurz grau/blau blinken lassen (wie Windows bei gesperrtem Fenster)."""
|
||||
farben = [w.SHADOW, w.NAVY] * mal
|
||||
|
||||
class Menueleiste(tk.Frame):
|
||||
"""Graue Menüleiste (die native von Windows 10/11 ist weiß und lässt sich nicht umfärben)."""
|
||||
def schritt(i=0):
|
||||
if i >= len(farben) or not self.winfo_exists():
|
||||
return
|
||||
for widget in (self, self.titel_lbl):
|
||||
widget.configure(bg=farben[i])
|
||||
self.after(70, lambda: schritt(i + 1))
|
||||
schritt()
|
||||
|
||||
def __init__(self, master, root: tk.Misc):
|
||||
super().__init__(master, bg=w.FACE)
|
||||
self.root = root
|
||||
self.knoepfe: dict[str, tk.Menubutton] = {}
|
||||
|
||||
def menue(self, label: str, underline: int | None = 0) -> tk.Menu:
|
||||
mb = tk.Menubutton(self, text=label, bg=w.FACE, relief="flat", bd=1, padx=6, pady=1,
|
||||
activebackground=w.NAVY, activeforeground=w.WHITE,
|
||||
underline=-1 if underline is None else underline)
|
||||
menu = tk.Menu(mb, tearoff=0)
|
||||
mb.configure(menu=menu)
|
||||
mb.pack(side="left")
|
||||
self.knoepfe[label] = mb
|
||||
if underline is not None:
|
||||
taste = label[underline].lower()
|
||||
self.root.bind_all(f"<Alt-KeyPress-{taste}>", lambda e, m=mb: self.oeffnen(m))
|
||||
return menu
|
||||
|
||||
def oeffnen(self, mb: tk.Menubutton):
|
||||
menu = mb.nametowidget(mb.cget("menu"))
|
||||
menu.tk_popup(mb.winfo_rootx(), mb.winfo_rooty() + mb.winfo_height())
|
||||
return "break"
|
||||
NATIV_VERSCHIEBEN = True # Tests können das abschalten, um den Tk-Weg zu prüfen
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- Hauptfenster
|
||||
@@ -222,7 +217,8 @@ class Menueleiste(tk.Frame):
|
||||
class Hauptrahmen:
|
||||
"""Umgibt das Hauptfenster. Im klassischen Modus mit eigener Titelleiste, sonst nur ein Container."""
|
||||
|
||||
RAND = 5 # Pixel am Rand, an denen man die Größe ziehen kann
|
||||
RAND = 6 # Pixel am Rand, an denen man die Größe ziehen kann (= sichtbarer Rahmen)
|
||||
ECKE = 18 # so weit vom Eck entfernt gilt der Rand noch als Ecke (diagonal ziehen)
|
||||
|
||||
def __init__(self, root: tk.Tk, titel: str, icon: tk.PhotoImage, schliessen, klassisch: bool = True,
|
||||
minbreite: int = 900, minhoehe: int = 520):
|
||||
@@ -237,10 +233,11 @@ class Hauptrahmen:
|
||||
self.aussen.pack(fill="both", expand=True)
|
||||
self.inhalt = self.aussen
|
||||
self.titelleiste = None
|
||||
root.sperrbereich = lambda: [self.inhalt] # type: ignore[attr-defined] # für modal.py
|
||||
return
|
||||
|
||||
root.overrideredirect(True)
|
||||
self.aussen = tk.Frame(root, bg=w.FACE, bd=2, relief="raised", padx=2, pady=2)
|
||||
self.aussen = tk.Frame(root, bg=w.FACE, bd=2, relief="raised", padx=self.RAND - 2, pady=self.RAND - 2)
|
||||
self.aussen.pack(fill="both", expand=True)
|
||||
self.titelleiste = Titelleiste(self.aussen, root, titel, icon, ("min", "max", "close"),
|
||||
schliessen=schliessen, minimieren=self.minimieren,
|
||||
@@ -249,10 +246,17 @@ class Hauptrahmen:
|
||||
self.titelleiste.ziehen_erlaubt = lambda: not self.maximiert
|
||||
self.inhalt = tk.Frame(self.aussen, bg=w.FACE)
|
||||
self.inhalt.pack(fill="both", expand=True)
|
||||
# Gesperrt wird bei offenem Dialog nur der Inhalt – Titelleiste und Rand bleiben bedienbar
|
||||
root.sperrbereich = lambda: [self.inhalt] # type: ignore[attr-defined]
|
||||
|
||||
self._rand: str = ""
|
||||
self._resize_geplant = None
|
||||
self.aussen.bind("<Motion>", self._rand_cursor)
|
||||
self.aussen.bind("<Leave>", lambda e: self.aussen.configure(cursor=""))
|
||||
# Mauszeiger zurücksetzen, sobald der Rand verlassen wird – Kindfenster erben sonst den
|
||||
# Größenänderungs-Zeiger, wenn das letzte Motion-Ereignis am Rand verpasst wurde.
|
||||
self.aussen.bind("<Leave>", self._cursor_zurueck)
|
||||
self.inhalt.bind("<Enter>", self._cursor_zurueck)
|
||||
self.titelleiste.bind("<Enter>", self._cursor_zurueck, add="+")
|
||||
self.aussen.bind("<ButtonPress-1>", self._resize_start)
|
||||
self.aussen.bind("<B1-Motion>", self._resize)
|
||||
root.bind_all("<Alt-F4>", lambda e: self.alt_f4(e.widget))
|
||||
@@ -264,7 +268,8 @@ class Hauptrahmen:
|
||||
# Ist ein modaler Dialog offen, gilt Alt+F4 immer ihm – egal wo der Tastaturfokus liegt
|
||||
# (unter Windows liefert Tk Tastendrücke an das Fenster mit Fokus, das kann noch das
|
||||
# Hauptfenster sein).
|
||||
sperre = self.root.grab_current()
|
||||
from provtool.gui import modal
|
||||
sperre = self.root.grab_current() or modal.oberstes()
|
||||
popup = self._offenes_popup()
|
||||
if sperre is not None and sperre.winfo_toplevel() is not self.root:
|
||||
fenster = sperre.winfo_toplevel()
|
||||
@@ -275,6 +280,11 @@ class Hauptrahmen:
|
||||
except tk.TclError:
|
||||
fenster = self.root
|
||||
if fenster is self.root:
|
||||
from provtool.gui import modal
|
||||
offen = modal.oberstes()
|
||||
if offen is not None: # Alt+F4 bei offenem Dialog schließt den Dialog
|
||||
offen.abbrechen()
|
||||
return "break"
|
||||
self._schliessen()
|
||||
elif hasattr(fenster, "abbrechen"):
|
||||
fenster.abbrechen()
|
||||
@@ -338,7 +348,14 @@ class Hauptrahmen:
|
||||
|
||||
# ---- Fensterfunktionen
|
||||
def minimieren(self):
|
||||
# Windows minimiert keine overrideredirect-Fenster → kurz normalen Rahmen aktivieren
|
||||
if IS_WIN:
|
||||
# Direkt über Windows: offene Dialoge (gehören dem Hauptfenster) werden mit aus- und
|
||||
# wieder eingeblendet. NICHT overrideredirect umschalten – Tk baut dabei das Fenster neu
|
||||
# und Windows würde die zugehörigen Dialoge zerstören.
|
||||
from provtool.gui import win32
|
||||
if win32.minimieren(self.root):
|
||||
return
|
||||
# Tk minimiert keine overrideredirect-Fenster -> kurz normalen Rahmen aktivieren
|
||||
geo = self.root.geometry()
|
||||
self.root.overrideredirect(False)
|
||||
self.root.iconify()
|
||||
@@ -373,9 +390,25 @@ class Hauptrahmen:
|
||||
|
||||
# ---- Größe ändern
|
||||
def _welcher_rand(self, x, y) -> str:
|
||||
bw, bh, r = self.aussen.winfo_width(), self.aussen.winfo_height(), self.RAND
|
||||
rand = ("n" if y < r else "s" if y >= bh - r else "") + ("w" if x < r else "e" if x >= bw - r else "")
|
||||
return rand
|
||||
bw, bh, r, k = self.aussen.winfo_width(), self.aussen.winfo_height(), self.RAND, self.ECKE
|
||||
ns = "n" if y < r else "s" if y >= bh - r else ""
|
||||
we = "w" if x < r else "e" if x >= bw - r else ""
|
||||
# an einer Kante nahe am Eck -> Ecke (diagonal), wie bei Windows
|
||||
if ns and not we:
|
||||
we = "w" if x < k else "e" if x >= bw - k else ""
|
||||
elif we and not ns:
|
||||
ns = "n" if y < k else "s" if y >= bh - k else ""
|
||||
return ns + we
|
||||
|
||||
def _cursor_zurueck(self, _e=None):
|
||||
if self._rand_zeiger:
|
||||
self._rand_zeiger = False
|
||||
try:
|
||||
self.aussen.configure(cursor="")
|
||||
except tk.TclError:
|
||||
pass
|
||||
|
||||
_rand_zeiger = False
|
||||
|
||||
CURSOR_WIN = {"n": "size_ns", "s": "size_ns", "e": "size_we", "w": "size_we",
|
||||
"nw": "size_nw_se", "se": "size_nw_se", "ne": "size_ne_sw", "sw": "size_ne_sw"}
|
||||
@@ -387,8 +420,10 @@ class Hauptrahmen:
|
||||
if self.maximiert:
|
||||
return
|
||||
rand = self._welcher_rand(e.x, e.y)
|
||||
zeiger = (self.CURSOR_WIN if IS_WIN else self.CURSOR_X11).get(rand, "")
|
||||
try:
|
||||
self.aussen.configure(cursor=(self.CURSOR_WIN if IS_WIN else self.CURSOR_X11).get(rand, ""))
|
||||
self.aussen.configure(cursor=zeiger)
|
||||
self._rand_zeiger = bool(zeiger)
|
||||
except tk.TclError:
|
||||
pass
|
||||
|
||||
@@ -412,4 +447,14 @@ class Hauptrahmen:
|
||||
if "n" in self._rand:
|
||||
neu = max(self.minhoehe, bh - dy)
|
||||
y, bh = y + (bh - neu), neu
|
||||
self.root.geometry(f"{bw}x{bh}+{x}+{y}")
|
||||
# Mausbewegungen bündeln: nur die jeweils letzte Größe anwenden (flüssiger unter Windows)
|
||||
self._resize_ziel = f"{bw}x{bh}+{x}+{y}"
|
||||
if self._resize_geplant is None:
|
||||
self._resize_geplant = self.root.after(15, self._resize_anwenden)
|
||||
|
||||
def _resize_anwenden(self):
|
||||
self._resize_geplant = None
|
||||
try:
|
||||
self.root.geometry(self._resize_ziel)
|
||||
except tk.TclError:
|
||||
pass
|
||||
Reference in new issue
Block a user