initial commit
this is a copy ;3
0
PfandApplication/__init__.py
Normal file
BIN
PfandApplication/images/auszeichnung.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
PfandApplication/images/bierflasche.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
PfandApplication/images/dose.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
PfandApplication/images/flaschen.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
PfandApplication/images/joghurt glas.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
PfandApplication/images/kasten.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
PfandApplication/images/monster.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
PfandApplication/images/plastikflasche.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
1542
PfandApplication/main.py
Normal file
218
PfandApplication/pfand_scanner.py
Normal 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
@@ -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()
|
||||
BIN
PfandApplication/wiki/__pycache__/main.cpython-313.pyc
Normal file
19
PfandApplication/wiki/listeHOFER.csv
Normal 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,
|
||||
|
27
PfandApplication/wiki/listeSPAR.csv
Normal 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"
|
||||
|
133
PfandApplication/wiki/main.py
Normal 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()
|
||||
|
||||
3
PfandApplication/wiki/readme.md
Normal 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!
|
||||
27
PfandApplication/wiki/wiki.md
Normal 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.
|
||||
|
||||
|
||||