Seine Ansage: "rechnungsdaten oben links, darunter rechnung an kunde, den tabellen kasten rechts - oder noch besser dnd einbauen" und dann genauer: "wie bei opnsense hast so einen kleinen knopf, dann drueckst drauf, ab da kannst du schieben, dann hast du einen save button, und dann ist die fest". - Seine Anordnung ist die Vorgabe (flaeche.STANDARD). - Verschieben muss man EINSCHALTEN. Ohne den Knopf sitzt alles fest, damit beim taeglichen Arbeiten nichts verrutscht. Im Modus stehen "Speichern" und "Verwerfen" daneben; erst Speichern schreibt in die config. - Waehrend des Ziehens haengt ein Etikett am Zeiger und der gegriffene Kasten wird blass - vorher sah man nur das Ziel und es wirkte "statisch" (sein Fund). - Getauscht statt frei abgelegt: freies Verschieben auf Pixel braucht Regeln fuer Ueberlappung, Groesse und leere Zellen, und am Ende steht das Fenster schief. Ein Tausch ist in einem Griff erledigt. - Der freie Platz folgt dem Kasten mit der langen Liste. Feste Gewichte gingen nicht: die Liste wandert, und der Kundenkasten wurde unten abgeschnitten. - Eine unbrauchbare Anordnung in der config faellt auf die Vorgabe zurueck. Icons: die Material Symbols vom Hub (M:/projekte/material-symbols), einmal nach PNG gewandelt und zur Laufzeit eingefaerbt (icons.py) - eine Datei je Icon, nicht drei Fassungen fuer hell/dunkel/Akzent. ⚠️ Ein PhotoImage gehoert GENAU EINEM Tk-Fenster. Der Icon-Cache reichte Bilder an das naechste weiter, sobald ein Pruefstand Journal und Rechnungstool nacheinander faehrt: 'image "pyimage1" doesn't exist'. Das Fenster wird jetzt mitgegeben, tk._default_root reicht dafuer nicht. Neu: pruef_flaeche.py. Alle 15 Pruefstaende gruen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V4t48uxDok5rJXhC9bX1Ax
62 lines
2.5 KiB
Python
62 lines
2.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Icons aus dem Hub-Vorrat (Material Symbols), eingefaerbt zur Laufzeit.
|
|
|
|
Die SVGs liegen auf dem Hub (`M:/projekte/material-symbols`) und wurden einmal
|
|
nach PNG gewandelt - als weisse Silhouette mit Transparenz. Hier bekommen sie
|
|
ihre Farbe: dieselbe Datei taugt fuer die Akzentfarbe, fuer gedaempfte Schrift
|
|
und fuer das helle Thema, ohne dass jemand drei Fassungen pflegt.
|
|
|
|
Ohne Pillow oder ohne Datei gibt es einfach kein Bild zurueck - der Aufrufer
|
|
zeigt dann seinen Text. Ein fehlendes Icon darf kein Fenster verhindern.
|
|
"""
|
|
import os
|
|
import tkinter as tk
|
|
|
|
import config
|
|
|
|
# ⚠️ Ein PhotoImage gehoert zu GENAU EINEM Tk-Fenster. Wird ein zweites
|
|
# aufgemacht (ein Pruefstand faehrt Journal und Rechnungstool nacheinander),
|
|
# zeigt ein weitergereichtes Bild ins Leere: 'image "pyimage1" doesn't exist'.
|
|
# Deshalb steht das Fenster mit im Schluessel.
|
|
_zwischenlager = {} # (fenster, name, groesse, farbe) -> PhotoImage
|
|
|
|
|
|
def _pfad(name):
|
|
for basis in (config.BASE_DIR, config.RESOURCE_DIR,
|
|
os.path.dirname(os.path.abspath(__file__))):
|
|
p = os.path.join(basis, "assets", "icons", name + ".png")
|
|
if os.path.exists(p):
|
|
return p
|
|
return ""
|
|
|
|
|
|
def hole(name, groesse=18, farbe="#e8e8ea", master=None):
|
|
"""Icon als PhotoImage - oder None, wenn es nicht geht.
|
|
|
|
`master` ist das Widget, in dem das Bild landet. Es MUSS mitgegeben werden,
|
|
wenn mehr als ein Tk-Fenster laeuft: `tk._default_root` zeigt dann auf das
|
|
zuerst geoeffnete, und das Bild gehoert dem falschen Fenster.
|
|
"""
|
|
fenster = master if master is not None else tk._default_root
|
|
if fenster is None:
|
|
return None
|
|
schluessel = (id(fenster), name, groesse, farbe)
|
|
if schluessel in _zwischenlager:
|
|
return _zwischenlager[schluessel]
|
|
pfad = _pfad(name)
|
|
if not pfad:
|
|
print(f"[icons] nicht gefunden: {name}")
|
|
return None
|
|
try:
|
|
from PIL import Image, ImageTk
|
|
bild = Image.open(pfad).convert("RGBA")
|
|
bild = bild.resize((groesse, groesse), Image.LANCZOS)
|
|
# Die Silhouette steckt im Alphakanal - die Farbe kommt hier drueber.
|
|
flaeche = Image.new("RGBA", bild.size, farbe)
|
|
flaeche.putalpha(bild.getchannel("A"))
|
|
fertig = ImageTk.PhotoImage(flaeche, master=fenster)
|
|
except Exception as e: # noqa: BLE001 - ein Icon darf nichts blockieren
|
|
print(f"[icons] {name} nicht ladbar: {e}")
|
|
return None
|
|
_zwischenlager[schluessel] = fertig
|
|
return fertig
|