ich freue mich hier selber mal ein kleines Problemchen zu posten.
Ich habe ein kleines Script von einem Kollegen aus VBA in Python übernommen und ein paar dinge angepasst. Jetzt ist jedoch immer noch das Problem, da es teilweise zu einer aufwändigen Berechnung kommt, dass sich das Programm schnell mal aufhängt.
An sich soll das Programm eine Kombination aus verschiedenen Wandkassetten berechnen, ich habe dazu auch noch implimentiert, dass sich das Programm auf Datenbanken stützen kann um noch bessere kombinationen zu finden.
Hat jemand eine Ahnung wie ich solch einen Berechnungsprozess mit bspw. 10 Kassetten oder der Datenbank schnell rechnen lassen kann.
Mfg Jonas
Code: Alles auswählen
import tkinter as tk
from tkinter import messagebox
import os
RAL_7000 = "#7E8B92"
RAL_7001 = "#8A9597" # Helleres Grau für Felder und Buttons
class KassetteApp:
def __init__(self, root):
self.root = root
self.root.title("Kassetten Kombi Rechner")
# Setze das Fenster-Icon
icon_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "LOGO-TOOLKKR.ico"))
try:
self.root.iconbitmap(icon_path)
except Exception:
pass
self.root.configure(bg=RAL_7000)
self.current_page = 0
self.inputs = {
"ziel": tk.StringVar(),
"toleranz": tk.StringVar(),
"kassetten": tk.StringVar(),
"priorisiert": tk.StringVar()
}
self.beste_kombi_text = ""
self.alle_kombis = []
self.datenbank_einbeziehen = tk.BooleanVar(value=False)
self.db_dateien = []
self.db_auswahl = tk.StringVar()
# Kein Kopfbereich/Titel mehr!
self.pages = [
self.seite_zielmass,
self.seite_toleranz,
self.seite_kassetten,
self.seite_prioritaet,
self.seite_ergebnis
]
self.page_frame = tk.Frame(root, bg=RAL_7001)
self.page_frame.pack(padx=30, pady=20, fill="both", expand=True)
self.navigation_frame = tk.Frame(root, bg=RAL_7001)
self.navigation_frame.pack(pady=(0, 20))
self.btn_left = tk.Button(
self.navigation_frame, text="←", width=12, command=self.zurueck,
font=("Segoe UI", 12), bg=RAL_7001, fg="#222", relief="groove", bd=2, highlightbackground=RAL_7001
)
self.btn_left.grid(row=0, column=0, padx=20, ipadx=5, ipady=5)
self.btn_right = tk.Button(
self.navigation_frame, text="→", width=12, command=self.vor,
font=("Segoe UI", 12), bg=RAL_7001, fg="#222", relief="groove", bd=2, highlightbackground=RAL_7001
)
self.btn_right.grid(row=0, column=1, padx=20, ipadx=5, ipady=5)
# Tastatur-Shortcuts
self.root.bind('<Return>', lambda event: self.btn_right.invoke())
self.root.bind('<KP_Enter>', lambda event: self.btn_right.invoke())
self.root.bind('<Escape>', lambda event: self.btn_left.invoke())
self.show_page()
def show_page(self):
for widget in self.page_frame.winfo_children():
widget.destroy()
self.pages[self.current_page]()
self.btn_left.config(
bg=RAL_7001, highlightbackground=RAL_7001,
text="←" if self.current_page > 0 else "✖ Beenden",
command=self.zurueck if self.current_page > 0 else self.root.destroy
)
self.btn_right.config(
bg=RAL_7001, highlightbackground=RAL_7001,
text="→" if self.current_page < len(self.pages) - 1 else "✅ Berechnen",
command=self.vor if self.current_page < len(self.pages) - 1 else self.berechnen
)
def seite_zielmass(self):
tk.Label(self.page_frame, text="Gesamtmaß in mm:", font=("Segoe UI", 12), bg=RAL_7001, fg="#222").pack(pady=(30, 5))
entry = tk.Entry(self.page_frame, textvariable=self.inputs["ziel"], font=("Segoe UI", 12), justify="center", bg="#fff", fg="#222")
entry.pack(ipady=4, pady=(0, 10))
entry.focus_set()
def seite_toleranz(self):
tk.Label(self.page_frame, text="Toleranz in mm:", font=("Segoe UI", 12), bg=RAL_7001, fg="#222").pack(pady=(30, 5))
entry = tk.Entry(self.page_frame, textvariable=self.inputs["toleranz"], font=("Segoe UI", 12), justify="center", bg="#fff", fg="#222")
entry.pack(ipady=4, pady=(0, 10))
entry.focus_set()
def seite_kassetten(self):
tk.Label(self.page_frame, text="Kassettenlängen (mit , getrennt):", font=("Segoe UI", 12), bg=RAL_7001, fg="#222").pack(pady=(30, 5))
entry = tk.Entry(self.page_frame, textvariable=self.inputs["kassetten"], font=("Segoe UI", 12), justify="center", bg="#fff", fg="#222")
entry.pack(ipady=4, pady=(0, 10))
entry.focus_set()
# Checkbox unter dem Eingabefeld
cb = tk.Checkbutton(
self.page_frame,
text="Datenbank einbeziehen",
variable=self.datenbank_einbeziehen,
font=("Segoe UI", 11),
bg=RAL_7001,
fg="#222",
selectcolor=RAL_7001,
activebackground=RAL_7001,
command=self.show_page # Seite neu zeichnen, wenn Haken gesetzt/entfernt wird
)
cb.pack(pady=(10, 0))
# Dropdown für Datenbankauswahl, nur wenn Haken gesetzt
if self.datenbank_einbeziehen.get():
self.db_dateien = self.finde_db_dateien()
wk_liste = [f.replace("kassetten_db_", "").replace(".csv", "") for f in self.db_dateien]
if wk_liste:
if not self.db_auswahl.get() or self.db_auswahl.get() not in wk_liste:
self.db_auswahl.set(wk_liste[0])
tk.Label(self.page_frame, text="Datenbank auswählen:", font=("Segoe UI", 11), bg=RAL_7001, fg="#222").pack(pady=(10, 2))
tk.OptionMenu(self.page_frame, self.db_auswahl, *wk_liste).pack()
else:
tk.Label(self.page_frame, text="Keine Datenbankdateien gefunden!", font=("Segoe UI", 11), bg=RAL_7001, fg="red").pack(pady=(10, 2))
def seite_prioritaet(self):
tk.Label(self.page_frame, text="Bevorzugte Länge in mm:", font=("Segoe UI", 12), bg=RAL_7001, fg="#222").pack(pady=(30, 5))
entry = tk.Entry(self.page_frame, textvariable=self.inputs["priorisiert"], font=("Segoe UI", 12), justify="center", bg="#fff", fg="#222")
entry.pack(ipady=4, pady=(0, 10))
entry.focus_set()
def seite_ergebnis(self):
tk.Label(self.page_frame, text="Beste Kombination:", anchor="center", justify="center",
font=("Segoe UI", 13, "bold"), bg=RAL_7001, fg="#222").pack(anchor="center", pady=(30, 5))
for line in self.beste_kombi_text.split('\n'):
tk.Label(self.page_frame, text=line, anchor="center", justify="center",
font=("Segoe UI", 12), bg=RAL_7001, fg="#222").pack(anchor="center")
tk.Label(self.page_frame, text="\nAlle gültigen Kombinationen:", font=("Segoe UI", 12, "bold"),
bg=RAL_7001, fg="#222").pack(pady=(20, 5))
text_widget = tk.Text(self.page_frame, height=15, width=60, wrap="word", font=("Segoe UI", 11), bg="#f5f7f8", fg="#222", relief="flat")
text_widget.pack(pady=(0, 10))
text_widget.insert("1.0", "\n".join(self.alle_kombis))
text_widget.config(state="disabled")
def zurueck(self):
if self.current_page > 0:
self.current_page -= 1
self.show_page()
def vor(self):
if self.current_page < len(self.pages) - 1:
self.current_page += 1
self.show_page()
def berechnen(self):
try:
ziel = int(self.inputs["ziel"].get())
toleranz = int(self.inputs["toleranz"].get())
kassetten = [int(x.strip()) for x in self.inputs["kassetten"].get().split(",") if x.strip().isdigit()]
priorisiert = int(self.inputs["priorisiert"].get())
except:
messagebox.showerror("Fehler", "Bitte überprüfe deine Eingaben!")
return
db_kassetten = []
if self.datenbank_einbeziehen.get():
if not self.db_dateien:
messagebox.showerror("Fehler", "Keine Datenbankdateien gefunden!")
return
# Finde die gewählte Datei
wk = self.db_auswahl.get()
db_file = f"kassetten_db_{wk}.csv"
db_path = os.path.abspath(os.path.join(r"N:\Public\CO_Bau\_Program\Daten", db_file))
if not os.path.exists(db_path):
messagebox.showerror("Fehler", f"Datenbankdatei nicht gefunden: {db_path}")
return
import csv
with open(db_path, newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
try:
laenge = int(row["laenge"])
db_kassetten.append(laenge)
except Exception:
continue
beste, priorisiert_anzahl, gesamt, abw, alle = self.finde_kombinationen(
ziel, toleranz, kassetten, priorisiert, db_kassetten if self.datenbank_einbeziehen.get() else None
)
if not beste:
self.beste_kombi_text = "Keine gültige Kombination gefunden."
else:
if self.datenbank_einbeziehen.get():
# alle_kassetten ist die Reihenfolge von beste
alle_kassetten = list(sorted(set(db_kassetten).union(set(kassetten))))
self.beste_kombi_text = " ".join(
f"{anz}x{k}" for anz, k in zip(beste, alle_kassetten) if anz > 0
)
else:
self.beste_kombi_text = " ".join(
f"{anz}x{k}" for anz, k in zip(beste, kassetten) if anz > 0
)
self.beste_kombi_text += f"\nGesamt: {gesamt} Stück | Abweichung: {abw} mm"
self.alle_kombis = alle
self.show_page()
def finde_kombinationen(self, ziel, toleranz, pflicht_kassetten, priorisiert, db_kassetten=None):
if db_kassetten is not None:
alle_kassetten = list(sorted(set(db_kassetten).union(set(pflicht_kassetten))))
else:
alle_kassetten = pflicht_kassetten
max_anzahl = [(ziel + toleranz) // k for k in alle_kassetten]
beste_kombi = None
beste_priorisiert = -1
beste_gesamt = float("inf")
beste_abweichung = None
alle_kombis = []
def rekursiv(index, aktuelle):
nonlocal beste_kombi, beste_priorisiert, beste_gesamt, beste_abweichung
if index >= len(alle_kassetten):
summe = sum(anz * kas for anz, kas in zip(aktuelle, alle_kassetten))
abw = summe - ziel
if 0 <= abw <= toleranz: # Nur positive Abweichung zulassen
gesamt = sum(aktuelle)
anz_prior = aktuelle[alle_kassetten.index(priorisiert)] if priorisiert in alle_kassetten else 0
if db_kassetten is not None:
for k in pflicht_kassetten:
idx = alle_kassetten.index(k)
if aktuelle[idx] < 1:
return
kombi_str = " ".join(f"{anz}x{k}" for anz, k in zip(aktuelle, alle_kassetten) if anz > 0)
kombi_str += f" | Gesamt: {gesamt} | Abweichung: {abw} mm"
alle_kombis.append(kombi_str)
if (anz_prior > beste_priorisiert) or (anz_prior == beste_priorisiert and gesamt < beste_gesamt):
beste_kombi = aktuelle[:]
beste_priorisiert = anz_prior
beste_gesamt = gesamt
beste_abweichung = abw
return
for i in range(max_anzahl[index] + 1):
aktuelle[index] = i
rekursiv(index + 1, aktuelle)
rekursiv(0, [0] * len(alle_kassetten))
return beste_kombi, beste_priorisiert, beste_gesamt, beste_abweichung, alle_kombis
def finde_db_dateien(self):
pfad = r"N:\Public\CO_Bau\_Program\Daten"
files = []
for f in os.listdir(pfad):
if f.startswith("kassetten_db_WK") and f.endswith(".csv"):
files.append(f)
return files
def neu_starten(self):
for var in self.inputs.values():
var.set("")
self.datenbank_einbeziehen.set(False)
self.db_auswahl.set("")
self.current_page = 0
self.beste_kombi_text = ""
self.alle_kombis = []
self.show_page()
# App starten
if __name__ == "__main__":
root = tk.Tk()
app = KassetteApp(root)
root.mainloop()