Win95-Design an das neue Hauptfenster angepasst, Version 1.6.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f296483fdc
commit
c35356aeb9
4 files changed
+57
-29
No files matched your search
@@ -76,7 +76,7 @@ Welche Provisionsart Airtime bzw. Jahresbonus ist, erkennt das Programm an den T
|
|||||||
„AirTime Provision“, „Umsatzbeteiligung“, „Jahresbonus“); umstellbar je Provisionsart unter
|
„AirTime Provision“, „Umsatzbeteiligung“, „Jahresbonus“); umstellbar je Provisionsart unter
|
||||||
*Händler ▸ Provisionsarten…*.
|
*Händler ▸ Provisionsarten…*.
|
||||||
|
|
||||||
Das Hauptfenster ist unter Windows ein echtes Programmfenster mit Win95-Optik: an den Bildschirmrand
|
Das Hauptfenster ist unter Windows ein echtes Programmfenster: an den Bildschirmrand
|
||||||
ziehen (Andocken), `Win`+Pfeiltasten, Doppelklick auf die Titelleiste und Ziehen am Rand funktionieren
|
ziehen (Andocken), `Win`+Pfeiltasten, Doppelklick auf die Titelleiste und Ziehen am Rand funktionieren
|
||||||
wie gewohnt.
|
wie gewohnt.
|
||||||
|
|
||||||
@@ -86,8 +86,10 @@ gekennzeichnet (grau) und nie wieder exportiert – auch nicht, wenn dieselbe Po
|
|||||||
anderen Datei oder Abrechnung auftaucht. Versehentlich? Rechtsklick ▸ *Kennzeichnung „abgerechnet“ aufheben*.
|
anderen Datei oder Abrechnung auftaucht. Versehentlich? Rechtsklick ▸ *Kennzeichnung „abgerechnet“ aufheben*.
|
||||||
*Datei ▸ Abrechnung erledigt / wieder öffnen* sperrt eine fertige Abrechnung gegen Änderungen.
|
*Datei ▸ Abrechnung erledigt / wieder öffnen* sperrt eine fertige Abrechnung gegen Änderungen.
|
||||||
|
|
||||||
**Design:** Standard ist ein modernes, helles Design (wie Windows 11); das Windows-95-Design gibt es weiter
|
**Design:** Standard ist das Design der Handy-Insel-Webtools (Lila, Schrift IBM Plex – liegt dem Programm bei):
|
||||||
unter *Datei ▸ Einstellungen ▸ Darstellung* (wirkt nach Neustart).
|
Kopfzeile mit Menüs und Suche, links Abrechnungen und Händler, oben Kennzahlen, über der Tabelle Filter
|
||||||
|
(nicht zugeordnet, von Hand geändert, doppelt, bereits abgerechnet). Oben rechts „Dunkel“/„Hell“ schaltet
|
||||||
|
das dunkle Design um. Das Windows-95-Design gibt es weiter unter *Datei ▸ Einstellungen ▸ Darstellung*.
|
||||||
|
|
||||||
**Linux:** `Provisionsmanager95-linux-x86_64-<Version>.tar.gz` entpacken und `Provisionsmanager95` starten
|
**Linux:** `Provisionsmanager95-linux-x86_64-<Version>.tar.gz` entpacken und `Provisionsmanager95` starten
|
||||||
(ab Debian 12 / Ubuntu 24.04). Daten liegen unter `~/.config/Provisionsmanager95`.
|
(ab Debian 12 / Ubuntu 24.04). Daten liegen unter `~/.config/Provisionsmanager95`.
|
||||||
@@ -129,7 +131,8 @@ Aufbau:
|
|||||||
| `provtool/db.py` | SQLite: Händler, Kennungen, Regeln, Abrechnungen, Positionen |
|
| `provtool/db.py` | SQLite: Händler, Kennungen, Regeln, Abrechnungen, Positionen |
|
||||||
| `provtool/export.py` | Excel-Export im Zielformat |
|
| `provtool/export.py` | Excel-Export im Zielformat |
|
||||||
| `provtool/money.py` | Beträge (intern immer in Cent) |
|
| `provtool/money.py` | Beträge (intern immer in Cent) |
|
||||||
| `provtool/gui/` | Oberfläche (Tkinter im Win95-Stil) |
|
| `provtool/gui/` | Oberfläche (Tkinter; Designs hell, dunkel, Win95) |
|
||||||
|
| `provtool/gui/schriften/` | IBM Plex (SIL Open Font License, siehe `OFL.txt`) |
|
||||||
| `provtool/gui/rahmen.py` | selbst gezeichnete Titelleiste, Menüleiste, Größenänderung |
|
| `provtool/gui/rahmen.py` | selbst gezeichnete Titelleiste, Menüleiste, Größenänderung |
|
||||||
| `provtool/gui/assistent.py` | Einrichtungs-Assistent, Rundgang, Tipp des Tages |
|
| `provtool/gui/assistent.py` | Einrichtungs-Assistent, Rundgang, Tipp des Tages |
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""Provisions-Manager 95 – Provisionsabrechnung für Unterhändler der Handy-Insel Miesbach."""
|
"""Provisions-Manager 95 – Provisionsabrechnung für Unterhändler der Handy-Insel Miesbach."""
|
||||||
|
|
||||||
__version__ = "1.5.0"
|
__version__ = "1.6.0"
|
||||||
APP_NAME = "Provisions-Manager 95"
|
APP_NAME = "Provisions-Manager 95"
|
||||||
APP_ORG = "Handy-Insel Miesbach"
|
APP_ORG = "Handy-Insel Miesbach"
|
||||||
+32
-18
@@ -216,18 +216,18 @@ class App:
|
|||||||
pw.pack(fill="both", expand=True, padx=0 if m else 6, pady=0 if m else 6)
|
pw.pack(fill="both", expand=True, padx=0 if m else 6, pady=0 if m else 6)
|
||||||
|
|
||||||
# ---------------- Seitenleiste: Abrechnungen und Händler
|
# ---------------- Seitenleiste: Abrechnungen und Händler
|
||||||
links = tk.Frame(pw, bg=w.FACE, bd=0 if m else 2, relief="flat" if m else "raised")
|
# Win95: zwei eigene Flächen mit blauer Titelleiste auf dem Desktop
|
||||||
innen = tk.Frame(links, bg=w.FACE)
|
links = tk.Frame(pw, bg=w.FACE if m else w.BG)
|
||||||
innen.pack(fill="both", expand=True, padx=12 if m else 4, pady=(16, 10) if m else 4)
|
innen = tk.Frame(links, bg=w.FACE if m else w.BG)
|
||||||
|
innen.pack(fill="both", expand=True, padx=12 if m else 0, pady=(16, 10) if m else 0)
|
||||||
seite = dict(style="Seite.Treeview") if m else {}
|
seite = dict(style="Seite.Treeview") if m else {}
|
||||||
|
rand = {} if m else dict(padx=2, pady=(0, 2)) # Abstand der Liste zum Win95-Rahmen
|
||||||
|
|
||||||
self.f_a = tk.Frame(innen, bg=w.FACE)
|
self.f_a = tk.Frame(innen, bg=w.FACE) if m else w.Karte(innen)
|
||||||
self.f_a.pack(fill="x", pady=(0, 16 if m else 6))
|
self.f_a.pack(fill="x", pady=(0, 16 if m else 6))
|
||||||
zeile = tk.Frame(self.f_a, bg=w.FACE)
|
zeile = self._abschnitt_zeile(self.f_a, "Abrechnungen")
|
||||||
zeile.pack(fill="x", pady=(0, 4))
|
|
||||||
w.abschnitt(zeile, "Abrechnungen").pack(side="left", padx=(8 if m else 2, 0))
|
|
||||||
self.b_neu = w.knopf(zeile, "+ Neu", self.neu, padx=8, pady=0, **self._link())
|
self.b_neu = w.knopf(zeile, "+ Neu", self.neu, padx=8, pady=0, **self._link())
|
||||||
self.b_neu.pack(side="right")
|
self.b_neu.pack(side="right", padx=0 if m else 1)
|
||||||
w.Tooltip(self.b_neu, "Neue Abrechnung anlegen (Strg+N)")
|
w.Tooltip(self.b_neu, "Neue Abrechnung anlegen (Strg+N)")
|
||||||
r, self.atree = w.scroll_tree(self.f_a, columns=("status",), show="tree", height=5, selectmode="browse",
|
r, self.atree = w.scroll_tree(self.f_a, columns=("status",), show="tree", height=5, selectmode="browse",
|
||||||
auto=True, **seite)
|
auto=True, **seite)
|
||||||
@@ -236,16 +236,14 @@ class App:
|
|||||||
w.auto_breite(self.atree, {"#0": 1})
|
w.auto_breite(self.atree, {"#0": 1})
|
||||||
self.atree.tag_configure("leise", foreground=w.SHADOW)
|
self.atree.tag_configure("leise", foreground=w.SHADOW)
|
||||||
self._punkte = {f: w.punkt(self.root, getattr(w, f)) for f in ("BLAU", "GOLD", "GRUEN", "ROT", "SHADOW")}
|
self._punkte = {f: w.punkt(self.root, getattr(w, f)) for f in ("BLAU", "GOLD", "GRUEN", "ROT", "SHADOW")}
|
||||||
r.pack(fill="both", expand=True)
|
r.pack(fill="both", expand=True, **rand)
|
||||||
self.atree.bind("<<TreeviewSelect>>", self._abrechnung_gewaehlt)
|
self.atree.bind("<<TreeviewSelect>>", self._abrechnung_gewaehlt)
|
||||||
|
|
||||||
self.f_h = tk.Frame(innen, bg=w.FACE)
|
self.f_h = tk.Frame(innen, bg=w.FACE) if m else w.Karte(innen)
|
||||||
self.f_h.pack(fill="both", expand=True)
|
self.f_h.pack(fill="both", expand=True)
|
||||||
zeile = tk.Frame(self.f_h, bg=w.FACE)
|
zeile = self._abschnitt_zeile(self.f_h, "Händler")
|
||||||
zeile.pack(fill="x", pady=(0, 4))
|
|
||||||
w.abschnitt(zeile, "Händler").pack(side="left", padx=(8 if m else 2, 0))
|
|
||||||
self.b_haendler = w.knopf(zeile, "Verwalten", self.haendler, padx=8, pady=0, **self._link())
|
self.b_haendler = w.knopf(zeile, "Verwalten", self.haendler, padx=8, pady=0, **self._link())
|
||||||
self.b_haendler.pack(side="right")
|
self.b_haendler.pack(side="right", padx=0 if m else 1)
|
||||||
w.Tooltip(self.b_haendler, "Händler und Kennungen verwalten (Strg+H)")
|
w.Tooltip(self.b_haendler, "Händler und Kennungen verwalten (Strg+H)")
|
||||||
r, self.htree = w.scroll_tree(self.f_h, columns=("anzahl", "summe"), show="tree", selectmode="browse",
|
r, self.htree = w.scroll_tree(self.f_h, columns=("anzahl", "summe"), show="tree", selectmode="browse",
|
||||||
auto=True, **seite)
|
auto=True, **seite)
|
||||||
@@ -255,7 +253,7 @@ class App:
|
|||||||
w.auto_breite(self.htree, {"#0": 1})
|
w.auto_breite(self.htree, {"#0": 1})
|
||||||
self.htree.tag_configure("grau", foreground=w.SHADOW)
|
self.htree.tag_configure("grau", foreground=w.SHADOW)
|
||||||
self.htree.tag_configure("fett", font=w.FONT_BOLD)
|
self.htree.tag_configure("fett", font=w.FONT_BOLD)
|
||||||
r.pack(fill="both", expand=True)
|
r.pack(fill="both", expand=True, **rand)
|
||||||
self.htree.bind("<<TreeviewSelect>>", self._haendler_gewaehlt)
|
self.htree.bind("<<TreeviewSelect>>", self._haendler_gewaehlt)
|
||||||
self.htree.bind("<Double-1>", lambda e: self._haendler_doppelklick())
|
self.htree.bind("<Double-1>", lambda e: self._haendler_doppelklick())
|
||||||
pw.add(links, minsize=240, width=320)
|
pw.add(links, minsize=240, width=320)
|
||||||
@@ -275,6 +273,9 @@ class App:
|
|||||||
self.l_titel.pack(anchor="w")
|
self.l_titel.pack(anchor="w")
|
||||||
self.l_info = tk.Label(texte, text="", bg=w.BG, fg=w.TEXT2, anchor="w")
|
self.l_info = tk.Label(texte, text="", bg=w.BG, fg=w.TEXT2, anchor="w")
|
||||||
self.l_info.pack(anchor="w")
|
self.l_info.pack(anchor="w")
|
||||||
|
if not m: # Win95: weiße Schrift auf dem Desktop, wie die Symbolbeschriftungen
|
||||||
|
for lbl in (self.l_status, self.l_titel, self.l_info):
|
||||||
|
lbl.configure(fg=w.WHITE)
|
||||||
self.b_export = w.knopf(kopf, "Exportieren", self.exportieren, primaer=True)
|
self.b_export = w.knopf(kopf, "Exportieren", self.exportieren, primaer=True)
|
||||||
self.b_export.pack(side="right", anchor="s")
|
self.b_export.pack(side="right", anchor="s")
|
||||||
w.Tooltip(self.b_export, "Händler-Abrechnungen als Excel speichern (Strg+E)")
|
w.Tooltip(self.b_export, "Händler-Abrechnungen als Excel speichern (Strg+E)")
|
||||||
@@ -305,10 +306,13 @@ class App:
|
|||||||
|
|
||||||
self.f_p = w.Karte(innen)
|
self.f_p = w.Karte(innen)
|
||||||
self.f_p.pack(fill="both", expand=True, pady=(0, 12 if m else 0))
|
self.f_p.pack(fill="both", expand=True, pady=(0, 12 if m else 0))
|
||||||
|
if not m: # Win95: Titel in der blauen Leiste, Filter darunter wie eine Werkzeugleiste
|
||||||
|
_, self.l_ptitel = w.titelleiste(self.f_p, "Positionen")
|
||||||
kopfzeile = tk.Frame(self.f_p, bg=w.FACE)
|
kopfzeile = tk.Frame(self.f_p, bg=w.FACE)
|
||||||
kopfzeile.pack(fill="x", padx=14 if m else 4, pady=10 if m else 4)
|
kopfzeile.pack(fill="x", padx=14 if m else 4, pady=10 if m else 4)
|
||||||
self.l_ptitel = tk.Label(kopfzeile, text="Positionen", font=(w.FONT_FAMILY, w.FONT_SIZE + 1, "bold"))
|
if m:
|
||||||
self.l_ptitel.pack(side="left", padx=(0, 12))
|
self.l_ptitel = tk.Label(kopfzeile, text="Positionen", font=(w.FONT_FAMILY, w.FONT_SIZE + 1, "bold"))
|
||||||
|
self.l_ptitel.pack(side="left", padx=(0, 12))
|
||||||
self.pills: dict[str, w.Pill] = {}
|
self.pills: dict[str, w.Pill] = {}
|
||||||
for key, text in self.FILTER:
|
for key, text in self.FILTER:
|
||||||
self.pills[key] = w.Pill(kopfzeile, text, lambda k=key: self._filter_setzen(k), aktiv=key == self.filter)
|
self.pills[key] = w.Pill(kopfzeile, text, lambda k=key: self._filter_setzen(k), aktiv=key == self.filter)
|
||||||
@@ -354,6 +358,16 @@ class App:
|
|||||||
w.knopf(leiste, "Alle aufklappen", lambda: self._klappen(True)).pack(side="right", padx=6)
|
w.knopf(leiste, "Alle aufklappen", lambda: self._klappen(True)).pack(side="right", padx=6)
|
||||||
pw.add(rechts, minsize=560)
|
pw.add(rechts, minsize=560)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _abschnitt_zeile(master, titel: str) -> tk.Frame:
|
||||||
|
"""Überschrift eines Abschnitts der Seitenleiste; Win95 als blaue Titelleiste. Knöpfe kommen rechts hinein."""
|
||||||
|
if not w.MODERN:
|
||||||
|
return w.titelleiste(master, titel)[0]
|
||||||
|
zeile = tk.Frame(master, bg=w.FACE)
|
||||||
|
zeile.pack(fill="x", pady=(0, 4))
|
||||||
|
w.abschnitt(zeile, titel).pack(side="left", padx=(8, 0))
|
||||||
|
return zeile
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _link() -> dict:
|
def _link() -> dict:
|
||||||
"""Kleine Textknöpfe in der Seitenleiste (modern ohne Rahmen, in Akzentfarbe)."""
|
"""Kleine Textknöpfe in der Seitenleiste (modern ohne Rahmen, in Akzentfarbe)."""
|
||||||
@@ -1247,7 +1261,7 @@ class App:
|
|||||||
"Gruppenzeile (Provisionsart) markieren und hier klicken – z. B. 80 % für den Sonderbonus. "
|
"Gruppenzeile (Provisionsart) markieren und hier klicken – z. B. 80 % für den Sonderbonus. "
|
||||||
"Mit „Als Standard merken“ gilt das auch in den nächsten Monaten automatisch."),
|
"Mit „Als Standard merken“ gilt das auch in den nächsten Monaten automatisch."),
|
||||||
(tb["Export"], "Exportieren",
|
(tb["Export"], "Exportieren",
|
||||||
"Zum Schluss erstellt „Export“ für jeden Händler eine Excel-Datei im gewohnten Abrechnungsformat "
|
"Zum Schluss erstellt „Exportieren“ für jeden Händler eine Excel-Datei im gewohnten Abrechnungsformat "
|
||||||
"(Airtime darin als eine Summenzeile) und – falls vorhanden – die Airtime-Liste."),
|
"(Airtime darin als eine Summenzeile) und – falls vorhanden – die Airtime-Liste."),
|
||||||
(self.status, "Summen",
|
(self.status, "Summen",
|
||||||
"Unten sehen Sie immer die Summe der angezeigten bzw. markierten Positionen: "
|
"Unten sehen Sie immer die Summe der angezeigten bzw. markierten Positionen: "
|
||||||
|
|||||||
+17
-6
@@ -34,7 +34,7 @@ _PALETTEN = {
|
|||||||
NAVY="#000080", TEAL="#008080", WHITE="#ffffff", INFO_BG="#ffffe1", SEL_BG="#000080",
|
NAVY="#000080", TEAL="#008080", WHITE="#ffffff", INFO_BG="#ffffe1", SEL_BG="#000080",
|
||||||
SEL_FG="#ffffff", RAND="#808080", BG="#008080", RAND2="#808080", TEXT2="#000000",
|
SEL_FG="#ffffff", RAND="#808080", BG="#008080", RAND2="#808080", TEXT2="#000000",
|
||||||
PRIMAER="#c0c0c0", AUF_PRIMAER="#000000", ZEBRA="#ffffff", MANUELL="#0000c0", ROT="#a00000",
|
PRIMAER="#c0c0c0", AUF_PRIMAER="#000000", ZEBRA="#ffffff", MANUELL="#0000c0", ROT="#a00000",
|
||||||
ROT_SOFT="#c0c0c0", GRUEN="#008000", GRUEN_SOFT="#c0c0c0", BLAU="#000080", BLAU_SOFT="#c0c0c0",
|
ROT_SOFT="#c0c0c0", GRUEN="#008000", GRUEN_SOFT="#c0c0c0", BLAU="#0000ff", BLAU_SOFT="#c0c0c0",
|
||||||
GOLD="#808000", GOLD_SOFT="#c0c0c0"),
|
GOLD="#808000", GOLD_SOFT="#c0c0c0"),
|
||||||
# Hausfarben der Handy-Insel-Webtools (VVL-Tool): Lila, IBM Plex
|
# Hausfarben der Handy-Insel-Webtools (VVL-Tool): Lila, IBM Plex
|
||||||
"modern": dict(FACE="#ffffff", HILITE="#ffffff", LIGHT="#f1edf8", SHADOW="#6b6386", DARK="#211a38",
|
"modern": dict(FACE="#ffffff", HILITE="#ffffff", LIGHT="#f1edf8", SHADOW="#6b6386", DARK="#211a38",
|
||||||
@@ -670,6 +670,15 @@ class Karte(tk.Frame):
|
|||||||
super().__init__(master, **kw)
|
super().__init__(master, **kw)
|
||||||
|
|
||||||
|
|
||||||
|
def titelleiste(master, text: str = "") -> tuple[tk.Frame, tk.Label]:
|
||||||
|
"""Win95: blaue Titelleiste oben in einer Karte (wie bei Fenster); rechts passen kleine Knöpfe hinein."""
|
||||||
|
f = tk.Frame(master, bg=NAVY)
|
||||||
|
f.pack(fill="x", padx=1, pady=1)
|
||||||
|
lbl = tk.Label(f, text=text, bg=NAVY, fg=WHITE, font=FONT_BOLD, anchor="w", padx=3)
|
||||||
|
lbl.pack(side="left", pady=1)
|
||||||
|
return f, lbl
|
||||||
|
|
||||||
|
|
||||||
def abschnitt(master, text: str, **kw) -> tk.Label:
|
def abschnitt(master, text: str, **kw) -> tk.Label:
|
||||||
"""Kleine Überschrift (modern in Großbuchstaben und grau, wie die Tabellenköpfe der Webtools)."""
|
"""Kleine Überschrift (modern in Großbuchstaben und grau, wie die Tabellenköpfe der Webtools)."""
|
||||||
if MODERN:
|
if MODERN:
|
||||||
@@ -731,10 +740,12 @@ def punkt(master, farbe: str, d: int = 9) -> tk.PhotoImage:
|
|||||||
"""Farbiger Punkt (Status in Listen); Ecken bleiben durchsichtig."""
|
"""Farbiger Punkt (Status in Listen); Ecken bleiben durchsichtig."""
|
||||||
img = tk.PhotoImage(master=master, width=d + 4, height=d)
|
img = tk.PhotoImage(master=master, width=d + 4, height=d)
|
||||||
r = d / 2
|
r = d / 2
|
||||||
for y in range(d):
|
# Win95: grauer Ring, damit der Punkt auf weißem Grund und auf der blauen Auswahl sichtbar ist
|
||||||
xs = [x for x in range(d) if (x + .5 - r) ** 2 + (y + .5 - r) ** 2 <= r * r]
|
for f, rr in ((farbe, r),) if MODERN else ((SHADOW, r), (farbe, r - 1)):
|
||||||
if xs:
|
for y in range(d):
|
||||||
img.put(farbe, to=(xs[0], y, xs[-1] + 1, y + 1))
|
xs = [x for x in range(d) if (x + .5 - r) ** 2 + (y + .5 - r) ** 2 <= rr * rr]
|
||||||
|
if xs:
|
||||||
|
img.put(f, to=(xs[0], y, xs[-1] + 1, y + 1))
|
||||||
return img
|
return img
|
||||||
|
|
||||||
|
|
||||||
@@ -848,7 +859,7 @@ def _bei_bedarf(leiste: ttk.Scrollbar):
|
|||||||
|
|
||||||
def scroll_tree(master, auto: bool = False, **kw) -> tuple[tk.Frame, ttk.Treeview]:
|
def scroll_tree(master, auto: bool = False, **kw) -> tuple[tk.Frame, ttk.Treeview]:
|
||||||
"""Treeview mit Bildlaufleisten (modern nur bei Bedarf). auto: keine waagrechte Leiste (Seitenleiste)."""
|
"""Treeview mit Bildlaufleisten (modern nur bei Bedarf). auto: keine waagrechte Leiste (Seitenleiste)."""
|
||||||
rahmen = Sunken(master, bg=WHITE)
|
rahmen = Sunken(master, bg=WHITE if MODERN else FACE) # Win95: graue Ecke zwischen den Leisten
|
||||||
tree = ttk.Treeview(rahmen, **kw)
|
tree = ttk.Treeview(rahmen, **kw)
|
||||||
vs = ttk.Scrollbar(rahmen, orient="vertical", command=tree.yview)
|
vs = ttk.Scrollbar(rahmen, orient="vertical", command=tree.yview)
|
||||||
tree.grid(row=0, column=0, sticky="nsew")
|
tree.grid(row=0, column=0, sticky="nsew")
|
||||||
|
|||||||
Reference in new issue
Block a user