initial commit

this is a copy ;3
This commit is contained in:
Rattatwinko
2025-04-15 18:41:08 +02:00
committed by GitHub
commit ea80ed32fa
23 changed files with 2330 additions and 0 deletions

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

1542
PfandApplication/main.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,218 @@
import tkinter as tk
from tkinter import ttk, simpledialog, messagebox
import cv2
from PIL import Image, ImageTk
from pyzbar.pyzbar import decode
from datetime import datetime, timedelta
import threading
import queue
import json
import os
class PfandScanner:
def __init__(self, window, window_title):
self.window = window
self.window.title(window_title)
self.data_file = "quantities.json"
self.load_json()
self.barcode_times = {}
self.prompted_barcodes = set()
self.camera_frame = ttk.Frame(window)
self.camera_frame.pack(side="left", padx=10, pady=5)
self.control_frame = ttk.Frame(window)
self.control_frame.pack(side="left", padx=10, pady=5, fill="y")
self.info_frame = ttk.Frame(window)
self.info_frame.pack(side="right", padx=10, pady=5, fill="both", expand=True)
self.camera_label = ttk.Label(self.camera_frame)
self.camera_label.pack()
focus_frame = ttk.LabelFrame(self.control_frame, text="Camera Controls")
focus_frame.pack(pady=5, padx=5, fill="x")
ttk.Label(focus_frame, text="Focus:").pack(pady=2)
self.focus_slider = ttk.Scale(focus_frame, from_=0, to=255, orient="horizontal")
self.focus_slider.set(0)
self.focus_slider.pack(pady=2, padx=5, fill="x")
self.autofocus_var = tk.BooleanVar(value=True)
self.autofocus_check = ttk.Checkbutton(
focus_frame, text="Autofocus", variable=self.autofocus_var, command=self.toggle_autofocus)
self.autofocus_check.pack(pady=2)
process_frame = ttk.LabelFrame(self.control_frame, text="Image Processing")
process_frame.pack(pady=5, padx=5, fill="x")
ttk.Label(process_frame, text="Brightness:").pack(pady=2)
self.brightness_slider = ttk.Scale(process_frame, from_=0, to=100, orient="horizontal")
self.brightness_slider.set(50)
self.brightness_slider.pack(pady=2, padx=5, fill="x")
ttk.Label(process_frame, text="Contrast:").pack(pady=2)
self.contrast_slider = ttk.Scale(process_frame, from_=0, to=100, orient="horizontal")
self.contrast_slider.set(50)
self.contrast_slider.pack(pady=2, padx=5, fill="x")
self.tree = ttk.Treeview(self.info_frame, columns=("Time", "Barcode", "Type", "Deposit"), show="headings")
self.tree.heading("Time", text="Time")
self.tree.heading("Barcode", text="Barcode")
self.tree.heading("Type", text="Type")
self.tree.heading("Deposit", text="Deposit (€)")
self.tree.column("Time", width=150)
self.tree.column("Barcode", width=200)
self.tree.column("Type", width=100)
self.tree.column("Deposit", width=100)
self.tree.pack(fill="both", expand=True)
scrollbar = ttk.Scrollbar(self.info_frame, orient="vertical", command=self.tree.yview)
scrollbar.pack(side="right", fill="y")
self.tree.configure(yscrollcommand=scrollbar.set)
self.cap = cv2.VideoCapture(0)
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
self.cap.set(cv2.CAP_PROP_AUTOFOCUS, 0)
self.cap.set(cv2.CAP_PROP_FOCUS, 0)
self.queue = queue.Queue()
self.pfand_values = {
"EINWEG": 0.25,
"MEHRWEG": 0.15,
"DOSE": 0.25,
}
self.process_video()
self.window.protocol("WM_DELETE_WINDOW", self.on_closing)
self.process_queue()
def load_json(self):
if os.path.exists(self.data_file):
with open(self.data_file, 'r') as f:
self.quantities = json.load(f)
else:
self.quantities = {}
def save_json(self):
with open(self.data_file, 'w') as f:
json.dump(self.quantities, f, indent=4)
def toggle_autofocus(self):
if self.autofocus_var.get():
self.cap.set(cv2.CAP_PROP_AUTOFOCUS, 1)
self.focus_slider.state(['disabled'])
else:
self.cap.set(cv2.CAP_PROP_AUTOFOCUS, 0)
self.focus_slider.state(['!disabled'])
self.cap.set(cv2.CAP_PROP_FOCUS, self.focus_slider.get())
def adjust_image(self, frame):
brightness = self.brightness_slider.get() / 50.0 - 1.0
contrast = self.contrast_slider.get() / 50.0
adjusted = cv2.convertScaleAbs(frame, alpha=contrast, beta=brightness * 127)
gray = cv2.cvtColor(adjusted, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
binary = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
return binary
def process_video(self):
ret, frame = self.cap.read()
if ret:
if not self.autofocus_var.get():
self.cap.set(cv2.CAP_PROP_FOCUS, self.focus_slider.get())
processed_frame = self.adjust_image(frame)
barcodes = decode(processed_frame) or decode(frame)
for barcode in barcodes:
points = barcode.polygon
if len(points) == 4:
pts = [(p.x, p.y) for p in points]
cv2.polylines(frame, [cv2.convexHull(cv2.UMat(cv2.Mat(pts))).get()], True, (0, 255, 0), 2)
barcode_data = barcode.data.decode('utf-8')
self.queue.put(barcode_data)
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
self.camera_label.imgtk = imgtk
self.camera_label.configure(image=imgtk)
self.window.after(10, self.process_video)
def show_product_selection(self, barcode_data):
if hasattr(self, 'product_win') and self.product_win.winfo_exists():
return # prevent multiple dialogs
self.product_win = tk.Toplevel(self.window)
self.product_win.title("Produktwahl")
ttk.Label(self.product_win, text=f"Welches Produkt soll dem Barcode '{barcode_data}' zugeordnet werden?").pack(pady=5)
selected_product = tk.StringVar()
for prod in self.quantities:
ttk.Radiobutton(self.product_win, text=prod, variable=selected_product, value=prod).pack(anchor='w')
def confirm():
prod = selected_product.get()
if prod:
self.quantities[prod] += 1
self.save_json()
self.product_win.destroy()
else:
messagebox.showwarning("Keine Auswahl", "Bitte ein Produkt auswählen.")
ttk.Button(self.product_win, text="Bestätigen", command=confirm).pack(pady=5)
def process_queue(self):
try:
while True:
barcode_data = self.queue.get_nowait()
now = datetime.now()
if barcode_data in self.barcode_times:
timestamps = self.barcode_times[barcode_data]
timestamps = [t for t in timestamps if now - t <= timedelta(seconds=20)]
if len(timestamps) >= 10:
continue
timestamps.append(now)
self.barcode_times[barcode_data] = timestamps
else:
self.barcode_times[barcode_data] = [now]
current_time = now.strftime("%Y-%m-%d %H:%M:%S")
if len(barcode_data) == 13:
pfand_type = "EINWEG"
elif len(barcode_data) == 8:
pfand_type = "MEHRWEG"
else:
pfand_type = "DOSE"
deposit = self.pfand_values.get(pfand_type, 0.00)
self.tree.insert("", 0, values=(current_time, barcode_data, pfand_type, f"{deposit:.2f}"))
if barcode_data not in self.prompted_barcodes:
self.prompted_barcodes.add(barcode_data)
self.window.after(0, self.show_product_selection, barcode_data)
except queue.Empty:
pass
finally:
self.window.after(100, self.process_queue)
def on_closing(self):
if self.cap.isOpened():
self.cap.release()
self.window.destroy()
if __name__ != "__main__":
def launch_pfand_scanner():
scanner_window = tk.Toplevel()
PfandScanner(scanner_window, "µScan V1.1.0")

255
PfandApplication/updater.py Normal file
View File

@@ -0,0 +1,255 @@
import tkinter as tk
from tkinter import ttk, messagebox
import os
import requests
import hashlib
from zipfile import ZipFile
import io
import shutil
import tempfile
import traceback
import threading
GITHUB_REPO_ZIP = "https://github.com/ZockerKatze/pfand/archive/refs/heads/main.zip"
IGNORED_FILES = {"key.py"}
class GitHubUpdater(tk.Toplevel):
def __init__(self, master=None):
super().__init__(master)
self.title("🔄 Pfand Updater")
self.geometry("800x600")
self.configure(bg="#ffffff")
self.local_dir = os.getcwd()
self.file_differences = []
self.structure = {}
self.current_view = "root"
self._setup_style()
self._build_ui()
# Run update check in background
threading.Thread(target=self.check_for_updates, daemon=True).start()
def _setup_style(self):
style = ttk.Style(self)
style.theme_use('clam')
style.configure("TLabel", font=("Segoe UI", 11), background="#ffffff")
style.configure("TButton", font=("Segoe UI", 10), padding=6, relief="flat", borderwidth=0)
style.map("TButton", background=[("active", "#e0e0e0")])
style.configure("Header.TLabel", font=("Segoe UI", 20, "bold"), background="#ffffff", foreground="#333")
style.configure("Status.TLabel", font=("Segoe UI", 12), background="#ffffff", foreground="#555")
style.configure("Treeview", font=("Segoe UI", 10))
style.configure("TFrame", background="#ffffff")
def _build_ui(self):
header = ttk.Label(self, text="Pfand Updater", style="Header.TLabel")
header.pack(pady=(20, 5))
self.status_label = ttk.Label(self, text="🔍 Suche nach Updates...", style="Status.TLabel")
self.status_label.pack(pady=(0, 10))
self.frame = ttk.Frame(self)
self.frame.pack(expand=True, fill="both", padx=20, pady=10)
self.canvas = tk.Canvas(self.frame, bg="#fafafa", bd=0, highlightthickness=0)
self.scrollbar = ttk.Scrollbar(self.frame, orient="vertical", command=self.canvas.yview)
self.scrollable_frame = ttk.Frame(self.canvas)
self.scrollable_frame.bind(
"<Configure>",
lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))
)
self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.canvas.pack(side="left", fill="both", expand=True)
self.scrollbar.pack(side="right", fill="y")
button_frame = ttk.Frame(self)
button_frame.pack(pady=15)
self.back_button = ttk.Button(button_frame, text="⬅️ Zurück", command=self.show_root_view)
self.back_button.pack(side="left", padx=10)
self.back_button.pack_forget()
self.update_button = ttk.Button(button_frame, text="⬆️ Dateien aktualisieren", command=self.perform_update, state='disabled')
self.update_button.pack(side="left", padx=10)
self.toggle_debug_btn = ttk.Button(self, text="🐞 Fehlerdetails anzeigen", command=self.toggle_debug_output)
self.toggle_debug_btn.pack()
self.toggle_debug_btn.pack_forget()
self.debug_output = tk.Text(self, height=8, bg="#f5f5f5", font=("Courier", 9))
self.debug_output.pack(fill="x", padx=20, pady=(0, 10))
self.debug_output.pack_forget()
self.debug_visible = False
def toggle_debug_output(self):
self.debug_visible = not self.debug_visible
if self.debug_visible:
self.debug_output.pack()
self.toggle_debug_btn.config(text="🔽 Fehlerdetails verbergen")
else:
self.debug_output.pack_forget()
self.toggle_debug_btn.config(text="🐞 Fehlerdetails anzeigen")
def show_root_view(self):
self.current_view = "root"
self.back_button.pack_forget()
self.display_structure(self.structure)
def display_structure(self, struct, parent_path=""):
for widget in self.scrollable_frame.winfo_children():
widget.destroy()
for name, content in sorted(struct.items()):
full_path = os.path.join(parent_path, name)
lbl = ttk.Label(self.scrollable_frame, text=f"📁 {name}" if isinstance(content, dict) else f"📄 {name}", style="TLabel")
if isinstance(content, dict):
lbl.bind("<Double-Button-1>", lambda e, p=full_path: self.open_folder(p))
lbl.pack(fill="x", padx=20, pady=6, anchor="w")
def open_folder(self, folder_path):
self.current_view = folder_path
self.back_button.pack()
parts = folder_path.split(os.sep)
subtree = self.structure
for part in parts:
subtree = subtree.get(part, {})
self.display_structure(subtree, folder_path)
def check_for_updates(self):
try:
self.status_label.config(text="⬇️ Lade Update herunter...", foreground="#ffb300")
self.update_idletasks()
response = requests.get(GITHUB_REPO_ZIP)
with ZipFile(io.BytesIO(response.content)) as zip_file:
temp_dir = tempfile.mkdtemp()
zip_file.extractall(temp_dir)
extracted_path = os.path.join(temp_dir, os.listdir(temp_dir)[0])
self.file_differences = self.compare_directories(extracted_path, self.local_dir)
if self.file_differences:
self.structure = self.build_structure(self.file_differences)
self.status_label.config(text="⚠️ Updates verfügbar", foreground="#e53935")
self.display_structure(self.structure)
self.update_button.config(state='normal')
else:
self.status_label.config(text="✅ Alles ist aktuell", foreground="#43a047")
except Exception:
self.status_label.config(text="❌ Fehler beim Laden", foreground="#e53935")
self.toggle_debug_btn.pack()
self.debug_output.insert("1.0", traceback.format_exc())
def compare_directories(self, src_dir, dest_dir):
differences = []
for root, _, files in os.walk(src_dir):
for file in files:
if file in IGNORED_FILES:
continue
src_path = os.path.join(root, file)
rel_path = os.path.relpath(src_path, src_dir)
dest_path = os.path.join(dest_dir, rel_path)
if not os.path.exists(dest_path) or not self.files_match(src_path, dest_path):
differences.append(rel_path)
return differences
def build_structure(self, file_paths):
tree = {}
for path in file_paths:
parts = path.split(os.sep)
d = tree
for part in parts[:-1]:
d = d.setdefault(part, {})
d[parts[-1]] = path
return tree
def files_match(self, file1, file2):
return self.hash_file(file1) == self.hash_file(file2)
def hash_file(self, filepath):
hash_md5 = hashlib.md5()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
def perform_update(self):
self.update_button.config(state='disabled')
self.status_label.config(text="🚧 Update läuft...", foreground="#fb8c00")
self.update_idletasks()
try:
response = requests.get(GITHUB_REPO_ZIP)
with ZipFile(io.BytesIO(response.content)) as zip_file:
temp_dir = tempfile.mkdtemp()
zip_file.extractall(temp_dir)
extracted_path = os.path.join(temp_dir, os.listdir(temp_dir)[0])
for rel_path in self.file_differences:
src_path = os.path.join(extracted_path, rel_path)
dest_path = os.path.join(self.local_dir, rel_path)
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
shutil.copy2(src_path, dest_path)
messagebox.showinfo("✅ Aktualisiert", "Dateien wurden erfolgreich aktualisiert.")
self.destroy()
except Exception as e:
messagebox.showerror("❌ Fehler", str(e))
self.toggle_debug_btn.pack()
self.debug_output.insert("1.0", traceback.format_exc())
@staticmethod
def files_match_static(file1, file2):
def hash_file(filepath):
hash_md5 = hashlib.md5()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
return hash_file(file1) == hash_file(file2)
def run_silent_update(master=None):
try:
response = requests.get(GITHUB_REPO_ZIP)
with ZipFile(io.BytesIO(response.content)) as zip_file:
temp_dir = tempfile.mkdtemp()
zip_file.extractall(temp_dir)
extracted_path = os.path.join(temp_dir, os.listdir(temp_dir)[0])
file_differences = []
for root_dir, _, files in os.walk(extracted_path):
for file in files:
if file in IGNORED_FILES:
continue
src_path = os.path.join(root_dir, file)
rel_path = os.path.relpath(src_path, extracted_path)
dest_path = os.path.join(os.getcwd(), rel_path)
if not os.path.exists(dest_path) or not GitHubUpdater.files_match_static(src_path, dest_path):
file_differences.append(rel_path)
if file_differences:
result = messagebox.askyesno("🔄 Update verfügbar", "Es sind Updates verfügbar. Möchten Sie aktualisieren?")
if result:
updater = GitHubUpdater(master)
updater.grab_set()
else:
print("Keine Updates verfügbar.")
except Exception as e:
print(f"Update-Check-Fehler: {e}")
def open_updater():
root = tk.Tk()
root.withdraw()
updater = GitHubUpdater(root)
updater.mainloop()

Binary file not shown.

View File

@@ -0,0 +1,19 @@
Kategorie,Produkt,Einzeln (€),Kiste inkl. aller Flaschen (€)
Bier,"Bierflasche 0,5l",0.20,
Bier,"Bierkiste 0,5l (LogiPack, Gösser, Stiegl, Puntigamer, Egger) 20er leer",3.00,7.00
Alkoholfreie Getränke,Römerquelle 1l,0.29,
Alkoholfreie Getränke,Splitkiste Römerquelle 1l 6er leer,2.00,3.74
Alkoholfreie Getränke,Splitkiste Römerquelle 1l 12er leer,4.00,7.48
Alkoholfreie Getränke,Coca Cola 1l,0.29,
Alkoholfreie Getränke,Splitkiste Coca Cola 1l 6er leer,2.00,3.74
Alkoholfreie Getränke,Splitkiste Coca Cola 1l 12er leer,4.00,7.48
Alkoholfreie Getränke,Kiste Coca Cola 1l 12er leer,3.00,6.48
Alkoholfreie Getränke,Normflasche 1l,0.29,
Alkoholfreie Getränke,Normkiste 1l 6er leer,3.00,4.74
Alkoholfreie Getränke,Exklusivmarken-Flasche 1l,0.29,
Alkoholfreie Getränke,Exklusivmarken-Kiste 1l 6er leer,3.00,4.74
Molkerei,Milchflasche 1l,0.22,
Molkerei,Milchkiste 1l 6er leer,5.50,6.82
Molkerei,Joghurtglas,0.17,
Einweg,Kunststoffflaschen,0.25,
Einweg,Metalldosen,0.25,
1 Kategorie Produkt Einzeln (€) Kiste inkl. aller Flaschen (€)
2 Bier Bierflasche 0,5l 0.20
3 Bier Bierkiste 0,5l (LogiPack, Gösser, Stiegl, Puntigamer, Egger) 20er leer 3.00 7.00
4 Alkoholfreie Getränke Römerquelle 1l 0.29
5 Alkoholfreie Getränke Splitkiste Römerquelle 1l 6er leer 2.00 3.74
6 Alkoholfreie Getränke Splitkiste Römerquelle 1l 12er leer 4.00 7.48
7 Alkoholfreie Getränke Coca Cola 1l 0.29
8 Alkoholfreie Getränke Splitkiste Coca Cola 1l 6er leer 2.00 3.74
9 Alkoholfreie Getränke Splitkiste Coca Cola 1l 12er leer 4.00 7.48
10 Alkoholfreie Getränke Kiste Coca Cola 1l 12er leer 3.00 6.48
11 Alkoholfreie Getränke Normflasche 1l 0.29
12 Alkoholfreie Getränke Normkiste 1l 6er leer 3.00 4.74
13 Alkoholfreie Getränke Exklusivmarken-Flasche 1l 0.29
14 Alkoholfreie Getränke Exklusivmarken-Kiste 1l 6er leer 3.00 4.74
15 Molkerei Milchflasche 1l 0.22
16 Molkerei Milchkiste 1l 6er leer 5.50 6.82
17 Molkerei Joghurtglas 0.17
18 Einweg Kunststoffflaschen 0.25
19 Einweg Metalldosen 0.25

View File

@@ -0,0 +1,27 @@
"Bierflasche Longneck 0,33 Liter","0,36"
"Bierflasche 0,5 Liter oder 0,33 Liter","0,20"
"Bierflasche Bügelflasche 0,5 Liter","0,36"
"Bierflasche Bügelflasche 0,33 Liter","0,36"
"Bierkiste 20 x 0,5 Liter","7,-"
"Bierkiste 20 x 0,33 Liter","7,-"
"Bierkiste 24 x 0,33 Liter","7,80"
"Bierkiste 12 x 0,33 Liter","5,40"
Kiste Gasteiner 6 x 1 Liter,"4,74"
Kiste 12 x 1 Liter (AF-Getränke),"6,48"
Kiste Vöslauer 9 x 1 Liter (PET-Flasche),"5,61"
Kiste Vöslauer 8 x 1 Liter,"6,32"
Kiste Vöslauer 4 x 1 Liter,"3,16"
Getränkeflasche 1 Liter (AF),"0,29"
Römerquelle Splitbox 6er,"3,74"
Römerquelle Splitbox 12er,"7,48"
"Kiste leer (20 x 0,5 Liter, 6 x 1 Liter, 12 x 1 Liter, 12 x 0,33 Liter, 24 x 0,33 Liter)","3,-"
Kiste Bügelflasche leer 6 x 2 Liter,"3,-"
Kiste Bügelflasche 6 x 2 Liter,"8,10"
"Kiste Bügelflasche leer 20 x 0,5 Liter","3,-"
"Kiste Bügelflasche 20 x 0,5 Liter","10,20"
Bügelflasche 2 Liter,"0,85"
Landliebe Joghurtglas 500 g,"0,17"
"Fruchtsaftflaschen/AF-Getränke: 0,2 Liter, 0,25 Liter oder 0,33 Liter","0,14"
Bierfass 25 Liter oder 50 Liter,"36,-"
Milch-Glasflasche 1 Liter,"0,22"
Original Mostflasche,"0,55"
1 Bierflasche Longneck 0,33 Liter 0,36
2 Bierflasche 0,5 Liter oder 0,33 Liter 0,20
3 Bierflasche Bügelflasche 0,5 Liter 0,36
4 Bierflasche Bügelflasche 0,33 Liter 0,36
5 Bierkiste 20 x 0,5 Liter 7,-
6 Bierkiste 20 x 0,33 Liter 7,-
7 Bierkiste 24 x 0,33 Liter 7,80
8 Bierkiste 12 x 0,33 Liter 5,40
9 Kiste Gasteiner 6 x 1 Liter 4,74
10 Kiste 12 x 1 Liter (AF-Getränke) 6,48
11 Kiste Vöslauer 9 x 1 Liter (PET-Flasche) 5,61
12 Kiste Vöslauer 8 x 1 Liter 6,32
13 Kiste Vöslauer 4 x 1 Liter 3,16
14 Getränkeflasche 1 Liter (AF) 0,29
15 Römerquelle Splitbox 6er 3,74
16 Römerquelle Splitbox 12er 7,48
17 Kiste leer (20 x 0,5 Liter, 6 x 1 Liter, 12 x 1 Liter, 12 x 0,33 Liter, 24 x 0,33 Liter) 3,-
18 Kiste Bügelflasche leer 6 x 2 Liter 3,-
19 Kiste Bügelflasche 6 x 2 Liter 8,10
20 Kiste Bügelflasche leer 20 x 0,5 Liter 3,-
21 Kiste Bügelflasche 20 x 0,5 Liter 10,20
22 Bügelflasche 2 Liter 0,85
23 Landliebe Joghurtglas 500 g 0,17
24 Fruchtsaftflaschen/AF-Getränke: 0,2 Liter, 0,25 Liter oder 0,33 Liter 0,14
25 Bierfass 25 Liter oder 50 Liter 36,-
26 Milch-Glasflasche 1 Liter 0,22
27 Original Mostflasche 0,55

View File

@@ -0,0 +1,133 @@
import tkinter as tk
from tkinter import ttk
import csv
import os
import re
def select_file(callback=None):
def set_choice(choice):
select_window.destroy()
if choice == "Wiki":
open_wiki()
else:
filename = os.path.join("wiki", "listeSPAR.csv" if choice == "SPAR" else "listeHOFER.csv")
if callback:
callback(filename)
else:
start_app(filename)
select_window = tk.Tk()
select_window.title("Wähle eine Liste")
select_window.geometry("300x200")
label = tk.Label(select_window, text="Bitte eine Liste wählen:", font=("Arial", 12))
label.pack(pady=10)
spar_button = tk.Button(select_window, text="SPAR", command=lambda: set_choice("SPAR"), width=15)
spar_button.pack(pady=5)
hofer_button = tk.Button(select_window, text="HOFER", command=lambda: set_choice("HOFER"), width=15)
hofer_button.pack(pady=5)
wiki_button = tk.Button(select_window, text="Wiki", command=lambda: set_choice("Wiki"), width=15)
wiki_button.pack(pady=5)
select_window.mainloop()
class CSVViewerApp:
def __init__(self, root, filename):
self.root = root
title = "PFANDLISTE - SPAR" if "SPAR" in filename else "PFANDLISTE - HOFER"
self.root.title(title)
self.root.geometry("600x400")
self.label = tk.Label(root, text=title, font=("Arial", 16, "bold"))
self.label.pack(pady=10)
self.frame = tk.Frame(root)
self.frame.pack(fill=tk.BOTH, expand=True)
self.tree = ttk.Treeview(self.frame)
self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.scrollbar = ttk.Scrollbar(self.frame, orient="vertical", command=self.tree.yview)
self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.tree.configure(yscroll=self.scrollbar.set)
self.load_csv(filename)
def load_csv(self, filename):
try:
with open(filename, newline='', encoding='utf-8') as file:
reader = csv.reader(file)
headers = next(reader, None)
self.tree['columns'] = headers
self.tree.heading("#0", text="#") # First column for index
self.tree.column("#0", width=50)
for header in headers:
self.tree.heading(header, text=header)
self.tree.column(header, anchor="center")
for i, row in enumerate(reader, start=1):
self.tree.insert("", "end", text=i, values=row)
except FileNotFoundError:
print(f"Error: {filename} not found!")
except Exception as e:
print(f"Error loading CSV: {e}")
## Doesnt really work yet
## In the Future maybe
def format_markdown(text_area, text):
text_area.tag_configure("bold", font=("Arial", 10, "bold"))
text_area.tag_configure("center", justify="center")
text_area.delete("1.0", tk.END)
segments = []
last_end = 0
for match in re.finditer(r'<p align="center">(.*?)</p>|\*\*(.*?)\*\*', text, re.DOTALL):
segments.append((text[last_end:match.start()], None))
if match.group(1): # Centered text
segments.append((match.group(1), "center"))
elif match.group(2): # Bold text
segments.append((match.group(2), "bold"))
last_end = match.end()
segments.append((text[last_end:], None))
for segment, tag in segments:
text_area.insert(tk.END, segment, tag if tag else "")
def open_wiki():
wiki_window = tk.Tk()
wiki_window.title("Wiki")
wiki_window.geometry("500x400")
text_area = tk.Text(wiki_window, wrap=tk.WORD)
text_area.pack(expand=True, fill=tk.BOTH)
filename = os.path.join("wiki", "wiki.md")
try:
with open(filename, "r", encoding="utf-8") as file:
content = file.read()
format_markdown(text_area, content)
except FileNotFoundError:
text_area.insert(tk.END, f"Fehler: '{filename}' nicht gefunden!")
wiki_window.mainloop()
def start_app(filename):
root = tk.Tk()
app = CSVViewerApp(root, filename)
root.mainloop()
if __name__ == "__main__":
select_file()

View File

@@ -0,0 +1,3 @@
big thank you to spar and hofer for the lists!
großes danke and spar und hofer für die listen!

View File

@@ -0,0 +1,27 @@
**Pfand in Österreich**
Pfand bezeichnet in Österreich ein System zur Rückgabe und Wiederverwertung von Einweg- und Mehrwegverpackungen, insbesondere bei Getränkebehältern. Es dient der Reduktion von Verpackungsmüll und der Förderung nachhaltiger Kreislaufwirtschaft.
**Mehrwegpfand**
Mehrwegflaschen aus Glas oder Kunststoff werden in Österreich seit Jahrzehnten genutzt. Diese Flaschen können mehrfach wiederbefüllt werden und sind durch ein Pfandsystem in den Handel integriert. Verbraucher zahlen beim Kauf eines Produkts mit Mehrwegverpackung ein Pfand, das sie bei der Rückgabe zurückerhalten.
**Einwegpfand**
Seit dem 1. Januar 2025 gibt es in Österreich ein Pfandsystem für Einweggetränkeflaschen und -dosen. PET-Flaschen und Aluminiumdosen mit einem Volumen zwischen 0,1 und 3 Litern sind pfandpflichtig. Das Ziel dieses Systems ist es, die Recyclingquote zu erhöhen und die Umweltbelastung durch achtlos weggeworfene Verpackungen (Littering) zu verringern.
**Pfandhöhe und Rückgabe**
Die Pfandhöhe beträgt je nach Verpackungsgröße und Material zwischen 25 und 30 Cent. Die Rückgabe erfolgt in Supermärkten und anderen Verkaufsstellen, die pfandpflichtige Getränke anbieten. Dort stehen Rücknahmeautomaten zur Verfügung, die das Pfand auszahlen oder als Einkaufsgutschrift verrechnen.
**Umweltauswirkungen und Vorteile**
Das Pfandsystem trägt wesentlich zur Reduktion von Plastik- und Metallabfällen bei und steigert die Recyclingquote. Es sorgt für eine effizientere Nutzung von Ressourcen und verringert die Umweltverschmutzung. Gleichzeitig wird durch das System ein wirtschaftlicher Anreiz geschaffen, leere Verpackungen nicht achtlos wegzuwerfen.
**Gesetzliche Grundlagen**
Die Einführung des Einwegpfands basiert auf dem Abfallwirtschaftsgesetz (AWG) und entsprechenden Verordnungen der österreichischen Bundesregierung. Es orientiert sich an erfolgreichen Pfandsystemen anderer europäischer Länder wie Deutschland oder Schweden.
Mit dem neuen Pfandsystem macht Österreich einen wichtigen Schritt in Richtung nachhaltiger Ressourcennutzung und Kreislaufwirtschaft.