From ea80ed32fabb0ba2b2844d395c598fb67936f7dd Mon Sep 17 00:00:00 2001 From: Rattatwinko <89251490+ZockerKatze@users.noreply.github.com> Date: Tue, 15 Apr 2025 18:41:08 +0200 Subject: [PATCH] initial commit this is a copy ;3 --- LICENSE | 21 + PfandApplication/__init__.py | 0 PfandApplication/images/auszeichnung.png | Bin 0 -> 6111 bytes PfandApplication/images/bierflasche.png | Bin 0 -> 16482 bytes PfandApplication/images/dose.png | Bin 0 -> 6448 bytes PfandApplication/images/flaschen.png | Bin 0 -> 4072 bytes PfandApplication/images/joghurt glas.png | Bin 0 -> 4113 bytes PfandApplication/images/kasten.png | Bin 0 -> 11491 bytes PfandApplication/images/monster.png | Bin 0 -> 22766 bytes PfandApplication/images/plastikflasche.png | Bin 0 -> 8808 bytes PfandApplication/main.py | 1542 +++++++++++++++++ PfandApplication/pfand_scanner.py | 218 +++ PfandApplication/updater.py | 255 +++ .../wiki/__pycache__/main.cpython-313.pyc | Bin 0 -> 8298 bytes PfandApplication/wiki/listeHOFER.csv | 19 + PfandApplication/wiki/listeSPAR.csv | 27 + PfandApplication/wiki/main.py | 133 ++ PfandApplication/wiki/readme.md | 3 + PfandApplication/wiki/wiki.md | 27 + README.md | 74 + iex.png | Bin 0 -> 42113 bytes requirements.txt | 7 + run.py | 4 + 23 files changed, 2330 insertions(+) create mode 100644 LICENSE create mode 100644 PfandApplication/__init__.py create mode 100644 PfandApplication/images/auszeichnung.png create mode 100644 PfandApplication/images/bierflasche.png create mode 100644 PfandApplication/images/dose.png create mode 100644 PfandApplication/images/flaschen.png create mode 100644 PfandApplication/images/joghurt glas.png create mode 100644 PfandApplication/images/kasten.png create mode 100644 PfandApplication/images/monster.png create mode 100644 PfandApplication/images/plastikflasche.png create mode 100644 PfandApplication/main.py create mode 100644 PfandApplication/pfand_scanner.py create mode 100644 PfandApplication/updater.py create mode 100644 PfandApplication/wiki/__pycache__/main.cpython-313.pyc create mode 100644 PfandApplication/wiki/listeHOFER.csv create mode 100644 PfandApplication/wiki/listeSPAR.csv create mode 100644 PfandApplication/wiki/main.py create mode 100644 PfandApplication/wiki/readme.md create mode 100644 PfandApplication/wiki/wiki.md create mode 100644 README.md create mode 100644 iex.png create mode 100644 requirements.txt create mode 100644 run.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7da123e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Österreichischer Pfandrechner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/PfandApplication/__init__.py b/PfandApplication/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/PfandApplication/images/auszeichnung.png b/PfandApplication/images/auszeichnung.png new file mode 100644 index 0000000000000000000000000000000000000000..0f70dfea59a1a968f07766c989f512fb8db19d96 GIT binary patch literal 6111 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 FzX2UPZAJh9 literal 0 HcmV?d00001 diff --git a/PfandApplication/images/dose.png b/PfandApplication/images/dose.png new file mode 100644 index 0000000000000000000000000000000000000000..ff773ad8e12743d3b57d70702a1e9b54ee1ba752 GIT binary patch literal 6448 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 Vl$RmB++QHT!N%oer4{n_e*mXSaPj~E literal 0 HcmV?d00001 diff --git a/PfandApplication/images/flaschen.png b/PfandApplication/images/flaschen.png new file mode 100644 index 0000000000000000000000000000000000000000..8cb8db657b8be88ae4258e221a92d2b9ae308912 GIT binary patch literal 4072 zcmeHKc~Dd57C$#E2_Pg=iwhE{Le&8jr68aIw74LJig*!_B~~FtWEBLXa4+Hl1;M2T zC@S>f8bBgpQMN?c^btXefGi4%$`T<^!Wv25^^f=7pPf4MW;)aN$2Z^o&YXL`dzO2C zzmv4nWt*n@5_JI3blmQ+3xGsCl7OO0Y|@zLv&5zrw%zkEfL86Ngn@KXz09O_$u}UZBAU&PYj(3RBGYe;{d$Es1#oc->)YIgXp}FyN4KNN?)djB^e`9T zsjVm}NRT_PHBg3}NmfHHRpb=9ru^euU~aD9+T@Lq`&_#^&SWFt;AlIIEk%IVS6r`u zyuIj(rQ9T5saux;l;S+@R4)1MI_ws=@mT_HDEK6l9+HClqh@8D09D{bTd=`6(iKhH zN+n&$Kyd&2WcfIqvw_5PC4onBLoOK{mBB>(cmT=I$ksI34q$J90K|`^(IJdg$Q^n) zu&MjaCNy>fnQmUjtW9tT4VGFhB*@fgsY-vRh`ICDHuF$C7k%VdAY~A#lY0`O{K%N z`2e*4xKyc0dmpiu4P`M_Y!JSrKt@9&mut z{8R`o|E@k|0A=m}=B}~xEM2&Rv4{cNmjlx?O?Q$ZvHWRb<{**vvjFUJt1!VCpKVC@ z4iFYyO{ELZR|UUle>dOb9N_-3?L0|VNUqh@Xib@|*iyaModlf2o%a-Le71&rn~E1y z4_c~&N0JZfV{wRY0vm6aN?QEEBXKc!)Z6J!W$&96aAP@d{Cu_|U_APd&ZeIV4`Km~ z`?wp!Vl0k%MC z6{lbt8huUNo~ckvz)_omzNOQwjWR(Sd%ZAp&g3LOjUJ&h$yWq1SV$>t3w#%nq@+Nf zU~{rXeAo2S7-1ULWILz2{*6*?We(OI^mu0P}Hz zB01vg*}L=k$jAc7;c~2}H}nZQ5;v?qxyYWkU;pFF)JcDi7BU}b0~v13S|5Wsm0x_T z49eDRNd0ci&*>CS0ZLeYYrodAhP!$Gn}rf8vnDlsG`dZYVl=XdhJbEl%FU-nwvSlF z7i~t~IIku1)>bvsub8GrN_yF3xwC$dh?ck?0>d_b6sFjfX;Np$_$JF?IOr%mt``|_NO*P~;; z84#~R*=$bF4f^-cvu{sGRPhIw zABQKlWiA&(#xGlNQOF*&$MS>1tuWr~N+Y-Y#}^96!gwKtgm*APx)lwpbD8d@Vmo@2 zuF5CIzsK@IuqHUPvmraRVE5z

)IDDvC z*8d0pJX7PKP2D|-PGD2zn!qv8HiRDP9|Vz zYnooQszbRQ8|+hjS##}I(+4rD41)Hqs-m_v$$yOp;5-*|7%T-mxTDqGv~?bb+v%rj zC7n7_rPucaV`=uqugXM#6X_}APw8M{;uBT?e!axI0`v9#sN_QdW?*^=hBv3PJyE+z zx<;)w2q`_-YlQ+u=jIkE#qTEJ4w9`@ob3~hot5NqXM;Q^s6J}I{JElAd%B1#_tuib z@!DW~ykc-Fvb@)-DRgn;g)+m3%S2$tuWl}TiAHQ4&oB{r8BX! z%p9ZUS-SwsBr9LlTG6YveQP*_6YG|A`n1Vy6i0`%sb+7y6nFOdY3#`405iZ;zk0>H z_;o>Jk)r}dcujS{#SD%Gi1OdnOgWahnpJS%XiM+{=CXX|9FH7ao5igQ9)62Hj+CltW(R!v35gHVT z;rO}!L4uXY^a)J$Riz=;UaS<+QY1f-8WmEM7l$ox&nj6C#?QnFbCn|1Lv@bBe%9UN zLJW;Qc6Kdj491?0W-K>QZ42Ea>uGTu##oHiYSuE=_y|#VkwwHrx9#KX!V+v|=cR?h zl*t_V)QD`HOcCEJ31jD=Aj(tv~F7*2##md*${mg2@y?3`} zbqcDd(mU-8)u-)2sOUml`Ba6BkREkekl$Q)2CFWZ(_PXWgb53YLGyrv*?9$tEV>n7 TJ`bvh(H$JOxH#O|%slZgO>;i# literal 0 HcmV?d00001 diff --git a/PfandApplication/images/joghurt glas.png b/PfandApplication/images/joghurt glas.png new file mode 100644 index 0000000000000000000000000000000000000000..6bc01d075261c071d41d68898fca00445e2e3a93 GIT binary patch literal 4113 zcmeHKX;@QN8a|022qI`jEvx8dv1~zYG0LW3;mQ(hs~`$QQBYZe5e$3czM!ODEQ<=L zDCkrKku@xd)`90%G?hS`L$BU&$e7(H@rhe}=m1YB=-vWE>{2}S?XHoR{ zQATL%1fSc`v(Rpzo6{Y0AjJN7?JnWcuMDfYeNXN-`pxHM&&Jp5JRhv=xH7THD0aic zOPT90RkhokIC=gjuMp|_Djj=jw9~DUYNx7(Gqv_j9JlqS7qK%@?-XzIWs;x# zSTB|-T=K{ZeOsa;SK%4`sUPZLVTeAfw9K;WeTv5N@jwKNbtSGMco>&L^$<$2Bs-&B z6S?lyy+z_IDBO=8fs{iBXq+o_4G?j_m!6E&TGekwg*B(CPtt7BR79DGsBY8(ySBf> z&9|lD+sw?&RBWyG-IF;y-XQ|jX9seqK>VxFlX}E0SNx$p^h?%GZE$I_5;FItM@lS6 zd-Y2*N7=PzuTIkQuUaUNBFq4K2;P3U6-tia)N01XHeNJ@KycNRK=p~+G-D7<@U~Rc zLs+&nn^-~WBT{d^^PBtx@nc;z_P5=KXg>6GiN(_u1l|La0%^;msSy3MhBL>JAV`sd zw_hp_(Wf43+_*S-*}}9)+zqwX;GyI7L1u+5+Hctr4pZZuRzF()*x9`0v9jzL?Qb**ugso&MJZ&4Y$75X2jjaN0 z!;i|eJWiFzxU$r>h#(0eu$Nq>MG7Udl3hw?fQ|H)a7ms6f#;HS!WCTGaeoGs_yQ7p ziP|jeyonQzNTYG2=R*n*Sc1G(Q#Hbez7 zw#@rqQPLmcQT{OkQEJRys!bC85mlMD$IcH`X1uD{6Rs>Y)kVZ6HgRSQY{U(4-)->| zp=X|hdZyed40k}v`WbN%rf3L8bU@}G!AKJv){~;WI-ZGy`%ijFEYwr8chO$tn608p zEGmZjRxz*_Ai7Me;=2HUdNd+9h%Xo()ES?(lLo-9`%QM56?(PoGVfZwE2=s1#CXkX zf0kW0*E%^A@m8-cor1g>a%8UY0i+8Qt9lpFjkCP8=6vzj6T=7X@&MJQXb(LD*@63A z*5HIvAVmPa5zGj@AEkRLt(gDBIG%SJRp-Wywi$fQm>IQV`c$pu|N zlpa*7n)uQ%R!r6fK(D>sfdYmmlgUjvIX;xGpR1EmgKTg zzL`Hg0HLvKmQua2TZ_I$<+M88mdU6wrrx3c__aLhEJVK5W!=nn?$wOgZY8pgnBOoE z)$PAzT}yw|>@wLCC`yeqKq|r?W0xAhnz$h3e17_?h7yt8@e$6nH}9ezgJ9ROW6OdU z!v?L_W9+nb+#|oqJy4!`$5=`X&Z|xc*WN1elCyCrTIP@KW98x=D-6o_V6gPsrG|Ue zTiTh110uDX*ZcC*&n?6u#m3Te$Nq56SIsBcpwB-?653=x<47vy&E5*g!3Q&}bpEA>9NuegMCd-FT!o*RVJG8(CpFKWuMVGrF$AzG=_1;P4Q^28Z~)=0b}G0 z8e-jILi{2=Dw69U9J==9l9gcju~hyjQQK}nx>a2`EWsU) z{hk^--IPOhzy(SN7lFVZ0brfe0AMu)z`gYZ05q2YVB-UT$IidH{53CsZHE7sLqfVW zQ#c&`Uq_sE-{g04bGn)ytfx(wM>`B}S%7uFHWvByr4Z1cD}-JJK&;T4gyl26{o(jyn^ ziM+sBtNQX${uwq|!)oC2=7qF08&tHBc3(}LaWZno%g44c zR_;~lKfO*lHhWc?3covEryJ6hExofC+2oMH9duw_NLdfad z-KE^aV_MfYc6#Iu)#V%1HzK9OKJz)SB-~C>qQ-ph39P;D~;P<6!w(EEZl- zW1`_{0DgF}B~-i728G9@OWl?CvMvi$eXq|Z^gEuSab#0m$rz^hr@nsvkIC^`7WFG~ V$}WYSI-RCAfp-UZJ=;Y+_fMmi=EML1 literal 0 HcmV?d00001 diff --git a/PfandApplication/images/kasten.png b/PfandApplication/images/kasten.png new file mode 100644 index 0000000000000000000000000000000000000000..61a0a5a3ec7e3e1aea563f752a5cb37b596368b4 GIT binary patch literal 11491 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 O0|-+alWHUPTmKK}R24b^ literal 0 HcmV?d00001 diff --git a/PfandApplication/images/monster.png b/PfandApplication/images/monster.png new file mode 100644 index 0000000000000000000000000000000000000000..beead2b3a88cafa5bb505a562423858fc99048b7 GIT binary patch literal 22766 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 literal 0 HcmV?d00001 diff --git a/PfandApplication/main.py b/PfandApplication/main.py new file mode 100644 index 0000000..06c913e --- /dev/null +++ b/PfandApplication/main.py @@ -0,0 +1,1542 @@ +import tkinter as tk +import tkinter +import webbrowser +from tkinter import ttk, messagebox, filedialog +import json + + +from PfandApplication.wiki import main as WIKI +from PfandApplication.pfand_scanner import launch_pfand_scanner +from PfandApplication.updater import open_updater as open_updater, run_silent_update + +from PIL import Image, ImageTk +import os +import subprocess +from datetime import datetime +from tkcalendar import DateEntry +import csv +import cv2 +from pyzbar.pyzbar import decode +import threading +import queue +import numpy as np +import shutil + +class Achievement: + def __init__(self, title, description, condition_type, condition_value): + self.title = title + self.description = description + self.condition_type = condition_type + self.condition_value = condition_value + self.unlocked = False + self.unlock_date = None + +class PfandCalculator: + def __init__(self, root): + self.root = root + self.root.title("Österreichischer Pfandrechner") + + # Load products and prices from JSON + self.load_products() + + self.quantities = {} + self.images = {} + self.spinboxes = {} # Store spinbox references + self.deposit_history = self.load_deposit_history() + self.scanned_barcodes = set() + self.barcode_history = [] # Store barcode scan history + + self.achievements = self.initialize_achievements() + self.load_achievements() + + if not os.path.exists('PfandApplication/images'): + os.makedirs('images') + + self.create_menu() + self.load_quantities() + self.create_widgets() + + # Scanner window + self.scanner_window = None + self.cap = None + self.scanning = False + + self.achievement_image = self.load_achievement_image() + + def load_image(self, product_name): + try: + # Use Flaschen icon for Bierflasche + if product_name == "Bierflasche": + product_name = "Flaschen" + + image_path = f"PfandApplication/images/{product_name.lower()}.png" + if os.path.exists(image_path): + try: + image = Image.open(image_path) + image = image.resize((100, 100), Image.Resampling.LANCZOS) + return ImageTk.PhotoImage(image) + except Exception as e: + print(f"Error processing image {image_path}: {e}") + return None + else: + print(f"Image not found: {image_path}") + return None + except Exception as e: + print(f"Error loading image for {product_name}: {e}") + return None + + def load_achievement_image(self): + try: + image_path = "PfandApplication/images/auszeichnung.png" + if os.path.exists(image_path): + try: + image = Image.open(image_path) + image = image.resize((50, 50), Image.Resampling.LANCZOS) + # Store both normal and gray versions + self.achievement_image = ImageTk.PhotoImage(image) + # Create grayscale version while preserving transparency + gray_image = Image.new('RGBA', image.size) + for x in range(image.width): + for y in range(image.height): + r, g, b, a = image.getpixel((x, y)) + # Convert to grayscale while preserving alpha + gray = int(0.299 * r + 0.587 * g + 0.114 * b) + # Make it lighter + gray = min(255, gray + 100) + gray_image.putpixel((x, y), (gray, gray, gray, a)) + self.achievement_image_gray = ImageTk.PhotoImage(gray_image) + return self.achievement_image + except Exception as e: + print(f"Error processing achievement image: {e}") + return None + print(f"Achievement image not found: {image_path}") + return None + except Exception as e: + print(f"Error loading achievement image: {e}") + return None + + def initialize_achievements(self): + return { + "each_100": Achievement("Krass, Weiter So!", "Du hast bis jetzt 100 von jedem Element gesammelt!", "each_element", 100), + "each_500": Achievement("Adlersson wäre neidisch!", "Adlersson wäre neidisch auf dich! Du hast 500 von jedem Element gesammelt!", "each_element", 500), + "each_1000": Achievement("Arbeitslos I", "Arbeitsamt hat angerufen! Du hast 1000 von jedem Element gesammelt!", "each_element", 1000), + "total_2000": Achievement("Arbeitslos II", "Das Arbeitsamt hat angst vor dir! Du hast 2000 totale Elemente gesammelt!", "total_elements", 2000), + "total_3000": Achievement("Arbeitslos III", "Drachenlord hat angst vor dir! Du hast mehr wie 3000 Elemente gesammelt!", "total_elements", 3000), + "total_over_3000": Achievement("Krankhafte Sucht!", "Du hast echt einen Vogel! Pfandangel #1! Du hast >3000 gesammelt!", "total_elements", 3001), + "first_deposit": Achievement("Depositer!", "Guter Anfang!", "deposits", 1), + "deposits_10": Achievement("Depositer I", "Cool, Weiter So!", "deposits", 10), + "deposits_50": Achievement("Depositer II", "WoW, Echt viele Abgaben!", "deposits", 50), + "deposits_100": Achievement("Depositer III", "Du bist der Meister der Abgaben!", "deposits", 100), + "deposits_150": Achievement("Meister Depositer", "Der Pfandautomat hat Angst vor dir, so viel wie du Abgegeben hast müsstest du eine Villa besitzen!", "deposits", 150), + # New scanner achievements + "first_scan": Achievement("Scanner Neuling", "Du hast deinen ersten Barcode gescannt!", "scans", 1), + "scans_50": Achievement("Scanner Pro", "50 Barcodes gescannt - du kennst dich aus!", "scans", 50), + "scans_100": Achievement("Scanner Meister", "100 Barcodes gescannt - der Profi ist da!", "scans", 100), + "scans_500": Achievement("Scanner Legende", "500 Barcodes gescannt - legendärer Scanner Status erreicht!", "scans", 500), + "daily_10": Achievement("Tages Champion", "10 Barcodes an einem Tag gescannt!", "daily_scans", 10), + "daily_25": Achievement("Tages Meister", "25 Barcodes an einem Tag gescannt - sehr fleißig!", "daily_scans", 25) + } + + def load_achievements(self): + try: + with open('achievements.json', 'r') as f: + data = json.load(f) + for key, achievement_data in data.items(): + if key in self.achievements: + self.achievements[key].unlocked = achievement_data['unlocked'] + self.achievements[key].unlock_date = achievement_data['unlock_date'] + except FileNotFoundError: + pass + + def save_achievements(self): + data = { + key: { + 'unlocked': achievement.unlocked, + 'unlock_date': achievement.unlock_date + } + for key, achievement in self.achievements.items() + } + with open('achievements.json', 'w') as f: + json.dump(data, f) + + def check_achievements(self): + total_elements = sum(self.deposit_history[-1]['quantities'].values()) if self.deposit_history else 0 + all_time_total = sum(sum(d['quantities'].values()) for d in self.deposit_history) + deposits_count = len(self.deposit_history) + + for achievement in ["each_100", "each_500", "each_1000"]: + if not self.achievements[achievement].unlocked: + if all(self.deposit_history[-1]['quantities'][product] >= self.achievements[achievement].condition_value + for product in self.products): + self.unlock_achievement(achievement) + + for achievement in ["total_2000", "total_3000", "total_over_3000"]: + if not self.achievements[achievement].unlocked and all_time_total >= self.achievements[achievement].condition_value: + self.unlock_achievement(achievement) + + deposit_achievements = { + 1: "first_deposit", + 10: "deposits_10", + 50: "deposits_50", + 100: "deposits_100", + 150: "deposits_150" + } + + for count, achievement_key in deposit_achievements.items(): + if not self.achievements[achievement_key].unlocked and deposits_count >= count: + self.unlock_achievement(achievement_key) + + def unlock_achievement(self, achievement_key): + achievement = self.achievements[achievement_key] + if not achievement.unlocked: + achievement.unlocked = True + achievement.unlock_date = datetime.now().strftime("%d.%m.%Y") + self.save_achievements() + messagebox.showinfo("Auszeichnung freigeschaltet!", + f"Neue Auszeichnung: {achievement.title}\n\n{achievement.description}") + + def show_achievements(self): + achievements_window = tk.Toplevel(self.root) + achievements_window.title("Auszeichnungen") + achievements_window.geometry("800x600") + + notebook = ttk.Notebook(achievements_window) + notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + all_frame = ttk.Frame(notebook) + notebook.add(all_frame, text="Alle Auszeichnungen") + + unlocked_frame = ttk.Frame(notebook) + notebook.add(unlocked_frame, text="Freigeschaltete") + + self._create_achievements_view(all_frame, show_all=True) + self._create_achievements_view(unlocked_frame, show_all=False) + + def _create_achievements_view(self, parent_frame, show_all=True): + canvas = tk.Canvas(parent_frame) + scrollbar = ttk.Scrollbar(parent_frame, orient="vertical", command=canvas.yview) + scrollable_frame = ttk.Frame(canvas) + + scrollable_frame.bind( + "", + lambda e: canvas.configure(scrollregion=canvas.bbox("all")) + ) + + canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") + canvas.configure(yscrollcommand=scrollbar.set) + + def _on_mousewheel(event): + canvas.yview_scroll(int(-1 * (event.delta / 120)), "units") + + def _bind_mousewheel(widget): + widget.bind('', _on_mousewheel) + for child in widget.winfo_children(): + _bind_mousewheel(child) + + canvas.bind('', _on_mousewheel) + _bind_mousewheel(scrollable_frame) + + sammeln_achievements = { + "each_100": self.achievements["each_100"], + "each_500": self.achievements["each_500"], + "each_1000": self.achievements["each_1000"], + "total_2000": self.achievements["total_2000"], + "total_3000": self.achievements["total_3000"], + "total_over_3000": self.achievements["total_over_3000"] + } + + abgeben_achievements = { + "first_deposit": self.achievements["first_deposit"], + "deposits_10": self.achievements["deposits_10"], + "deposits_50": self.achievements["deposits_50"], + "deposits_100": self.achievements["deposits_100"], + "deposits_150": self.achievements["deposits_150"] + } + + scanner_achievements = { + "first_scan": self.achievements["first_scan"], + "scans_50": self.achievements["scans_50"], + "scans_100": self.achievements["scans_100"], + "scans_500": self.achievements["scans_500"], + "daily_10": self.achievements["daily_10"], + "daily_25": self.achievements["daily_25"] + } + + row = 0 + + def add_group_header(title): + nonlocal row + header_frame = ttk.Frame(scrollable_frame) + header_frame.grid(row=row, column=0, sticky='ew', padx=5, pady=(15, 5)) + + header_label = ttk.Label(header_frame, text=title, + font=('TkDefaultFont', 12, 'bold')) + header_label.pack(anchor='w') + + separator = ttk.Separator(scrollable_frame, orient='horizontal') + row += 1 + separator.grid(row=row, column=0, sticky='ew', pady=2) + row += 1 + + def add_achievement(key, achievement): + nonlocal row + if not show_all and not achievement.unlocked: + return + + frame = ttk.Frame(scrollable_frame) + frame.grid(row=row, column=0, sticky='ew', padx=5, pady=5) + + if self.achievement_image: + if achievement.unlocked: + image_label = ttk.Label(frame, image=self.achievement_image) + else: + image_label = ttk.Label(frame, image=self.achievement_image_gray) + image_label.grid(row=0, column=0, rowspan=2, padx=(5, 10), pady=5) + + content_frame = ttk.Frame(frame) + content_frame.grid(row=0, column=1, sticky='nsew', pady=5) + + title_label = ttk.Label(content_frame, text=achievement.title, + font=('TkDefaultFont', 10, 'bold')) + title_label.grid(row=0, column=0, sticky='w') + + if achievement.unlocked: + date_label = ttk.Label(content_frame, + text=f"Freigeschaltet am: {achievement.unlock_date}", + font=('TkDefaultFont', 8)) + date_label.grid(row=0, column=1, padx=(20, 0)) + + desc_label = ttk.Label(content_frame, text=achievement.description, + wraplength=600) + desc_label.grid(row=1, column=0, columnspan=2, sticky='w', pady=(2, 0)) + + content_frame.grid_columnconfigure(0, weight=1) + content_frame.grid_columnconfigure(1, weight=0) + + separator = ttk.Separator(scrollable_frame, orient='horizontal') + row += 1 + separator.grid(row=row, column=0, sticky='ew', pady=5) + row += 1 + + _bind_mousewheel(frame) + + add_group_header("Sammeln") + for key, achievement in sammeln_achievements.items(): + add_achievement(key, achievement) + + add_group_header("Abgeben") + for key, achievement in abgeben_achievements.items(): + add_achievement(key, achievement) + + add_group_header("Scanner") + for key, achievement in scanner_achievements.items(): + add_achievement(key, achievement) + + canvas.pack(side="left", fill="both", expand=True) + scrollbar.pack(side="right", fill="y") + + # Credit Section (Overall made nicer in Version 7.04.001) || Changed up a bit in V7.04.101 + + def create_credits(self): + about_window = tk.Toplevel(self.root) + about_window.title("Über Programm") + about_window.resizable(True, True) + + # Configure grid weights so widgets expand properly | This is some new Stuff! + about_window.grid_columnconfigure(0, weight=1) + about_window.grid_columnconfigure(1, weight=1) + about_window.grid_rowconfigure(0, weight=1) + + label = tk.Label(about_window, + text=( + "PfandApp V.7.04.301\n" + "Erstellt mit TKinter, CV2, Numpy, PyZbar, TGTG-API, TKCalendar, Datetime\n" + "Eigene Module: Updater, TGTG_OC, Wiki, BuildUtil\n" + "Großen Dank an SPAR, HOFER\n" + "Daten werden in RootFS gespeichert\n" + "Danke für die Idee --> Österreich" + ), + padx=10, + pady=10, + justify="center", + anchor="center") + label.grid(row=0, column=0, columnspan=2, pady=10, sticky="nsew") + + url = "https://zockerkatze.github.io/ZockerKatze/" + + # Website button + website_button = tk.Button(about_window, text="WebSite", command=lambda: webbrowser.open(url)) + website_button.grid(row=1, column=0, padx=10, pady=10, sticky="ew") + + # Close button + close_button = tk.Button(about_window, text="Close", command=about_window.destroy) + close_button.grid(row=1, column=1, padx=10, pady=10, sticky="ew") + + def update_credits(self): # Credits for the Updater Application (not some update function for some credits) || Rewrote this in Version 7.04.101 => Inconsistency is key + about_update = tk.Toplevel(self.root) + about_update.title("Über UpdaterApp") + about_update.geometry("650x190") + + about_update.grid_columnconfigure(0, weight=1) # horizont + about_update.grid_rowconfigure(0, weight=1) # vertically + about_update.grid_rowconfigure(1, weight=0) # tight :3 (OwO) + + # Text content + label_update_app = tk.Label( + about_update, + text=( + "Updater App für PfandApp\n" + "Version 1.200.000\n" + "Diese Updater App nutzt das GitHub Repository, um die App zu updaten.\n" + "Nach Updates sollte die App neugestartet (oder reloaded, bei UI) werden.\n" + "Beim Starten der App wird nach Updates gesucht!" + ), + justify="left", anchor="center" + ) + label_update_app.grid(row=0, column=0, sticky='nsew', padx=10, pady=10) + + # Close button at the bottom (like u) :3 + close_button = tk.Button(about_update, text="Close", command=about_update.destroy) + close_button.grid(row=1, column=0, sticky='ew', padx=10, pady=(0, 10)) + + def µScan_credits(self): + about_µScan = tk.Toplevel(self.root) + about_μScan.title("Über µScan") + about_μScan.geometry("650x190") + + about_μScan.grid_columnconfigure(0, weight=1) + about_μScan.grid_rowconfigure(0, weight=1) + about_μScan.grid_rowconfigure(1, weight=0) + + label_µScan_app = tk.Label( + about_μScan, + text=( + "µScan - Der bessere Barcode Scanner\n" + "Version 1.1.0\n" + "µScan erfordert einen UI Reload (Strg+R) in der Root Anwendung\n" + "µScan ist für mehrere Barcodes gemacht, die in einer kurzen Zeit gescannt werden sollten\n" + ), + justify="left", anchor="center" + ) + label_μScan_app.grid(row=0, column=0, sticky="nsew", padx=10, pady=10) + + close_button = tk.Button(about_µScan, text="Close", command=about_μScan.destroy) + close_button.grid(row=1, column=0, sticky='ew', padx=10, pady=(0, 10)) + + def create_menu(self): + self.menubar = tk.Menu(self.root) + self.root.config(menu=self.menubar) + + # "Datei" Menu + + file_menu = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(label="Datei", menu=file_menu) + file_menu.add_command(label="Speichern", command=self.save_quantities, accelerator="Strg+S") + file_menu.add_command(label="Ordner öffnen", command=self.open_file_location, accelerator="Strg+O") + file_menu.add_command(label="Speicherdatei löschen", command=self.remove_save_file, accelerator="Strg+Shift+F1") + file_menu.add_separator() + file_menu.add_command(label="Neulanden der UI", command=self.recreate_widgets, accelerator="Strg+R") + file_menu.add_command(label="Updater", command=open_updater, accelerator="Strg+U") # Added this to the File Menu too! + file_menu.add_separator() + file_menu.add_command(label="Öffne PfandListe", command=WIKI.select_file, accelerator="Strg+L") + file_menu.add_command(label="Beenden", command=self.root.quit, accelerator="Strg+Q") + file_menu.add_separator() + file_menu.add_command(label="Über Programm", command=self.create_credits, accelerator="Strg+F10") + + # Deposit Menu + + deposit_menu = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(label="Pfand", menu=deposit_menu) + deposit_menu.add_command(label="Pfand Abgeben", command=self.quick_deposit, accelerator="Strg+D") + deposit_menu.add_command(label="Abgabe Historie", command=self.show_deposit_history, accelerator="Strg+H") + deposit_menu.add_separator() + deposit_menu.add_command(label="Historie Exportieren (CSV)", command=self.export_history_csv, accelerator="Strg+E") + deposit_menu.add_command(label="Historie Löschen", command=self.clear_deposit_history, accelerator="Strg+Shift+F2") + + # Scanner Menu + + scanner_menu = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(label="Scanner", menu=scanner_menu) + scanner_menu.add_command(label="Scanner öffnen", command=self.open_scanner_window, accelerator="Strg+B") + scanner_menu.add_separator() + scanner_menu.add_command(label="Öffne µScan", command=launch_pfand_scanner, accelerator="Strg+Shift+B") #µScan + scanner_menu.add_command(label="Über µScan", command=self.µScan_credits) #µScan credits + scanner_menu.add_separator() + scanner_menu.add_command(label="Barcodes Exportieren (CSV)", command=self.export_barcodes_csv, accelerator="Strg+Shift+E") + + # Achivements Menu + + achievements_menu = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(label="Auszeichnungen", menu=achievements_menu) + achievements_menu.add_command(label="Auszeichnungen anzeigen", command=self.show_achievements, accelerator="Strg+F6") + achievements_menu.add_command(label="Auszeichnungen löschen", command=self.delete_achievements, accelerator="Strg+F7") + + # Add custom products menu + + products_menu = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(label="Produkte", menu=products_menu) + products_menu.add_command(label="Produkt hinzufügen", command=self.show_add_product_window, accelerator="Strg+P") + products_menu.add_command(label="Produkte verwalten", command=self.show_manage_products_window, accelerator="Strg+Shift+P") + + + update_menu = tk.Menu(self.menubar, tearoff=0) + self.menubar.add_cascade(label="Updater", menu=update_menu) + update_menu.add_command(label="Öffne Updater", command=open_updater, accelerator="Strg+U") # Version (7.4.000 Updater Version) + update_menu.add_separator() + update_menu.add_command(label="Über Updater", command=self.update_credits) # Also no keybind here, same reason as the tgtg one #V7.4.001 + + # Manage Keybinds + + self.root.bind('', lambda e: self.save_quantities()) + self.root.bind('', lambda e: self.open_file_location()) + self.root.bind('', lambda e: self.root.quit()) + self.root.bind('', lambda e: self.quick_deposit()) + self.root.bind('', lambda e: self.show_deposit_history()) + self.root.bind('', lambda e: self.export_history_csv()) + self.root.bind('', lambda e: self.open_scanner_window()) + self.root.bind('', lambda e: launch_pfand_scanner()) #µScan + self.root.bind('', lambda e: self.handle_shift_f1(e)) + self.root.bind('', lambda e: self.handle_shift_f2(e)) + self.root.bind('', lambda e: self.show_achievements()) + self.root.bind('', lambda e: self.delete_achievements()) + self.root.bind('', lambda e: start_tgtg(self.root)) + self.root.bind('', lambda e: ask_for_tokens()) + self.root.bind('', lambda e: self.create_credits()) + self.root.bind('', lambda e: self.export_barcodes_csv() if e.state & 0x1 else self.export_history_csv()) + self.root.bind('', lambda e: self.show_add_product_window()) + self.root.bind('', lambda e: self.show_manage_products_window() if e.state & 0x1 else self.show_add_product_window()) + self.root.bind('', lambda e: WIKI.select_file()) + self.root.bind('', lambda e: self.recreate_widgets()) + self.root.bind('', lambda e: open_updater()) # New Update Feature (Version 7.4.000 UPDATER) + + def open_file_location(self): + current_dir = os.getcwd() + if os.name == 'nt': # Windows + os.startfile(current_dir) + else: # Linux/Mac + try: + subprocess.run(['xdg-open', current_dir]) + except: + subprocess.run(['open', current_dir]) + + def remove_save_file(self): + if os.path.exists('quantities.json'): + if messagebox.askyesno("Löschen bestätigen", "Sind Sie sicher, dass Sie die Speicherdatei löschen möchten?"): + try: + os.remove('quantities.json') + messagebox.showinfo("Erfolg", "Speicherdatei wurde erfolgreich gelöscht!") + self.quantities = {product: 0 for product in self.products} + self.update_total() + for widget in self.root.winfo_children(): + if isinstance(widget, ttk.Frame): + for child in widget.winfo_children(): + if isinstance(child, ttk.Frame): + for grandchild in child.winfo_children(): + if isinstance(grandchild, ttk.Spinbox): + grandchild.set("0") + except Exception as e: + messagebox.showerror("Fehler", f"Datei konnte nicht gelöscht werden: {str(e)}") + else: + messagebox.showinfo("Info", "Keine Speicherdatei vorhanden.") + + def load_quantities(self): + try: + with open('quantities.json', 'r') as f: + self.quantities = json.load(f) + except FileNotFoundError: + self.quantities = {product: 0 for product in self.products} + + def save_quantities(self): + try: + with open('quantities.json', 'w') as f: + json.dump(self.quantities, f) + messagebox.showinfo("Erfolg", "Mengen wurden erfolgreich gespeichert!") + except Exception as e: + messagebox.showerror("Fehler", f"Fehler beim Speichern der Mengen: {str(e)}") + + def create_widgets(self): + main_frame = ttk.Frame(self.root) + main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + + for i, product in enumerate(self.products): + frame = ttk.Frame(main_frame) + frame.grid(row=0, column=i, padx=10, pady=5) + + image = self.load_image(product) + if image: + self.images[product] = image + label = ttk.Label(frame, image=self.images[product]) + label.grid(row=0, column=0, pady=5) + else: + canvas = tk.Canvas(frame, width=100, height=100, bg='white') + canvas.grid(row=0, column=0, pady=5) + canvas.create_text(50, 50, text=f"Kein {product}\nBild gefunden") + + ttk.Label(frame, text=product).grid(row=1, column=0, pady=2) + ttk.Label(frame, text=f"€{self.PRICES[product]:.2f}").grid(row=2, column=0, pady=2) + + quantity_var = tk.StringVar(value=str(self.quantities.get(product, 0))) + spinbox = ttk.Spinbox( + frame, + from_=0, + to=100, + width=5, + textvariable=quantity_var, + command=lambda p=product, v=quantity_var: self.update_quantity(p, v) + ) + spinbox.grid(row=3, column=0, pady=5) + + # Store spinbox reference + self.spinboxes[product] = spinbox + + spinbox.bind('', lambda event, p=product, v=quantity_var: self.update_quantity(p, v)) + spinbox.bind('', lambda event, p=product, v=quantity_var: self.update_quantity(p, v)) + + self.total_label = ttk.Label(main_frame, text="Gesamt: €0.00", font=('TkDefaultFont', 10, 'bold')) + self.total_label.grid(row=1, column=0, columnspan=len(self.products), pady=10) + + self.update_total() + + def update_quantity(self, product, var, event=None): + try: + quantity = int(var.get()) + self.quantities[product] = quantity + self.update_total() + except ValueError: + var.set(str(self.quantities.get(product, 0))) + + def update_total(self): + total = sum(self.quantities[product] * self.PRICES[product] for product in self.products) # get total + self.total_label.config(text=f"Gesamt: €{total:.2f}") + + def load_deposit_history(self): + try: + with open('deposit_history.json', 'r') as f: + return json.load(f) + except FileNotFoundError: + return [] + + def save_deposit_history(self): + try: + with open('deposit_history.json', 'w') as f: + json.dump(self.deposit_history, f) + except Exception as e: + messagebox.showerror("Fehler", f"Fehler beim Speichern der Historie: {str(e)}") + + # Changed in Version 7.4.101 + def show_deposit_history(self): + try: + with open("deposit_history.json", "r", encoding="utf-8") as file: + deposit_history = json.load(file) + except (FileNotFoundError, json.JSONDecodeError) as e: + print("error while loading file") + return + + history_window = tk.Toplevel(self.root) + history_window.title("Pfand Abgabe Historie") + history_window.geometry("900x500") + + main_frame = ttk.Frame(history_window) + main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + all_items = set() + for deposit in deposit_history: + all_items.update(deposit['quantities'].keys()) + + item_columns = sorted(all_items) + columns = ['Datum'] + item_columns + ['Gesamt'] + + tree = ttk.Treeview(main_frame, columns=columns, show='headings') + + for col in columns: + tree.heading(col, text=col, anchor='center') + width = 100 if col == 'Datum' else 80 + tree.column(col, width=width, anchor='center' if col != 'Gesamt' else 'e') + + scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=tree.yview) + tree.configure(yscrollcommand=scrollbar.set) + + tree.grid(row=0, column=0, sticky='nsew') + scrollbar.grid(row=0, column=1, sticky='ns') + + main_frame.grid_columnconfigure(0, weight=1) + main_frame.grid_rowconfigure(0, weight=1) + + totals = {item: 0 for item in item_columns} + total_amount = 0 + + for deposit in deposit_history: + quantities = deposit.get('quantities', {}) + row = [deposit.get('date', '')] + for item in item_columns: + count = quantities.get(item, 0) + row.append(count) + totals[item] += count + amount = deposit.get('total', 0.0) + row.append(f"{amount:.2f}") + total_amount += amount + tree.insert('', tk.END, values=row) + + totals_frame = ttk.Frame(main_frame) + totals_frame.grid(row=1, column=0, sticky='ew', pady=(5, 0)) + + separator = ttk.Separator(main_frame, orient='horizontal') + separator.grid(row=2, column=0, sticky='ew', pady=2) + + bold_font = ('TkDefaultFont', 9, 'bold') + + row = ["Gesamt:"] + for item in item_columns: + row.append(f"{totals[item]} {item}") + row.append(f"€{total_amount:.2f}") + for idx, value in enumerate(row): + ttk.Label(totals_frame, text=value, font=bold_font).grid(row=0, column=idx, sticky='w', padx=5) + + separator.grid(row=3, column=0, sticky='ew', pady=5) + + def make_deposit(self): + deposit_dialog = tk.Toplevel(self.root) + deposit_dialog.title("Pfand Abgeben") + deposit_dialog.geometry("300x150") + deposit_dialog.transient(self.root) + deposit_dialog.grab_set() + + ttk.Label(deposit_dialog, text="Abgabe Datum:").pack(pady=5) + date_picker = DateEntry(deposit_dialog, width=12, background='darkblue', + foreground='white', borderwidth=2, locale='de_DE') + date_picker.pack(pady=5) + + def confirm_deposit(): + date = date_picker.get_date().strftime("%d.%m.%Y") + current_total = sum(self.quantities[product] * self.PRICES[product] + for product in self.products) + + deposit_record = { + 'date': date, + 'quantities': dict(self.quantities), + 'total': current_total + } + self.deposit_history.append(deposit_record) + self.save_deposit_history() + + self.quantities = {product: 0 for product in self.products} + self.save_quantities() + self.update_total() + + for widget in self.root.winfo_children(): + if isinstance(widget, ttk.Frame): + for child in widget.winfo_children(): + if isinstance(child, ttk.Frame): + for grandchild in child.winfo_children(): + if isinstance(grandchild, ttk.Spinbox): + grandchild.set("0") + + messagebox.showinfo("Erfolg", "Pfand wurde erfolgreich abgegeben!") + deposit_dialog.destroy() + + button_frame = ttk.Frame(deposit_dialog) + button_frame.pack(pady=20) + ttk.Button(button_frame, text="Abgeben", command=confirm_deposit).pack(side=tk.LEFT, padx=5) + ttk.Button(button_frame, text="Abbrechen", command=deposit_dialog.destroy).pack(side=tk.LEFT, padx=5) + + def quick_deposit(self): + if sum(self.quantities.values()) == 0: + messagebox.showinfo("Info", "Keine Mengen zum Abgeben vorhanden.") + return + + if messagebox.askyesno("Pfand Abgeben", "Möchten Sie den Pfand mit dem heutigen Datum abgeben?"): + current_date = datetime.now().strftime("%d.%m.%Y") + current_total = sum(self.quantities[product] * self.PRICES[product] + for product in self.products) + + deposit_record = { + 'date': current_date, + 'quantities': dict(self.quantities), + 'total': current_total + } + self.deposit_history.append(deposit_record) + self.save_deposit_history() + + self.check_achievements() + + self.quantities = {product: 0 for product in self.products} + self.save_quantities() + self.update_total() + + for widget in self.root.winfo_children(): + if isinstance(widget, ttk.Frame): + for child in widget.winfo_children(): + if isinstance(child, ttk.Frame): + for grandchild in child.winfo_children(): + if isinstance(grandchild, ttk.Spinbox): + grandchild.set("0") + + messagebox.showinfo("Erfolg", "Pfand wurde erfolgreich abgegeben!") + else: + self.make_deposit() + + def export_history_csv(self): + if not self.deposit_history: + messagebox.showinfo("Info", "Keine Historie zum Exportieren vorhanden.") + return + + try: + file_path = filedialog.asksaveasfilename( + defaultextension=".csv", + filetypes=[("CSV Dateien", "*.csv")], + initialfile="pfand_historie.csv" + ) + + if not file_path: + return + + with open(file_path, 'w', newline='', encoding='utf-8') as csvfile: + writer = csv.writer(csvfile, delimiter=';') + + # Create header with all products + header = ['Datum'] + self.products + ['Gesamt (€)'] + writer.writerow(header) + + for deposit in self.deposit_history: + # Create row with all products + row = [deposit['date']] + for product in self.products: + row.append(deposit['quantities'].get(product, 0)) + row.append(f"{deposit['total']:.2f}") + writer.writerow(row) + + messagebox.showinfo("Erfolg", "Historie wurde erfolgreich exportiert!") + except Exception as e: + messagebox.showerror("Fehler", f"Fehler beim Exportieren: {str(e)}") + + def clear_deposit_history(self): + if not self.deposit_history: + messagebox.showinfo("Info", "Keine Historie zum Löschen vorhanden.") + return + + if messagebox.askyesno("Löschen bestätigen", + "Sind Sie sicher, dass Sie die gesamte Abgabe-Historie löschen möchten?\n" + "Dieser Vorgang kann nicht rückgängig gemacht werden!"): + try: + self.deposit_history = [] + if os.path.exists('deposit_history.json'): + os.remove('deposit_history.json') + messagebox.showinfo("Erfolg", "Abgabe-Historie wurde erfolgreich gelöscht!") + except Exception as e: + messagebox.showerror("Fehler", f"Fehler beim Löschen der Historie: {str(e)}") + + def handle_shift_f1(self, event): + if event.state & 0x1: + self.remove_save_file() + + def handle_shift_f2(self, event): + if event.state & 0x1: + self.clear_deposit_history() + + def delete_achievements(self): + if not any(achievement.unlocked for achievement in self.achievements.values()): + messagebox.showinfo("Info", "Keine Auszeichnungen zum Löschen vorhanden.") + return + + if messagebox.askyesno("Löschen bestätigen", + "Sind Sie sicher, dass Sie alle Auszeichnungen löschen möchten?\n" + "Dieser Vorgang kann nicht rückgängig gemacht werden!"): + try: + for achievement in self.achievements.values(): + achievement.unlocked = False + achievement.unlock_date = None + + if os.path.exists('achievements.json'): + os.remove('achievements.json') + messagebox.showinfo("Erfolg", "Alle Auszeichnungen wurden erfolgreich gelöscht!") + except Exception as e: + messagebox.showerror("Fehler", f"Fehler beim Löschen der Auszeichnungen: {str(e)}") + + def open_scanner_window(self): + if self.scanner_window is None or not self.scanner_window.winfo_exists(): + self.scanner_window = tk.Toplevel(self.root) + self.scanner_window.title("Pfand Scanner") + self.scanner_window.protocol("WM_DELETE_WINDOW", self.close_scanner_window) + + # Create frames for scanner layout + self.camera_frame = ttk.Frame(self.scanner_window) + self.camera_frame.pack(side="left", padx=10, pady=5) + + self.scanner_control_frame = ttk.Frame(self.scanner_window) + self.scanner_control_frame.pack(side="left", padx=10, pady=5, fill="y") + + # Create camera label + self.camera_label = ttk.Label(self.camera_frame) + self.camera_label.pack() + + # Create focus control + focus_frame = ttk.LabelFrame(self.scanner_control_frame, text="Kamera Einstellungen") + focus_frame.pack(pady=5, padx=5, fill="x") + + ttk.Label(focus_frame, text="Fokus:").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="Autofokus", + variable=self.autofocus_var, + command=self.toggle_autofocus + ) + self.autofocus_check.pack(pady=2) + + # Create image processing controls + process_frame = ttk.LabelFrame(self.scanner_control_frame, text="Bildverarbeitung") + process_frame.pack(pady=5, padx=5, fill="x") + + ttk.Label(process_frame, text="Helligkeit:").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="Kontrast:").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") + + # Start/Stop button + self.scan_button = ttk.Button( + self.scanner_control_frame, + text="Scannen Starten", + command=self.toggle_scanning + ) + self.scan_button.pack(pady=10) + + # Initialize scan counter for achievements + self.daily_scans = 0 + self.total_scans = 0 + self.last_scan_date = None + + # Queue for thread-safe communication + self.queue = queue.Queue() + + # Set window size to match camera resolution + self.scanner_window.geometry("1600x800") + + def close_scanner_window(self): + if self.scanning: + self.toggle_scanning() + if self.cap: + self.cap.release() + self.cap = None + if self.scanner_window: + self.scanner_window.destroy() + self.scanner_window = None + + def toggle_scanning(self): + if not self.scanning: + self.cap = cv2.VideoCapture(0) + + # Set optimal camera properties for performance + self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) + self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) + self.cap.set(cv2.CAP_PROP_FPS, 30) # Request 30 FPS + self.cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) # Minimize buffer delay + + self.cap.set(cv2.CAP_PROP_AUTOFOCUS, 0) + self.cap.set(cv2.CAP_PROP_FOCUS, 0) + + self.scanning = True + self.scan_button.configure(text="Scannen Stoppen") + self.process_video() + else: + self.scanning = False + if self.cap: + self.cap.release() + self.cap = None + self.scan_button.configure(text="Scannen Starten") + self.camera_label.configure(image='') + + def toggle_autofocus(self): + if self.cap: + 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): + # This method is now only used for preview adjustments + 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) + return adjusted + + def process_video(self): + if not self.scanning: + return + + try: + ret, frame = self.cap.read() + if ret: + if not self.autofocus_var.get(): + self.cap.set(cv2.CAP_PROP_FOCUS, self.focus_slider.get()) + + # Resize frame for faster processing (720p is plenty for barcode detection) + frame = cv2.resize(frame, (1280, 720)) + + # Only process every 3rd frame for barcode detection + if hasattr(self, 'frame_count'): + self.frame_count += 1 + else: + self.frame_count = 0 + + if self.frame_count % 3 == 0: # Process every 3rd frame + # Process in grayscale for better performance + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + barcodes = decode(gray) + + for barcode in barcodes: + points = barcode.polygon + if len(points) == 4: + pts = np.array([(p.x, p.y) for p in points]) + cv2.polylines(frame, [pts], True, (0, 255, 0), 2) + + barcode_data = barcode.data.decode('utf-8') + if barcode_data not in self.scanned_barcodes: + self.scanned_barcodes.add(barcode_data) + self.scanner_window.after(0, lambda d=barcode_data: self.handle_barcode(d)) + + # Convert and display frame + 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) + + if self.scanning: + # Use a shorter delay for higher frame rate + self.scanner_window.after(5, self.process_video) + except Exception as e: + print(f"Error in process_video: {e}") + if self.scanning: + self.scanner_window.after(5, self.process_video) + + def handle_barcode(self, barcode_data): + # First dialog for Pfand symbol verification + verify_dialog = tk.Toplevel(self.scanner_window) + verify_dialog.title("Pfand Symbol Überprüfung") + verify_dialog.transient(self.scanner_window) + verify_dialog.grab_set() + + ttk.Label(verify_dialog, text="Ist ein Pfand Symbol auf dem Produkt?").pack(pady=10) + + def handle_verification(has_pfand): + verify_dialog.destroy() + if has_pfand: + # Add barcode to history with timestamp and Pfand status + self.barcode_history.append({ + 'timestamp': datetime.now().strftime("%d.%m.%Y %H:%M:%S"), + 'barcode': barcode_data, + 'has_pfand': True + }) + self.show_product_selection_dialog(barcode_data) + else: + # Add barcode to history with timestamp and Pfand status + self.barcode_history.append({ + 'timestamp': datetime.now().strftime("%d.%m.%Y %H:%M:%S"), + 'barcode': barcode_data, + 'has_pfand': False + }) + self.scanned_barcodes.remove(barcode_data) + messagebox.showinfo("Kein Pfand", "Dieses Produkt hat kein Pfand Symbol.") + + button_frame = ttk.Frame(verify_dialog) + button_frame.pack(pady=10) + ttk.Button(button_frame, text="Ja", command=lambda: handle_verification(True)).pack(side=tk.LEFT, padx=5) + ttk.Button(button_frame, text="Nein", command=lambda: handle_verification(False)).pack(side=tk.LEFT, padx=5) + + def show_product_selection_dialog(self, barcode_data): + dialog = tk.Toplevel(self.scanner_window) + dialog.title("Barcode Erkannt") + dialog.transient(self.scanner_window) + dialog.grab_set() + + ttk.Label(dialog, text=f"Barcode erkannt: {barcode_data}").pack(pady=10) + ttk.Label(dialog, text="Produkttyp auswählen:").pack(pady=5) + + product_var = tk.StringVar() + for product in self.products: + ttk.Radiobutton(dialog, text=product, variable=product_var, value=product).pack() + + def confirm(): + selected_product = product_var.get() + if selected_product: + print(f"Erhöhe {selected_product}") # Debug print + # Update quantity + current_qty = self.quantities.get(selected_product, 0) + self.quantities[selected_product] = current_qty + 1 + print(f"Neue Menge für {selected_product}: {self.quantities[selected_product]}") # Debug print + + # Update scan counters and check achievements + self.update_scan_achievements() + + # Force immediate UI update + def do_update(): + try: + # Directly update the spinbox + spinbox = self.spinboxes[selected_product] + spinbox.set(str(self.quantities[selected_product])) + spinbox.update() # Force the spinbox to update + + # Update the total + self.update_total() + self.root.update_idletasks() # Force the entire UI to update + + # Save the quantities + self.save_quantities() + print("UI Update und Speicherung abgeschlossen") # Debug print + except Exception as e: + print(f"Fehler beim UI Update: {e}") + + # Schedule the update for the next event loop iteration + self.root.after(1, do_update) + dialog.destroy() + + def skip(): + self.scanned_barcodes.remove(barcode_data) + dialog.destroy() + + button_frame = ttk.Frame(dialog) + button_frame.pack(pady=10) + ttk.Button(button_frame, text="Hinzufügen", command=confirm).pack(side=tk.LEFT, padx=5) + ttk.Button(button_frame, text="Überspringen", command=skip).pack(side=tk.LEFT, padx=5) + + def update_scan_achievements(self): + current_date = datetime.now().strftime("%Y-%m-%d") + + # Update daily scan counter + if self.last_scan_date != current_date: + self.daily_scans = 1 + self.last_scan_date = current_date + else: + self.daily_scans += 1 + + # Update total scan counter + self.total_scans += 1 + + # Check scan-related achievements + achievements_to_check = { + 1: "first_scan", + 50: "scans_50", + 100: "scans_100", + 500: "scans_500" + } + + # Check total scans achievements + for count, achievement_key in achievements_to_check.items(): + if self.total_scans == count and not self.achievements[achievement_key].unlocked: + self.unlock_achievement(achievement_key) + self.save_achievements() + + # Check daily scan achievements + daily_achievements = { + 10: "daily_10", + 25: "daily_25" + } + + for count, achievement_key in daily_achievements.items(): + if self.daily_scans == count and not self.achievements[achievement_key].unlocked: + self.unlock_achievement(achievement_key) + self.save_achievements() + + # Save the updated scan counts + self.save_quantities() # This ensures we don't lose progress + + def update_ui(self): + def update_spinboxes(): + try: + # Update all spinboxes to match quantities + main_frame = self.root.winfo_children()[0] # Get the main frame + for frame in main_frame.winfo_children(): + if isinstance(frame, ttk.Frame): + # Find the product this frame represents + for widget in frame.winfo_children(): + if isinstance(widget, ttk.Label) and widget.cget('text') in self.products: + product = widget.cget('text') + current_qty = self.quantities.get(product, 0) + # Find and update the spinbox + for w in frame.winfo_children(): + if isinstance(w, ttk.Spinbox): + w.set(str(current_qty)) + w.update() + break + + # Update the total + self.update_total() + except Exception as e: + print(f"Error updating UI: {e}") + + # Ensure updates happen in the main thread + if threading.current_thread() is threading.main_thread(): + update_spinboxes() + else: + self.root.after(0, update_spinboxes) + + def on_closing(self): + if self.scanner_window and self.scanner_window.winfo_exists(): + self.close_scanner_window() + self.root.destroy() + + def export_barcodes_csv(self): + if not self.barcode_history: + messagebox.showinfo("Info", "Keine Barcodes zum Exportieren vorhanden.") + return + + try: + file_path = filedialog.asksaveasfilename( + defaultextension=".csv", + filetypes=[("CSV Dateien", "*.csv")], + initialfile="barcode_historie.csv" + ) + + if not file_path: + return + + with open(file_path, 'w', newline='', encoding='utf-8') as csvfile: + writer = csv.writer(csvfile, delimiter=';') + + # Write header + writer.writerow(['Datum', 'Barcode', 'Hat Pfand']) + + # Write barcode history + for entry in self.barcode_history: + writer.writerow([ + entry['timestamp'], + entry['barcode'], + 'Ja' if entry['has_pfand'] else 'Nein' + ]) + + messagebox.showinfo("Erfolg", "Barcode Historie wurde erfolgreich exportiert!") + except Exception as e: + messagebox.showerror("Fehler", f"Fehler beim Exportieren: {str(e)}") + + def load_products(self): + try: + with open('products.json', 'r') as f: + data = json.load(f) + self.products = data.get('products', []) + self.PRICES = data.get('prices', {}) + except FileNotFoundError: + # Default products if no JSON exists + self.products = ["Flaschen", "Bierflasche", "Kasten", "Dose", "Plastikflasche", "Monster", "Joghurt Glas"] + self.PRICES = { + "Flaschen": 0.25, + "Bierflasche": 0.20, + "Kasten": 3.00, + "Dose": 0.25, + "Plastikflasche": 0.25, + "Monster": 0.25, + "Joghurt Glas": 0.17, + } + self.save_products() + + def save_products(self): + try: + data = { + 'products': self.products, + 'prices': self.PRICES + } + with open('products.json', 'w') as f: + json.dump(data, f) + except Exception as e: + messagebox.showerror("Fehler", f"Fehler beim Speichern der Produkte: {str(e)}") + + def show_add_product_window(self): + dialog = tk.Toplevel(self.root) + dialog.title("Neues Produkt hinzufügen") + dialog.geometry("400x300") + dialog.transient(self.root) + dialog.grab_set() + + # Product name + name_frame = ttk.Frame(dialog) + name_frame.pack(fill='x', padx=10, pady=5) + ttk.Label(name_frame, text="Produktname:").pack(side='left') + name_var = tk.StringVar() + name_entry = ttk.Entry(name_frame, textvariable=name_var) + name_entry.pack(side='left', fill='x', expand=True, padx=5) + + # Deposit amount + deposit_frame = ttk.Frame(dialog) + deposit_frame.pack(fill='x', padx=10, pady=5) + ttk.Label(deposit_frame, text="Pfandbetrag (€):").pack(side='left') + deposit_var = tk.StringVar() + deposit_entry = ttk.Entry(deposit_frame, textvariable=deposit_var) + deposit_entry.pack(side='left', fill='x', expand=True, padx=5) + + # Image selection + image_frame = ttk.Frame(dialog) + image_frame.pack(fill='x', padx=10, pady=5) + ttk.Label(image_frame, text="Bild:").pack(side='left') + image_path_var = tk.StringVar() + image_entry = ttk.Entry(image_frame, textvariable=image_path_var) + image_entry.pack(side='left', fill='x', expand=True, padx=5) + ttk.Button(image_frame, text="Durchsuchen", command=lambda: self.select_image(image_path_var)).pack(side='left') + + def add_product(): + name = name_var.get().strip() + try: + deposit = float(deposit_var.get().replace(',', '.')) + except ValueError: + messagebox.showerror("Fehler", "Bitte geben Sie einen gültigen Pfandbetrag ein.") + return + + if not name: + messagebox.showerror("Fehler", "Bitte geben Sie einen Produktnamen ein.") + return + + if name in self.products: + messagebox.showerror("Fehler", "Ein Produkt mit diesem Namen existiert bereits.") + return + + image_path = image_path_var.get() + if image_path: + try: + # Create images directory if it doesn't exist + if not os.path.exists('PfandApplication/images'): + os.makedirs('images') + + # Copy and rename the image + new_image_path = f"PfandApplication/images/{name.lower()}.png" + shutil.copy2(image_path, new_image_path) + except Exception as e: + messagebox.showerror("Fehler", f"Fehler beim Kopieren des Bildes: {str(e)}") + return + + # Add the new product + self.products.append(name) + self.PRICES[name] = deposit + self.save_products() + + # Update the UI + self.recreate_widgets() + dialog.destroy() + + ttk.Button(dialog, text="Hinzufügen", command=add_product).pack(pady=10) + + def select_image(self, image_path_var, preview_label=None): + file_path = filedialog.askopenfilename( + filetypes=[("PNG files", "*.png"), ("All files", "*.*")] + ) + if file_path: + image_path_var.set(file_path) + if preview_label: + try: + image = Image.open(file_path) + # Resize image to fit preview (100x100) + image.thumbnail((100, 100), Image.Resampling.LANCZOS) + photo = ImageTk.PhotoImage(image) + preview_label.configure(image=photo) + preview_label.image = photo # Keep a reference + except Exception as e: + print(f"Error loading preview: {e}") + + def show_manage_products_window(self): + dialog = tk.Toplevel(self.root) + dialog.title("Produkte verwalten") + dialog.geometry("1200x600") + dialog.transient(self.root) + dialog.grab_set() + + # Create main container with two columns + main_container = ttk.Frame(dialog) + main_container.pack(fill='both', expand=True, padx=10, pady=5) + + # Left column for product list + left_frame = ttk.Frame(main_container) + left_frame.pack(side='left', fill='both', expand=True, padx=(0, 5)) + + # Right column for add/edit product + right_frame = ttk.LabelFrame(main_container, text="Produkt hinzufügen") + right_frame.pack(side='right', fill='both', expand=True, padx=(5, 0)) + + # Create treeview in left frame + tree = ttk.Treeview(left_frame, columns=('Name', 'Pfand'), show='headings') + tree.heading('Name', text='Name') + tree.heading('Pfand', text='Pfand (€)') + tree.pack(fill='both', expand=True) + + # Add scrollbar for treeview + scrollbar = ttk.Scrollbar(left_frame, orient='vertical', command=tree.yview) + scrollbar.pack(side='right', fill='y') + tree.configure(yscrollcommand=scrollbar.set) + + # Populate treeview + for product in self.products: + tree.insert('', 'end', values=(product, f"{self.PRICES[product]:.2f}")) + + # Add product form in right frame + # Product name + name_frame = ttk.Frame(right_frame) + name_frame.pack(fill='x', padx=10, pady=5) + ttk.Label(name_frame, text="Produktname:").pack(side='left') + name_var = tk.StringVar() + name_entry = ttk.Entry(name_frame, textvariable=name_var) + name_entry.pack(side='left', fill='x', expand=True, padx=5) + + # Deposit amount + deposit_frame = ttk.Frame(right_frame) + deposit_frame.pack(fill='x', padx=10, pady=5) + ttk.Label(deposit_frame, text="Pfandbetrag (€):").pack(side='left') + deposit_var = tk.StringVar() + deposit_entry = ttk.Entry(deposit_frame, textvariable=deposit_var) + deposit_entry.pack(side='left', fill='x', expand=True, padx=5) + + # Image selection with preview + image_frame = ttk.Frame(right_frame) + image_frame.pack(fill='x', padx=10, pady=5) + ttk.Label(image_frame, text="Bild:").pack(side='left') + image_path_var = tk.StringVar() + image_entry = ttk.Entry(image_frame, textvariable=image_path_var) + image_entry.pack(side='left', fill='x', expand=True, padx=5) + ttk.Button(image_frame, text="Durchsuchen", command=lambda: self.select_image(image_path_var, preview_label)).pack(side='left') + + # Image preview + preview_frame = ttk.Frame(right_frame) + preview_frame.pack(fill='x', padx=10, pady=5) + preview_label = ttk.Label(preview_frame) + preview_label.pack() + + def update_preview(image_path): + if image_path and os.path.exists(image_path): + try: + image = Image.open(image_path) + # Resize image to fit preview (100x100) + image.thumbnail((100, 100), Image.Resampling.LANCZOS) + photo = ImageTk.PhotoImage(image) + preview_label.configure(image=photo) + preview_label.image = photo # Keep a reference + except Exception as e: + print(f"Error loading preview: {e}") + + def select_image_with_preview(image_path_var, preview_label): + file_path = filedialog.askopenfilename( + filetypes=[("PNG files", "*.png"), ("All files", "*.*")] + ) + if file_path: + image_path_var.set(file_path) + update_preview(file_path) + + def add_product(): + name = name_var.get().strip() + try: + deposit = float(deposit_var.get().replace(',', '.')) + except ValueError: + messagebox.showerror("Fehler", "Bitte geben Sie einen gültigen Pfandbetrag ein.") + return + + if not name: + messagebox.showerror("Fehler", "Bitte geben Sie einen Produktnamen ein.") + return + + if name in self.products: + messagebox.showerror("Fehler", "Ein Produkt mit diesem Namen existiert bereits.") + return + + image_path = image_path_var.get() + if image_path: + try: + # Create images directory if it doesn't exist + if not os.path.exists('PfandApplication/images'): + os.makedirs('images') + + # Copy and rename the image + new_image_path = f"PfandApplication/images/{name.lower()}.png" + shutil.copy2(image_path, new_image_path) + except Exception as e: + messagebox.showerror("Fehler", f"Fehler beim Kopieren des Bildes: {str(e)}") + return + + # Add the new product + self.products.append(name) + self.PRICES[name] = deposit + self.save_products() + + # Update treeview + tree.insert('', 'end', values=(name, f"{deposit:.2f}")) + tree.yview_moveto(1) # Scroll to the bottom to show the new item + + # Clear form + name_var.set("") + deposit_var.set("") + image_path_var.set("") + preview_label.configure(image='') + + # Update the main window UI + self.recreate_widgets() + + def delete_product(): + selected = tree.selection() + if not selected: + messagebox.showwarning("Warnung", "Bitte wählen Sie ein Produkt aus.") + return + + if messagebox.askyesno("Bestätigen", "Möchten Sie das ausgewählte Produkt wirklich löschen?"): + item = tree.item(selected[0]) + product_name = item['values'][0] + + # Remove from lists + self.products.remove(product_name) + del self.PRICES[product_name] + + # Delete image if exists + image_path = f"PfandApplication/images/{product_name.lower()}.png" + if os.path.exists(image_path): + try: + os.remove(image_path) + except Exception as e: + print(f"Fehler beim Löschen des Bildes: {e}") + + # Save changes and update UI + self.save_products() + self.recreate_widgets() + dialog.destroy() + + # Add buttons + button_frame = ttk.Frame(right_frame) + button_frame.pack(fill='x', padx=10, pady=10) + ttk.Button(button_frame, text="Hinzufügen", command=add_product).pack(side='left', padx=5) + ttk.Button(button_frame, text="Löschen", command=delete_product).pack(side='left', padx=5) + + # Bind image selection to preview update + image_path_var.trace_add('write', lambda *args: update_preview(image_path_var.get())) + + def recreate_widgets(self): + # Clear existing widgets + for widget in self.root.winfo_children(): + widget.destroy() + + # Recreate menu + self.create_menu() + + # Reload quantities + self.load_quantities() + + # Recreate main widgets + self.create_widgets() + + @staticmethod + def launch(check_for_update): + root = tk.Tk() + app = PfandCalculator(root) + + if check_for_update == True: + root.after(1, run_silent_update) # Run uc on start (1s delay) => updater.py module || UNCOMMENT IN PROD + else: + pass + + root.mainloop() + +if __name__ == "__main__": + PfandCalculator.launch(True) diff --git a/PfandApplication/pfand_scanner.py b/PfandApplication/pfand_scanner.py new file mode 100644 index 0000000..d1ca5be --- /dev/null +++ b/PfandApplication/pfand_scanner.py @@ -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") diff --git a/PfandApplication/updater.py b/PfandApplication/updater.py new file mode 100644 index 0000000..d4dd296 --- /dev/null +++ b/PfandApplication/updater.py @@ -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( + "", + 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("", 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() diff --git a/PfandApplication/wiki/__pycache__/main.cpython-313.pyc b/PfandApplication/wiki/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0fbf13acb3d437cf030ab27bb9634f7628acfd0 GIT binary patch literal 8298 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 U6ANtdpShf0oO#9)u0$2|e@hhnZU6uP literal 0 HcmV?d00001 diff --git a/PfandApplication/wiki/listeHOFER.csv b/PfandApplication/wiki/listeHOFER.csv new file mode 100644 index 0000000..17df5c7 --- /dev/null +++ b/PfandApplication/wiki/listeHOFER.csv @@ -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, diff --git a/PfandApplication/wiki/listeSPAR.csv b/PfandApplication/wiki/listeSPAR.csv new file mode 100644 index 0000000..ac1f734 --- /dev/null +++ b/PfandApplication/wiki/listeSPAR.csv @@ -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" diff --git a/PfandApplication/wiki/main.py b/PfandApplication/wiki/main.py new file mode 100644 index 0000000..f8c001c --- /dev/null +++ b/PfandApplication/wiki/main.py @@ -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'

(.*?)

|\*\*(.*?)\*\*', 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() + diff --git a/PfandApplication/wiki/readme.md b/PfandApplication/wiki/readme.md new file mode 100644 index 0000000..4a44415 --- /dev/null +++ b/PfandApplication/wiki/readme.md @@ -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! diff --git a/PfandApplication/wiki/wiki.md b/PfandApplication/wiki/wiki.md new file mode 100644 index 0000000..cd39d0f --- /dev/null +++ b/PfandApplication/wiki/wiki.md @@ -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. + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..46762f9 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +# ♻️ Pfandrechner Application Suite + +**Version:** V.7.04.301 +**License:** [MIT](LICENSE) + +Welcome to the **Pfandrechner Application - Package** – a sleek and powerful tool for tracking and calculating container deposits ("Pfand") in Austria 🇦🇹. Whether you're returning a few bottles or managing full bags, this app has you covered! + +This is a copy of [Pfand](https://github.com/ZockerKatze/pfand), but this is in Package Form which can be **imported** and integrated! + +--- + +## ✨ Features + +🔢 **Deposit Calculator** – Instantly compute the total value of your returned bottles and cans. + +🏆 **Achievements** – Track your progress and unlock fun rewards for your deposit milestones. + +📜 **History & Exports** – View your past returns and export the data for safekeeping or bragging rights. + +📦 **TGTG Integration** – Check on your "Too Good To Go" orders directly within the app. ( You need to setup your API Key first! ) + +⚙️ **Smart Updater** – Keeps the app fresh with the latest features and fixes. + +--- + +## 🚀 Getting Started + +### 1. Clone the Repository + +```bash +git clone https://github.com/ZockerKatze/pfand.git +cd pfand +``` + +### 2. Install Dependencies + +Make sure you’re using Python 3, then run: + +```bash +pip install -r requirements.txt +``` + +### 3. Launch the App + +```bash +python run.py +``` + +--- + +## 🧮 How to Count + +You can either: + +- ✍️ **Manually** count and enter your container numbers + _OR_ +- 🔬 Use **µScan** – the improved scanner for fast and accurate counting with barcode recognition using _pyzbar_! + +--- + +## 🤝 Contributing + +Want to improve the app or add new features? Awesome! +Fork the repo, make your changes, and send a pull request. 💡 + +--- + +## 📄 License + +This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. + +--- + +Made with 💚 for recycling and a cleaner future. diff --git a/iex.png b/iex.png new file mode 100644 index 0000000000000000000000000000000000000000..32ef07411f314d73acd77f9b2e52987794694df4 GIT binary patch literal 42113 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 o-bWv1UFWE#Z~u>eRmA;f%5qv!