Neues Design im Stil der Handy-Insel-Webtools: Kopfzeile, Seitenleiste, Kennzahlen, Filter, hell/dunkel, IBM Plex
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2600eea574
commit
f296483fdc
12 files changed
+654
-160
No files matched your search
+233
-40
@@ -22,17 +22,35 @@ INFO_BG = "#ffffe1"
|
||||
SEL_BG, SEL_FG = NAVY, WHITE # Auswahl/Markierung
|
||||
RAND = SHADOW # dünne Rahmenlinien (nur modern)
|
||||
DESIGN = "win95"
|
||||
MODERN = False
|
||||
MODERN = False # „modern“ oder „dunkel“
|
||||
DUNKEL = False
|
||||
APP_NAME = "Provisions-Manager 95" # modern: ohne „95“
|
||||
|
||||
# Farbnamen: FACE = Fläche (Fenster, Karten, Dialoge), BG = Grund hinter den Karten, WHITE = Eingabefelder/Listen,
|
||||
# DARK = Text, TEXT2 = Nebentext, SHADOW = leiser Text/Win95-Schatten, NAVY = Akzent, RAND/RAND2 = Linien,
|
||||
# PRIMAER/AUF_PRIMAER = Hauptknopf, MANUELL = von Hand geänderte Beträge, *_SOFT = Grund der Status-Pills.
|
||||
_PALETTEN = {
|
||||
"win95": dict(FACE="#c0c0c0", HILITE="#ffffff", LIGHT="#dfdfdf", SHADOW="#808080", DARK="#000000",
|
||||
NAVY="#000080", TEAL="#008080", WHITE="#ffffff", INFO_BG="#ffffe1", SEL_BG="#000080",
|
||||
SEL_FG="#ffffff", RAND="#808080"),
|
||||
"modern": dict(FACE="#f3f3f3", HILITE="#ffffff", LIGHT="#ebebeb", SHADOW="#8a8a8a", DARK="#1b1b1b",
|
||||
NAVY="#005fb8", TEAL="#e6eef6", WHITE="#ffffff", INFO_BG="#ffffff", SEL_BG="#cce4f7",
|
||||
SEL_FG="#1b1b1b", RAND="#d1d1d1"),
|
||||
SEL_FG="#ffffff", RAND="#808080", BG="#008080", RAND2="#808080", TEXT2="#000000",
|
||||
PRIMAER="#c0c0c0", AUF_PRIMAER="#000000", ZEBRA="#ffffff", MANUELL="#0000c0", ROT="#a00000",
|
||||
ROT_SOFT="#c0c0c0", GRUEN="#008000", GRUEN_SOFT="#c0c0c0", BLAU="#000080", BLAU_SOFT="#c0c0c0",
|
||||
GOLD="#808000", GOLD_SOFT="#c0c0c0"),
|
||||
# Hausfarben der Handy-Insel-Webtools (VVL-Tool): Lila, IBM Plex
|
||||
"modern": dict(FACE="#ffffff", HILITE="#ffffff", LIGHT="#f1edf8", SHADOW="#6b6386", DARK="#211a38",
|
||||
NAVY="#5e35b1", TEAL="#f1eef8", WHITE="#ffffff", INFO_BG="#ffffff", SEL_BG="#ede7fa",
|
||||
SEL_FG="#2d1b69", RAND="#e5e0f0", BG="#f1eef8", RAND2="#d6cee8", TEXT2="#5b5372",
|
||||
PRIMAER="#2d1b69", AUF_PRIMAER="#ffffff", ZEBRA="#faf8fd", MANUELL="#5e35b1", ROT="#b8223f",
|
||||
ROT_SOFT="#fbe4e9", GRUEN="#11734d", GRUEN_SOFT="#def3ea", BLAU="#2a59c4", BLAU_SOFT="#e4ebfb",
|
||||
GOLD="#8a6212", GOLD_SOFT="#f7eed8"),
|
||||
"dunkel": dict(FACE="#1c1436", HILITE="#1c1436", LIGHT="#2a2049", SHADOW="#948daa", DARK="#ece8f6",
|
||||
NAVY="#a88beb", TEAL="#120c22", WHITE="#221940", INFO_BG="#2a2049", SEL_BG="#3a2d66",
|
||||
SEL_FG="#ece8f6", RAND="#312551", BG="#120c22", RAND2="#3f3167", TEXT2="#ada5c6",
|
||||
PRIMAER="#c3adee", AUF_PRIMAER="#1c1436", ZEBRA="#201838", MANUELL="#c3adee", ROT="#f26982",
|
||||
ROT_SOFT="#3a1e28", GRUEN="#4fc796", GRUEN_SOFT="#163025", BLAU="#7da0f0", BLAU_SOFT="#1e2743",
|
||||
GOLD="#d8b058", GOLD_SOFT="#332a17"),
|
||||
}
|
||||
globals().update(_PALETTEN["win95"])
|
||||
|
||||
IS_WIN = sys.platform.startswith("win")
|
||||
FONT_FAMILY = "MS Sans Serif" if IS_WIN else "DejaVu Sans"
|
||||
@@ -40,26 +58,57 @@ FONT_SIZE = 8 if IS_WIN else 9
|
||||
FONT = (FONT_FAMILY, FONT_SIZE)
|
||||
FONT_BOLD = (FONT_FAMILY, FONT_SIZE, "bold")
|
||||
FONT_BIG = (FONT_FAMILY, FONT_SIZE + 4, "bold")
|
||||
FONT_MONO = ("Courier New" if IS_WIN else "DejaVu Sans Mono", FONT_SIZE + 1)
|
||||
MONO_FAMILY = "Courier New" if IS_WIN else "DejaVu Sans Mono"
|
||||
FONT_MONO = (MONO_FAMILY, FONT_SIZE + 1)
|
||||
FONT_KLEIN_FETT = FONT_TITEL = FONT_ZAHL = FONT_BOLD
|
||||
|
||||
|
||||
def design_setzen(name: str) -> None:
|
||||
"""„modern“ oder „win95“ – setzt Farben und Schriften (Feinschliff der Schrift in apply())."""
|
||||
global DESIGN, MODERN, FONT_FAMILY, FONT_SIZE, APP_NAME
|
||||
"""„modern“ (hell), „dunkel“ oder „win95“ – setzt Farben und Schriften (Feinschliff der Schrift in apply())."""
|
||||
global DESIGN, MODERN, DUNKEL, FONT_FAMILY, FONT_SIZE, APP_NAME, MONO_FAMILY
|
||||
DESIGN = name if name in _PALETTEN else "modern"
|
||||
MODERN = DESIGN == "modern"
|
||||
MODERN = DESIGN in ("modern", "dunkel")
|
||||
DUNKEL = DESIGN == "dunkel"
|
||||
APP_NAME = "Provisions-Manager" if MODERN else "Provisions-Manager 95"
|
||||
globals().update(_PALETTEN[DESIGN])
|
||||
FONT_FAMILY = ("Segoe UI" if MODERN else "MS Sans Serif") if IS_WIN else "DejaVu Sans"
|
||||
FONT_SIZE = 9 if (MODERN or not IS_WIN) else 8
|
||||
MONO_FAMILY = ("Consolas" if MODERN else "Courier New") if IS_WIN else "DejaVu Sans Mono"
|
||||
FONT_SIZE = 10 if MODERN else (8 if IS_WIN else 9)
|
||||
_schriften()
|
||||
|
||||
|
||||
def _schriften():
|
||||
global FONT, FONT_BOLD, FONT_BIG
|
||||
global FONT, FONT_BOLD, FONT_BIG, FONT_MONO, FONT_KLEIN_FETT, FONT_TITEL, FONT_ZAHL
|
||||
FONT = (FONT_FAMILY, FONT_SIZE)
|
||||
FONT_BOLD = (FONT_FAMILY, FONT_SIZE, "bold")
|
||||
FONT_BIG = (FONT_FAMILY, FONT_SIZE + 4, "bold")
|
||||
FONT_MONO = (MONO_FAMILY, FONT_SIZE + (0 if MODERN else 1))
|
||||
FONT_KLEIN_FETT = (FONT_FAMILY, FONT_SIZE - 2, "bold") # Abschnitts-Überschriften (ABRECHNUNGEN …)
|
||||
FONT_TITEL = (FONT_FAMILY, FONT_SIZE + 8, "bold") # Name der geöffneten Abrechnung
|
||||
FONT_ZAHL = (MONO_FAMILY, FONT_SIZE + 6) if MODERN else (FONT_FAMILY, FONT_SIZE + 4, "bold") # Kennzahlen
|
||||
|
||||
|
||||
SCHRIFTEN = Path(__file__).resolve().parent / "schriften"
|
||||
|
||||
|
||||
def _schriften_laden() -> None:
|
||||
"""IBM Plex (Hausschrift der Handy-Insel-Webtools) nur für dieses Programm laden – ohne Installation."""
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
dateien = sorted(SCHRIFTEN.glob("*.ttf"))
|
||||
try:
|
||||
if IS_WIN:
|
||||
for f in dateien:
|
||||
ctypes.windll.gdi32.AddFontResourceExW(str(f), 0x10, 0) # FR_PRIVATE
|
||||
return
|
||||
lib = ctypes.util.find_library("fontconfig")
|
||||
if lib:
|
||||
fc = ctypes.CDLL(lib)
|
||||
fc.FcConfigAppFontAddFile.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
||||
for f in dateien:
|
||||
fc.FcConfigAppFontAddFile(None, str(f).encode())
|
||||
except Exception:
|
||||
pass # dann eben Segoe UI / Systemschrift
|
||||
|
||||
# --------------------------------------------------------------------------- Pixel-Icons (16x16)
|
||||
PALETTE = {
|
||||
@@ -332,12 +381,15 @@ class Icons:
|
||||
# --------------------------------------------------------------------------- Theme
|
||||
|
||||
def apply(root: tk.Tk) -> None:
|
||||
global FONT_FAMILY
|
||||
if MODERN and not IS_WIN: # Linux: die erste vorhandene moderne Schrift
|
||||
global FONT_FAMILY, MONO_FAMILY
|
||||
if MODERN: # die erste vorhandene moderne Schrift, am liebsten IBM Plex
|
||||
from tkinter import font as tkfont
|
||||
_schriften_laden()
|
||||
vorhanden = set(tkfont.families(root))
|
||||
FONT_FAMILY = next((f for f in ("Segoe UI", "Noto Sans", "Cantarell", "Ubuntu", "DejaVu Sans")
|
||||
if f in vorhanden), FONT_FAMILY)
|
||||
FONT_FAMILY = next((f for f in ("IBM Plex Sans", "Segoe UI", "Noto Sans", "Cantarell", "Ubuntu",
|
||||
"DejaVu Sans") if f in vorhanden), FONT_FAMILY)
|
||||
MONO_FAMILY = next((f for f in ("IBM Plex Mono", "Consolas", "DejaVu Sans Mono") if f in vorhanden),
|
||||
MONO_FAMILY)
|
||||
_schriften()
|
||||
root.configure(bg=FACE)
|
||||
o = root.option_add
|
||||
@@ -399,40 +451,61 @@ def apply(root: tk.Tk) -> None:
|
||||
|
||||
|
||||
def _modern(root: tk.Tk, st: ttk.Style, zeile: int) -> None:
|
||||
"""Flach und hell: dünne graue Rahmen statt 3D-Kanten, Akzentfarbe beim Fokus."""
|
||||
"""Flach wie die Handy-Insel-Webtools: weiße Flächen, dünne Linien, Lila als Akzent."""
|
||||
o = root.option_add
|
||||
for klasse in ("Entry", "Text", "Listbox", "Spinbox"):
|
||||
o(f"*{klasse}.relief", "flat")
|
||||
o(f"*{klasse}.borderWidth", 3)
|
||||
o(f"*{klasse}.borderWidth", 4)
|
||||
o(f"*{klasse}.highlightThickness", 1)
|
||||
o(f"*{klasse}.highlightBackground", RAND)
|
||||
o(f"*{klasse}.highlightBackground", RAND2)
|
||||
o(f"*{klasse}.highlightColor", NAVY)
|
||||
o(f"*{klasse}.background", WHITE)
|
||||
o(f"*{klasse}.foreground", DARK)
|
||||
o(f"*{klasse}.insertBackground", DARK)
|
||||
o("*Entry.disabledBackground", LIGHT)
|
||||
o("*Button.relief", "flat")
|
||||
o("*Button.borderWidth", 0)
|
||||
o("*Button.highlightThickness", 1)
|
||||
o("*Button.highlightBackground", RAND)
|
||||
o("*Button.background", WHITE)
|
||||
o("*Button.activeBackground", SEL_BG)
|
||||
o("*Button.padY", 3)
|
||||
o("*Checkbutton.activeBackground", FACE)
|
||||
o("*Radiobutton.activeBackground", FACE)
|
||||
o("*Button.highlightBackground", RAND2)
|
||||
o("*Button.background", FACE)
|
||||
o("*Button.activeBackground", LIGHT)
|
||||
o("*Button.padX", 12)
|
||||
o("*Button.padY", 4)
|
||||
o("*Button.cursor", "hand2")
|
||||
for klasse in ("Checkbutton", "Radiobutton"):
|
||||
o(f"*{klasse}.activeBackground", FACE)
|
||||
o(f"*{klasse}.selectColor", WHITE)
|
||||
o("*selectBackground", SEL_BG)
|
||||
o("*selectForeground", SEL_FG)
|
||||
o("*Menu.relief", "flat")
|
||||
o("*TCombobox*Listbox.background", WHITE)
|
||||
o("*TCombobox*Listbox.foreground", DARK)
|
||||
o("*TCombobox*Listbox.selectBackground", SEL_BG)
|
||||
o("*TCombobox*Listbox.selectForeground", SEL_FG)
|
||||
st.theme_use("clam")
|
||||
st.configure(".", background=FACE, foreground=DARK, font=FONT, troughcolor=FACE, selectbackground=SEL_BG,
|
||||
selectforeground=SEL_FG, fieldbackground=WHITE, bordercolor=RAND, lightcolor=WHITE,
|
||||
darkcolor=RAND, focuscolor=NAVY)
|
||||
st.configure("Treeview", background=WHITE, fieldbackground=WHITE, rowheight=zeile + 4, borderwidth=0, font=FONT)
|
||||
selectforeground=SEL_FG, fieldbackground=WHITE, bordercolor=RAND, lightcolor=FACE,
|
||||
darkcolor=RAND, focuscolor=NAVY, insertcolor=DARK)
|
||||
st.configure("Treeview", background=WHITE, fieldbackground=WHITE, foreground=DARK, rowheight=zeile + 10,
|
||||
borderwidth=0, font=FONT)
|
||||
st.map("Treeview", background=[("selected", SEL_BG)], foreground=[("selected", SEL_FG)])
|
||||
st.configure("Treeview.Heading", background=WHITE, relief="flat", borderwidth=1, font=FONT, padding=(6, 3),
|
||||
bordercolor=RAND, lightcolor=WHITE, darkcolor=RAND)
|
||||
st.configure("Treeview.Heading", background=FACE, foreground=SHADOW, relief="flat", borderwidth=1,
|
||||
font=FONT_KLEIN_FETT, padding=(10, 7), bordercolor=RAND, lightcolor=FACE, darkcolor=RAND)
|
||||
st.map("Treeview.Heading", background=[("active", LIGHT)], relief=[("pressed", "flat")])
|
||||
st.configure("TScrollbar", background=LIGHT, troughcolor=FACE, arrowcolor=SHADOW, bordercolor=FACE,
|
||||
lightcolor=LIGHT, darkcolor=LIGHT, relief="flat", arrowsize=12, width=12)
|
||||
st.map("TScrollbar", background=[("active", RAND)])
|
||||
st.configure("TCombobox", fieldbackground=WHITE, background=WHITE, arrowcolor=DARK, padding=3,
|
||||
bordercolor=RAND, lightcolor=WHITE, darkcolor=WHITE)
|
||||
# Seitenleiste: ohne Kopf, hohe Zeilen, Auswahl lila wie im VVL-Tool
|
||||
st.configure("Seite.Treeview", background=FACE, fieldbackground=FACE, rowheight=zeile + 16, font=FONT,
|
||||
borderwidth=0)
|
||||
st.layout("Seite.Treeview", [("Treeview.treearea", {"sticky": "nswe"})]) # ohne Rahmen
|
||||
st.map("Seite.Treeview", background=[("selected", SEL_BG)], foreground=[("selected", NAVY)])
|
||||
st.configure("TScrollbar", background=RAND2, troughcolor=FACE, arrowcolor=SHADOW, bordercolor=FACE,
|
||||
lightcolor=RAND2, darkcolor=RAND2, relief="flat", arrowsize=10, width=10, gripcount=0)
|
||||
st.map("TScrollbar", background=[("active", SHADOW)])
|
||||
st.configure("TCombobox", fieldbackground=WHITE, background=FACE, foreground=DARK, arrowcolor=DARK, padding=4,
|
||||
bordercolor=RAND2, lightcolor=WHITE, darkcolor=WHITE)
|
||||
st.map("TCombobox", fieldbackground=[("readonly", WHITE)], selectbackground=[("readonly", WHITE)],
|
||||
selectforeground=[("readonly", DARK)], bordercolor=[("focus", NAVY)])
|
||||
st.configure("TPanedwindow", background=BG)
|
||||
st.configure("Sash", sashthickness=6, background=BG)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- Wort löschen
|
||||
@@ -566,12 +639,19 @@ class Statusleiste(tk.Frame):
|
||||
self.vars[key].set(text)
|
||||
|
||||
|
||||
def knopf(master, text: str, command=None, icon: tk.PhotoImage | None = None, breite: int | None = None, **kw):
|
||||
def knopf(master, text: str, command=None, icon: tk.PhotoImage | None = None, breite: int | None = None,
|
||||
primaer: bool = False, **kw):
|
||||
"""primaer: der eine Hauptknopf einer Fläche (modern dunkel-lila gefüllt, Win95 mit dickem Rahmen)."""
|
||||
if MODERN:
|
||||
if primaer:
|
||||
kw = dict(bg=PRIMAER, fg=AUF_PRIMAER, activebackground=NAVY, activeforeground=AUF_PRIMAER,
|
||||
highlightbackground=PRIMAER, font=FONT_BOLD) | kw
|
||||
b = tk.Button(master, text=text, command=command, **kw) # Aussehen aus apply()/_modern()
|
||||
else:
|
||||
b = tk.Button(master, text=text, command=command, bd=2, relief="raised", bg=FACE,
|
||||
activebackground=FACE, **kw)
|
||||
if primaer:
|
||||
b.configure(default="active")
|
||||
if icon is not None:
|
||||
b.configure(image=icon, compound="left", padx=6)
|
||||
if breite:
|
||||
@@ -579,6 +659,104 @@ def knopf(master, text: str, command=None, icon: tk.PhotoImage | None = None, br
|
||||
return b
|
||||
|
||||
|
||||
class Karte(tk.Frame):
|
||||
"""Fläche, die Inhalte zusammenfasst (Kennzahl, Tabelle): modern weiß mit dünner Linie."""
|
||||
|
||||
def __init__(self, master, **kw):
|
||||
if MODERN:
|
||||
kw = dict(bg=FACE, bd=0, highlightthickness=1, highlightbackground=RAND, highlightcolor=RAND) | kw
|
||||
else:
|
||||
kw = dict(bg=FACE, bd=2, relief="raised") | kw
|
||||
super().__init__(master, **kw)
|
||||
|
||||
|
||||
def abschnitt(master, text: str, **kw) -> tk.Label:
|
||||
"""Kleine Überschrift (modern in Großbuchstaben und grau, wie die Tabellenköpfe der Webtools)."""
|
||||
if MODERN:
|
||||
return tk.Label(master, text=text.upper(), fg=SHADOW, font=FONT_KLEIN_FETT, anchor="w", **kw)
|
||||
return tk.Label(master, text=text, font=FONT_BOLD, anchor="w", **kw)
|
||||
|
||||
|
||||
def _rund(c: tk.Canvas, x0, y0, x1, y1, r, **kw):
|
||||
"""Abgerundetes Rechteck auf einem Canvas."""
|
||||
r = min(r, (x1 - x0) / 2, (y1 - y0) / 2)
|
||||
pkt = [x0 + r, y0, x1 - r, y0, x1, y0, x1, y0 + r, x1, y1 - r, x1, y1, x1 - r, y1, x0 + r, y1, x0, y1,
|
||||
x0, y1 - r, x0, y0 + r, x0, y0]
|
||||
return c.create_polygon(pkt, smooth=True, **kw)
|
||||
|
||||
|
||||
class Pill(tk.Canvas):
|
||||
"""Umschalter für Filter („Alle“, „Von Hand geändert“ …): modern abgerundet, Win95 als Knopf,
|
||||
der eingedrückt bleibt. command bekommt nichts übergeben; aktiv wird von außen gesetzt."""
|
||||
|
||||
def __init__(self, master, text: str, command=None, aktiv: bool = False, **kw):
|
||||
from tkinter import font as tkfont
|
||||
f = tkfont.Font(root=master, font=FONT)
|
||||
breite, hoehe = f.measure(text) + (24 if MODERN else 16), f.metrics("linespace") + (10 if MODERN else 8)
|
||||
bg = kw.pop("bg", master.cget("bg"))
|
||||
super().__init__(master, width=breite, height=hoehe, bg=bg, highlightthickness=0, bd=0, takefocus=1,
|
||||
cursor="hand2" if MODERN else "", **kw)
|
||||
self.text, self.command, self.aktiv = text, command, aktiv
|
||||
self._zeichnen()
|
||||
self.bind("<Button-1>", lambda e: self._ausloesen())
|
||||
self.bind("<space>", lambda e: self._ausloesen())
|
||||
self.bind("<Return>", lambda e: self._ausloesen())
|
||||
|
||||
def _ausloesen(self):
|
||||
self.focus_set()
|
||||
if self.command:
|
||||
self.command()
|
||||
|
||||
def setzen(self, aktiv: bool):
|
||||
if aktiv != self.aktiv:
|
||||
self.aktiv = aktiv
|
||||
self._zeichnen()
|
||||
|
||||
def _zeichnen(self):
|
||||
self.delete("all")
|
||||
b, h = int(self["width"]), int(self["height"])
|
||||
if MODERN:
|
||||
fuell, linie, fg = (PRIMAER, PRIMAER, AUF_PRIMAER) if self.aktiv else (FACE, RAND2, TEXT2)
|
||||
_rund(self, 1, 1, b - 2, h - 2, h, fill=fuell, outline=linie)
|
||||
else:
|
||||
oben, unten = (SHADOW, HILITE) if self.aktiv else (HILITE, SHADOW)
|
||||
self.create_rectangle(0, 0, b - 1, h - 1, fill=LIGHT if self.aktiv else FACE, outline=DARK)
|
||||
self.create_line(1, h - 2, 1, 1, b - 2, 1, fill=oben)
|
||||
self.create_line(1, h - 2, b - 2, h - 2, b - 2, 1, fill=unten)
|
||||
fg = DARK
|
||||
self.create_text(b // 2 + (1 if self.aktiv and not MODERN else 0), h // 2, text=self.text, fill=fg, font=FONT)
|
||||
|
||||
|
||||
def punkt(master, farbe: str, d: int = 9) -> tk.PhotoImage:
|
||||
"""Farbiger Punkt (Status in Listen); Ecken bleiben durchsichtig."""
|
||||
img = tk.PhotoImage(master=master, width=d + 4, height=d)
|
||||
r = d / 2
|
||||
for y in range(d):
|
||||
xs = [x for x in range(d) if (x + .5 - r) ** 2 + (y + .5 - r) ** 2 <= r * r]
|
||||
if xs:
|
||||
img.put(farbe, to=(xs[0], y, xs[-1] + 1, y + 1))
|
||||
return img
|
||||
|
||||
|
||||
def logo(master, groesse: int = 34) -> tk.Canvas:
|
||||
"""Logo-Kachel wie in den Webtools: Lila-Verlauf, abgerundet, weißes %."""
|
||||
c = tk.Canvas(master, width=groesse, height=groesse, bg=master.cget("bg"), highlightthickness=0, bd=0)
|
||||
img = tk.PhotoImage(master=master, width=groesse, height=groesse)
|
||||
a, e = (0x2d, 0x1b, 0x69), (0x5e, 0x35, 0xb1)
|
||||
r = groesse * 0.28
|
||||
for y in range(groesse):
|
||||
for x in range(groesse):
|
||||
dx = max(r - x - .5, x + .5 - (groesse - r), 0)
|
||||
dy = max(r - y - .5, y + .5 - (groesse - r), 0)
|
||||
if dx * dx + dy * dy <= r * r:
|
||||
t = (x + y) / (2 * groesse - 2)
|
||||
img.put("#%02x%02x%02x" % tuple(round(a[i] + (e[i] - a[i]) * t) for i in range(3)), (x, y))
|
||||
c.create_image(0, 0, image=img, anchor="nw")
|
||||
c.bild = img # type: ignore[attr-defined] # Referenz halten
|
||||
c.create_text(groesse / 2, groesse / 2, text="%", fill="#ffffff", font=(FONT_FAMILY, int(groesse * .4), "bold"))
|
||||
return c
|
||||
|
||||
|
||||
class ToolbarButton(tk.Label):
|
||||
"""Flacher Werkzeugleisten-Knopf (Office-95-Stil): hebt sich beim Überfahren, senkt sich beim Klick."""
|
||||
|
||||
@@ -660,15 +838,28 @@ def gruppe(master, titel: str, **kw) -> tk.LabelFrame:
|
||||
return tk.LabelFrame(master, text=titel, bd=2, relief="groove", bg=FACE, padx=6, pady=4, **kw)
|
||||
|
||||
|
||||
def scroll_tree(master, **kw) -> tuple[tk.Frame, ttk.Treeview]:
|
||||
def _bei_bedarf(leiste: ttk.Scrollbar):
|
||||
"""Bildlaufleiste nur zeigen, wenn nicht alles hineinpasst."""
|
||||
def setzen(lo, hi):
|
||||
leiste.grid() if float(lo) > 0 or float(hi) < 1 else leiste.grid_remove()
|
||||
leiste.set(lo, hi)
|
||||
return setzen
|
||||
|
||||
|
||||
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)."""
|
||||
rahmen = Sunken(master, bg=WHITE)
|
||||
tree = ttk.Treeview(rahmen, **kw)
|
||||
vs = ttk.Scrollbar(rahmen, orient="vertical", command=tree.yview)
|
||||
hs = ttk.Scrollbar(rahmen, orient="horizontal", command=tree.xview)
|
||||
tree.configure(yscrollcommand=vs.set, xscrollcommand=hs.set)
|
||||
tree.grid(row=0, column=0, sticky="nsew")
|
||||
vs.grid(row=0, column=1, sticky="ns")
|
||||
hs.grid(row=1, column=0, sticky="ew")
|
||||
if auto and MODERN:
|
||||
rahmen.configure(highlightthickness=0, bg=FACE)
|
||||
if not auto:
|
||||
hs = ttk.Scrollbar(rahmen, orient="horizontal", command=tree.xview)
|
||||
hs.grid(row=1, column=0, sticky="ew")
|
||||
tree.configure(xscrollcommand=_bei_bedarf(hs) if MODERN else hs.set)
|
||||
tree.configure(yscrollcommand=_bei_bedarf(vs) if auto or MODERN else vs.set)
|
||||
rahmen.rowconfigure(0, weight=1)
|
||||
rahmen.columnconfigure(0, weight=1)
|
||||
return rahmen, tree
|
||||
@@ -747,6 +938,8 @@ class Dialog(tk.Toplevel):
|
||||
self.geometry(f"{w}x{h}+{max(0, x)}+{max(0, y)}") # Größe + Position zusammen (siehe Tooltip)
|
||||
self.deiconify()
|
||||
self.update_idletasks()
|
||||
if DUNKEL:
|
||||
win32.dunkle_titelleiste(self)
|
||||
if KLASSISCH and win32.IS_WIN:
|
||||
# eigener Taskleisten-Eintrag; wird erst nach kurzem Aus-/Einblenden wirksam
|
||||
if win32.taskleisteneintrag(self):
|
||||
@@ -796,7 +989,7 @@ class Dialog(tk.Toplevel):
|
||||
def knopfleiste(self, knoepfe: list[tuple[str, object]], default: int = 0) -> tk.Frame:
|
||||
f = tk.Frame(self, bg=FACE)
|
||||
for i, (text, cmd) in enumerate(knoepfe):
|
||||
b = knopf(f, text, cmd, breite=10)
|
||||
b = knopf(f, text, cmd, breite=10, primaer=MODERN and i == default)
|
||||
if i == default:
|
||||
b.configure(default="active")
|
||||
b.pack(side="left", padx=4)
|
||||
|
||||
Reference in new issue
Block a user