Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ebcfeba1e4 | |||
| 22f0798f81 | |||
| 5b51c67069 | |||
| d9efaa4620 | |||
| cf55db126d | |||
|
|
8d81052d4c | ||
| 998228a604 |
647
.gitignore
vendored
647
.gitignore
vendored
@@ -1 +1,648 @@
|
||||
*.pyc
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[codz]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
# Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
# poetry.lock
|
||||
# poetry.toml
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
||||
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
||||
# pdm.lock
|
||||
# pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# pixi
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
||||
# pixi.lock
|
||||
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
||||
# in the .venv directory. It is recommended not to include this directory in version control.
|
||||
.pixi
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# Redis
|
||||
*.rdb
|
||||
*.aof
|
||||
*.pid
|
||||
|
||||
# RabbitMQ
|
||||
mnesia/
|
||||
rabbitmq/
|
||||
rabbitmq-data/
|
||||
|
||||
# ActiveMQ
|
||||
activemq-data/
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.envrc
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
# .idea/
|
||||
|
||||
# Abstra
|
||||
# Abstra is an AI-powered process automation framework.
|
||||
# Ignore directories containing user credentials, local state, and settings.
|
||||
# Learn more at https://abstra.io/docs
|
||||
.abstra/
|
||||
|
||||
# Visual Studio Code
|
||||
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
||||
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
||||
# you could uncomment the following to ignore the entire vscode folder
|
||||
# .vscode/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
# Marimo
|
||||
marimo/_static/
|
||||
marimo/_lsp/
|
||||
__marimo__/
|
||||
|
||||
# Streamlit
|
||||
.streamlit/secrets.toml
|
||||
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
*.env
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
|
||||
[Dd]ebug/x64/
|
||||
[Dd]ebugPublic/x64/
|
||||
[Rr]elease/x64/
|
||||
[Rr]eleases/x64/
|
||||
bin/x64/
|
||||
obj/x64/
|
||||
|
||||
[Dd]ebug/x86/
|
||||
[Dd]ebugPublic/x86/
|
||||
[Rr]elease/x86/
|
||||
[Rr]eleases/x86/
|
||||
bin/x86/
|
||||
obj/x86/
|
||||
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
[Aa][Rr][Mm]64[Ee][Cc]/
|
||||
bld/
|
||||
[Oo]bj/
|
||||
[Oo]ut/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Build results on 'Bin' directories
|
||||
**/[Bb]in/*
|
||||
# Uncomment if you have tasks that rely on *.refresh files to move binaries
|
||||
# (https://github.com/github/gitignore/pull/3736)
|
||||
#!**/[Bb]in/*.refresh
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
*.trx
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Approval Tests result files
|
||||
*.received.*
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.idb
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
# but not Directory.Build.rsp, as it configures directory-level build defaults
|
||||
!Directory.Build.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.tlog
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||
*.dsw
|
||||
*.dsp
|
||||
|
||||
# Visual Studio 6 technical files
|
||||
*.ncb
|
||||
*.aps
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
**/.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
**/.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
**/.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
**/__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
#tools/**
|
||||
#!tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
MSBuild_Logs/
|
||||
|
||||
# AWS SAM Build and Temporary Artifacts folder
|
||||
.aws-sam
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
**/.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
**/.localhistory/
|
||||
|
||||
# Visual Studio History (VSHistory) files
|
||||
.vshistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
**/.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# VS Code files for those working on multiple tools
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
# Windows Installer files from build outputs
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
@@ -1,5 +1,6 @@
|
||||
# @ImportStructure ; ref@todo/main.py , Element 3
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, filedialog
|
||||
from tkinter import ttk, messagebox, filedialog, font
|
||||
import webbrowser
|
||||
import json
|
||||
from PIL import Image, ImageTk
|
||||
@@ -15,15 +16,26 @@ import queue
|
||||
import numpy as np
|
||||
import shutil
|
||||
|
||||
# Local Dependencies
|
||||
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 PfandApplication.tgtg_orderchecker import main as tgtg
|
||||
from PfandApplication.tgtg_orderchecker import setupkey as tgtg_kt
|
||||
from PfandApplication.todo import main as todo
|
||||
from PfandApplication.todo.main import todo_instance
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
from wiki import main as wiki
|
||||
from pfand_scanner import launch_pfand_scanner
|
||||
from updater import open_updater as open_updater, run_silent_update
|
||||
from tgtg_orderchecker import main as tgtg
|
||||
from tgtg_orderchecker import setupkey as tgtg_kt
|
||||
from todo.main import todo_instance
|
||||
except ImportError:
|
||||
print("tried to import without package name. failed, trying to import using package name now")
|
||||
pass
|
||||
else:
|
||||
# @LocalImportStructure ; ref@todo/main.py , Element 3
|
||||
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 PfandApplication.tgtg_orderchecker import main as tgtg
|
||||
from PfandApplication.tgtg_orderchecker import setupkey as tgtg_kt
|
||||
from PfandApplication.todo.main import todo_instance
|
||||
|
||||
class Achievement:
|
||||
def __init__(self, title, description, condition_type, condition_value):
|
||||
@@ -519,32 +531,156 @@ class PfandCalculator:
|
||||
close_button.grid(row=2, column=1, padx=10, pady=10, sticky="ew")
|
||||
|
||||
todo_button = (
|
||||
tk.Button( # Looks like fucking shit , fix this horsecrap in the future
|
||||
tk.Button( # fixed this in commit 5b51c670694ba813a56880eabbdb8a8f6446fa7c
|
||||
about_window, text="Load Todo", command=self.create_todo_list
|
||||
)
|
||||
)
|
||||
todo_button.grid(row=2, column=3, padx=10, pady=10, sticky="ew")
|
||||
|
||||
# Todo
|
||||
todo_button.grid(row=3, column=0, columnspan=2, padx=10, pady=10, sticky="ew")
|
||||
|
||||
# ref@todo/main.py
|
||||
def create_todo_list(self):
|
||||
todo = tk.Toplevel(self.root)
|
||||
todo.title("Todo Liste")
|
||||
todo.resizable(True, True)
|
||||
|
||||
segoeui = font.Font(family="Segoe UI",size=11,weight="normal")
|
||||
monaco = font.Font(family="Monaco", size=9, slant="italic")
|
||||
|
||||
seperator = ttk.Separator(todo, orient="horizontal")
|
||||
seperator2 = ttk.Separator(todo, orient="horizontal")
|
||||
|
||||
|
||||
for col in range(2):
|
||||
todo.grid_columnconfigure(col, weight=1)
|
||||
todo.grid_rowconfigure(0, weight=0)
|
||||
|
||||
def close_todo():
|
||||
todo.destroy()
|
||||
|
||||
# Function to get done Todo Elements
|
||||
# tuple[0] total, [1] done. [2] precent for pbar
|
||||
def return_elements(todo_string : str) -> tuple[int, int, int]:
|
||||
total = todo_string.count("[ ]") + todo_string.count("[x]")
|
||||
done = todo_string.count("[x]")
|
||||
percent_done = (done / total) * 100 if total > 0 else 0
|
||||
return total, done, percent_done
|
||||
|
||||
progress_bar = ttk.Progressbar(todo, value=return_elements(todo_instance.load_todo())[2], style='Striped.Horizontal.TProgressbar')
|
||||
|
||||
# Shit will display here that is going to be loaded from external
|
||||
label_todo = tk.Label(
|
||||
todo,
|
||||
label_todo = tk.Label( todo,
|
||||
text=todo_instance.load_todo(),
|
||||
font=segoeui,
|
||||
padx=10,
|
||||
pady=10,
|
||||
justify="center",
|
||||
justify="left",
|
||||
anchor="center",
|
||||
)
|
||||
label_todo.grid(row=1, column=0, columnspan=2, pady=0, sticky="nsew")
|
||||
|
||||
label_todo_changelog = tk.Label( todo,
|
||||
text=todo_instance.load_changelog(),
|
||||
font=segoeui,
|
||||
padx=10,
|
||||
pady=10,
|
||||
justify="left",
|
||||
anchor="center"
|
||||
)
|
||||
|
||||
label_todo_tldr = tk.Label( todo,
|
||||
text=todo_instance.load_tldr(),
|
||||
font=segoeui,
|
||||
padx=10,
|
||||
pady=10,
|
||||
anchor="center",
|
||||
justify="left"
|
||||
)
|
||||
|
||||
percent = return_elements(todo_instance.load_todo())[2]
|
||||
percent = round(percent, 2)
|
||||
|
||||
label_todo_percentage_bar_text = tk.Label(
|
||||
todo,
|
||||
text=f"{percent}% Done (For Todo)",
|
||||
font=monaco,
|
||||
justify="right",
|
||||
anchor="w",
|
||||
padx=10,
|
||||
pady=0
|
||||
)
|
||||
|
||||
label_todo_close = tk.Button( todo,
|
||||
text="Schließen",
|
||||
command=close_todo,
|
||||
font=segoeui,
|
||||
padx=10,
|
||||
pady=10,
|
||||
)
|
||||
|
||||
# setup grid
|
||||
label_todo_tldr.grid(
|
||||
row=1,
|
||||
column=0,
|
||||
columnspan=2,
|
||||
padx=0,
|
||||
pady=5,
|
||||
sticky="nsew"
|
||||
)
|
||||
|
||||
seperator.grid(
|
||||
row=2,
|
||||
column=0,
|
||||
columnspan=2,
|
||||
pady=5,
|
||||
sticky="ew"
|
||||
)
|
||||
|
||||
label_todo.grid(
|
||||
row=3,
|
||||
column=0,
|
||||
columnspan=2,
|
||||
pady=5,
|
||||
sticky="nsew"
|
||||
)
|
||||
|
||||
seperator2.grid(
|
||||
row=4,
|
||||
column=0,
|
||||
columnspan=2,
|
||||
pady=5,
|
||||
sticky="ew"
|
||||
)
|
||||
|
||||
label_todo_changelog.grid(
|
||||
row=5,
|
||||
column=0,
|
||||
columnspan=2,
|
||||
pady=5,
|
||||
sticky="nsew"
|
||||
)
|
||||
|
||||
label_todo_percentage_bar_text.grid(
|
||||
row=6,
|
||||
column=0,
|
||||
columnspan=2,
|
||||
sticky="ew"
|
||||
)
|
||||
|
||||
progress_bar.grid(
|
||||
row=7,
|
||||
column=0,
|
||||
columnspan=2,
|
||||
pady=5,
|
||||
sticky="ew"
|
||||
)
|
||||
|
||||
label_todo_close.grid(
|
||||
row=8,
|
||||
column=0,
|
||||
columnspan=2,
|
||||
pady=5,
|
||||
sticky="nsew"
|
||||
)
|
||||
|
||||
# TGTG Credits
|
||||
def TGTG_credits(self):
|
||||
@@ -714,7 +850,7 @@ class PfandCalculator:
|
||||
about_uscan,
|
||||
text=(
|
||||
"µScan - Der bessere Barcode Scanner\n"
|
||||
"Version 2.2.2\n"
|
||||
"Version 2.3.5\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"
|
||||
"Beachte das µScan eine Kamera benötigt die mindestens 30FPS aufnehmen kann (Process-FPS können eingestellt werden!)"
|
||||
@@ -1736,7 +1872,6 @@ class PfandCalculator:
|
||||
"Kasten",
|
||||
"Dose",
|
||||
"Plastikflasche",
|
||||
"Monster",
|
||||
"Joghurt Glas",
|
||||
]
|
||||
self.PRICES = {
|
||||
@@ -1745,7 +1880,6 @@ class PfandCalculator:
|
||||
"Kasten": 3.00,
|
||||
"Dose": 0.25,
|
||||
"Plastikflasche": 0.25,
|
||||
"Monster": 0.25,
|
||||
"Joghurt Glas": 0.17,
|
||||
}
|
||||
self.save_products()
|
||||
@@ -2093,4 +2227,4 @@ class PfandCalculator:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
PfandCalculator.launch(True)
|
||||
PfandCalculator.launch(False)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# µScan V2.2.2
|
||||
# µScan V2.3.0 - Verbesserte Deutsche Version
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, simpledialog, messagebox
|
||||
import cv2
|
||||
@@ -10,81 +10,328 @@ import queue
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import numpy as np
|
||||
|
||||
class PfandScanner:
|
||||
def __init__(self, window, window_title):
|
||||
self.window = window
|
||||
self.window.title(window_title)
|
||||
self.window.geometry("1920x1080")
|
||||
self.window.minsize(960, 540)
|
||||
self.window.geometry("1600x900")
|
||||
self.window.minsize(1200, 700)
|
||||
self.window.configure(bg='#f0f0f0')
|
||||
|
||||
# Configure main window grid
|
||||
self.window.columnconfigure(0, weight=1)
|
||||
self.window.rowconfigure(0, weight=1)
|
||||
|
||||
# Style configuration
|
||||
self.setup_styles()
|
||||
|
||||
self.data_file = "quantities.json"
|
||||
self.load_json()
|
||||
self.products_file = "products.json"
|
||||
self.load_data()
|
||||
|
||||
self.barcode_times = {}
|
||||
self.prompted_barcodes = set()
|
||||
self.current_barcodes = [] # Store current frame's barcodes for outlining
|
||||
|
||||
self.selected_device_index = tk.IntVar(value=0)
|
||||
self.last_process_time = time.time()
|
||||
|
||||
# FPS Einstellung ist hier!
|
||||
# FPS Setting is here!
|
||||
self.process_interval = 0.30 # 30 FPS
|
||||
# FPS Setting
|
||||
self.process_interval = 0.15 # Improved processing speed
|
||||
|
||||
# Collapsible scan list state
|
||||
self.scan_list_collapsed = tk.BooleanVar(value=False)
|
||||
|
||||
# Threading control
|
||||
self.running = True
|
||||
self.camera_thread = None
|
||||
self.process_thread = None
|
||||
|
||||
self.init_gui()
|
||||
self.init_camera()
|
||||
|
||||
self.queue = queue.Queue()
|
||||
self.frame_queue = queue.Queue(maxsize=2) # Limit queue size to prevent memory issues
|
||||
self.pfand_values = {
|
||||
"EINWEG": 0.25,
|
||||
"MEHRWEG": 0.15,
|
||||
"DOSE": 0.25,
|
||||
}
|
||||
|
||||
self.update_preview()
|
||||
# Start threads
|
||||
self.start_threads()
|
||||
|
||||
self.window.protocol("WM_DELETE_WINDOW", self.on_closing)
|
||||
self.process_queue()
|
||||
|
||||
def setup_styles(self):
|
||||
self.style = ttk.Style()
|
||||
self.style.theme_use('clam')
|
||||
|
||||
# Configure colors
|
||||
self.colors = {
|
||||
'primary': '#2c3e50',
|
||||
'secondary': '#3498db',
|
||||
'success': '#27ae60',
|
||||
'warning': '#f39c12',
|
||||
'danger': '#e74c3c',
|
||||
'light': '#ecf0f1',
|
||||
'dark': '#34495e'
|
||||
}
|
||||
|
||||
# Configure custom styles
|
||||
self.style.configure('Title.TLabel', font=('Segoe UI', 16, 'bold'), foreground=self.colors['primary'])
|
||||
self.style.configure('Heading.TLabel', font=('Segoe UI', 12, 'bold'), foreground=self.colors['dark'])
|
||||
self.style.configure('Info.TLabel', font=('Segoe UI', 10), foreground=self.colors['dark'])
|
||||
self.style.configure('Success.TLabel', font=('Segoe UI', 10, 'bold'), foreground=self.colors['success'])
|
||||
|
||||
self.style.configure('Custom.TLabelFrame', relief='solid', borderwidth=1)
|
||||
self.style.configure('Camera.TFrame', relief='solid', borderwidth=2)
|
||||
|
||||
def load_data(self):
|
||||
# Load products
|
||||
if os.path.exists(self.products_file):
|
||||
try:
|
||||
with open(self.products_file, 'r', encoding='utf-8') as f:
|
||||
products_data = json.load(f)
|
||||
self.products = products_data.get("products", [])
|
||||
self.prices = products_data.get("prices", {})
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Laden der Produkte: {e}")
|
||||
self.products = []
|
||||
self.prices = {}
|
||||
else:
|
||||
self.products = []
|
||||
self.prices = {}
|
||||
|
||||
# Load quantities
|
||||
if os.path.exists(self.data_file):
|
||||
try:
|
||||
with open(self.data_file, 'r', encoding='utf-8') as f:
|
||||
self.quantities = json.load(f)
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Laden der Mengen: {e}")
|
||||
self.quantities = {}
|
||||
else:
|
||||
self.quantities = {}
|
||||
|
||||
def save_json(self):
|
||||
"""Save quantities to quantities.json"""
|
||||
try:
|
||||
with open(self.data_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(self.quantities, f, indent=4, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Speichern der Mengen: {e}")
|
||||
|
||||
def init_gui(self):
|
||||
self.main_frame = ttk.Frame(self.window)
|
||||
# Main container
|
||||
self.main_frame = ttk.Frame(self.window, padding="10")
|
||||
self.main_frame.grid(sticky="nsew")
|
||||
self.main_frame.columnconfigure(0, weight=3)
|
||||
self.main_frame.columnconfigure(1, weight=1)
|
||||
self.main_frame.columnconfigure(2, weight=3)
|
||||
self.main_frame.rowconfigure(0, weight=1)
|
||||
self.main_frame.columnconfigure(1, weight=2) # Camera gets more space
|
||||
self.main_frame.columnconfigure(0, weight=1) # Controls
|
||||
self.main_frame.columnconfigure(2, weight=1) # Info
|
||||
self.main_frame.rowconfigure(1, weight=1)
|
||||
|
||||
self.camera_frame = ttk.Frame(self.main_frame)
|
||||
self.camera_frame.grid(row=0, column=0, padx=5, pady=5, sticky="nsew")
|
||||
# Header
|
||||
header_frame = ttk.Frame(self.main_frame)
|
||||
header_frame.grid(row=0, column=0, columnspan=3, sticky="ew", pady=(0, 10))
|
||||
|
||||
self.control_frame = ttk.Frame(self.main_frame)
|
||||
self.control_frame.grid(row=0, column=1, padx=5, pady=5, sticky="nsew")
|
||||
title_label = ttk.Label(header_frame, text="µScan", style='Title.TLabel')
|
||||
title_label.pack(side="left")
|
||||
|
||||
self.info_frame = ttk.Frame(self.main_frame)
|
||||
self.info_frame.grid(row=0, column=2, padx=5, pady=5, sticky="nsew")
|
||||
status_label = ttk.Label(header_frame, text="v2.3.5", style='Info.TLabel')
|
||||
status_label.pack(side="right")
|
||||
|
||||
self.camera_label = ttk.Label(self.camera_frame)
|
||||
self.camera_label.pack(expand=True, fill="both")
|
||||
# Control Panel (Left)
|
||||
self.control_frame = ttk.LabelFrame(self.main_frame, text="Steuerung", padding="10")
|
||||
self.control_frame.grid(row=1, column=0, padx=(0, 10), sticky="nsew")
|
||||
|
||||
# Camera View (Center)
|
||||
self.camera_frame = ttk.LabelFrame(self.main_frame, text="Kameraansicht", padding="5")
|
||||
self.camera_frame.grid(row=1, column=1, padx=5, sticky="nsew")
|
||||
self.camera_frame.columnconfigure(0, weight=1)
|
||||
self.camera_frame.rowconfigure(0, weight=1)
|
||||
|
||||
# Info Panel (Right) - now collapsible
|
||||
self.info_frame = ttk.LabelFrame(self.main_frame, text="Scan-Ergebnisse", padding="10")
|
||||
self.info_frame.grid(row=1, column=2, padx=(10, 0), sticky="nsew")
|
||||
|
||||
# Camera display
|
||||
camera_container = ttk.Frame(self.camera_frame, relief='solid', borderwidth=2)
|
||||
camera_container.grid(sticky="nsew", padx=5, pady=5)
|
||||
camera_container.columnconfigure(0, weight=1)
|
||||
camera_container.rowconfigure(0, weight=1)
|
||||
|
||||
self.camera_label = ttk.Label(camera_container, text="Kamera wird geladen...", anchor="center")
|
||||
self.camera_label.grid(sticky="nsew")
|
||||
|
||||
self.init_device_selector()
|
||||
self.init_controls()
|
||||
self.init_treeview()
|
||||
self.init_statistics()
|
||||
|
||||
def init_device_selector(self):
|
||||
device_frame = ttk.LabelFrame(self.control_frame, text="Video Device")
|
||||
device_frame.pack(pady=5, padx=5, fill="x")
|
||||
device_frame = ttk.LabelFrame(self.control_frame, text="Kamera-Einstellungen", padding="10")
|
||||
device_frame.pack(fill="x", pady=(0, 10))
|
||||
|
||||
ttk.Label(device_frame, text="Choose Camera:").pack(anchor="w", padx=5)
|
||||
ttk.Label(device_frame, text="Kamera auswählen:", style='Heading.TLabel').pack(anchor="w")
|
||||
|
||||
self.device_combo = ttk.Combobox(device_frame, state="readonly")
|
||||
self.device_combo.pack(fill="x", padx=5)
|
||||
self.device_combo.pack(fill="x", pady=(5, 10))
|
||||
|
||||
available_devices = self.list_video_devices()
|
||||
self.device_combo['values'] = [f"Camera {i}" for i in available_devices]
|
||||
self.device_combo['values'] = [f"Kamera {i}" for i in available_devices]
|
||||
if available_devices:
|
||||
self.device_combo.current(0)
|
||||
self.device_combo.bind("<<ComboboxSelected>>", self.change_camera)
|
||||
|
||||
# Camera status
|
||||
self.camera_status = ttk.Label(device_frame, text="Status: Initialisierung...", style='Info.TLabel')
|
||||
self.camera_status.pack(anchor="w")
|
||||
|
||||
def init_controls(self):
|
||||
# Focus controls
|
||||
focus_frame = ttk.LabelFrame(self.control_frame, text="Fokus-Steuerung", padding="10")
|
||||
focus_frame.pack(fill="x", pady=(0, 10))
|
||||
|
||||
self.autofocus_var = tk.BooleanVar(value=True)
|
||||
self.autofocus_check = ttk.Checkbutton(
|
||||
focus_frame, text="Auto-Fokus", variable=self.autofocus_var, command=self.toggle_autofocus)
|
||||
self.autofocus_check.pack(anchor="w", pady=(0, 5))
|
||||
|
||||
ttk.Label(focus_frame, text="Manueller Fokus:").pack(anchor="w")
|
||||
self.focus_slider = ttk.Scale(focus_frame, from_=0, to=255, orient="horizontal")
|
||||
self.focus_slider.set(0)
|
||||
self.focus_slider.pack(fill="x", pady=(0, 5))
|
||||
|
||||
# Image processing controls
|
||||
process_frame = ttk.LabelFrame(self.control_frame, text="Bildverbesserung", padding="10")
|
||||
process_frame.pack(fill="x", pady=(0, 10))
|
||||
|
||||
ttk.Label(process_frame, text="Helligkeit:").pack(anchor="w")
|
||||
self.brightness_slider = ttk.Scale(process_frame, from_=0, to=100, orient="horizontal")
|
||||
self.brightness_slider.set(50)
|
||||
self.brightness_slider.pack(fill="x", pady=(0, 5))
|
||||
|
||||
ttk.Label(process_frame, text="Kontrast:").pack(anchor="w")
|
||||
self.contrast_slider = ttk.Scale(process_frame, from_=0, to=100, orient="horizontal")
|
||||
self.contrast_slider.set(50)
|
||||
self.contrast_slider.pack(fill="x", pady=(0, 10))
|
||||
|
||||
# Scan options
|
||||
scan_frame = ttk.LabelFrame(self.control_frame, text="Scan-Optionen", padding="10")
|
||||
scan_frame.pack(fill="x")
|
||||
|
||||
self.outline_var = tk.BooleanVar(value=True)
|
||||
ttk.Checkbutton(scan_frame, text="Barcodes umranden", variable=self.outline_var).pack(anchor="w")
|
||||
|
||||
self.beep_var = tk.BooleanVar(value=True)
|
||||
ttk.Checkbutton(scan_frame, text="Ton bei Scan", variable=self.beep_var).pack(anchor="w")
|
||||
|
||||
def init_statistics(self):
|
||||
"""Initialize statistics panel"""
|
||||
stats_frame = ttk.LabelFrame(self.info_frame, text="Statistiken", padding="10")
|
||||
stats_frame.pack(fill="x", pady=(0, 10))
|
||||
|
||||
self.total_scans_label = ttk.Label(stats_frame, text="Gesamte Scans: 0", style='Info.TLabel')
|
||||
self.total_scans_label.pack(anchor="w")
|
||||
|
||||
self.total_value_label = ttk.Label(stats_frame, text="Gesamtwert: €0,00", style='Success.TLabel')
|
||||
self.total_value_label.pack(anchor="w")
|
||||
|
||||
self.session_time_label = ttk.Label(stats_frame, text="Sitzungsdauer: 00:00", style='Info.TLabel')
|
||||
self.session_time_label.pack(anchor="w")
|
||||
|
||||
self.session_start = datetime.now()
|
||||
self.total_scans = 0
|
||||
self.total_value = 0.0
|
||||
|
||||
def init_treeview(self):
|
||||
# Header frame with collapse button
|
||||
tree_header_frame = ttk.Frame(self.info_frame)
|
||||
tree_header_frame.pack(fill="x", pady=(0, 5))
|
||||
|
||||
# Collapse button
|
||||
self.collapse_button = ttk.Button(
|
||||
tree_header_frame,
|
||||
text="▼ Scan-Liste",
|
||||
command=self.toggle_scan_list,
|
||||
width=15
|
||||
)
|
||||
self.collapse_button.pack(side="left")
|
||||
|
||||
# Clear button
|
||||
clear_button = ttk.Button(
|
||||
tree_header_frame,
|
||||
text="Liste löschen",
|
||||
command=self.clear_scan_list
|
||||
)
|
||||
clear_button.pack(side="right")
|
||||
|
||||
# Treeview frame (collapsible)
|
||||
self.tree_container = ttk.Frame(self.info_frame)
|
||||
self.tree_container.pack(fill="both", expand=True)
|
||||
|
||||
self.tree = ttk.Treeview(
|
||||
self.tree_container,
|
||||
columns=("Zeit", "Barcode", "Typ", "Pfand"),
|
||||
show="headings",
|
||||
height=15
|
||||
)
|
||||
|
||||
# Configure columns
|
||||
self.tree.heading("Zeit", text="Zeit")
|
||||
self.tree.heading("Barcode", text="Barcode")
|
||||
self.tree.heading("Typ", text="Typ")
|
||||
self.tree.heading("Pfand", text="Pfand")
|
||||
|
||||
self.tree.column("Zeit", width=100, minwidth=80)
|
||||
self.tree.column("Barcode", width=120, minwidth=100)
|
||||
self.tree.column("Typ", width=80, minwidth=60)
|
||||
self.tree.column("Pfand", width=70, minwidth=50)
|
||||
|
||||
# Scrollbars
|
||||
v_scrollbar = ttk.Scrollbar(self.tree_container, orient="vertical", command=self.tree.yview)
|
||||
h_scrollbar = ttk.Scrollbar(self.tree_container, orient="horizontal", command=self.tree.xview)
|
||||
|
||||
self.tree.configure(yscrollcommand=v_scrollbar.set, xscrollcommand=h_scrollbar.set)
|
||||
|
||||
# Grid layout
|
||||
self.tree.grid(row=0, column=0, sticky="nsew")
|
||||
v_scrollbar.grid(row=0, column=1, sticky="ns")
|
||||
h_scrollbar.grid(row=1, column=0, sticky="ew")
|
||||
|
||||
self.tree_container.columnconfigure(0, weight=1)
|
||||
self.tree_container.rowconfigure(0, weight=1)
|
||||
|
||||
# Alternate row colors
|
||||
self.tree.tag_configure('oddrow', background='#f9f9f9')
|
||||
self.tree.tag_configure('evenrow', background='white')
|
||||
|
||||
def toggle_scan_list(self):
|
||||
"""Toggle the visibility of the scan list"""
|
||||
if self.scan_list_collapsed.get():
|
||||
# Expand
|
||||
self.tree_container.pack(fill="both", expand=True)
|
||||
self.collapse_button.configure(text="▼ Scan-Liste")
|
||||
self.scan_list_collapsed.set(False)
|
||||
# Resize window columns
|
||||
self.main_frame.columnconfigure(2, weight=1)
|
||||
else:
|
||||
# Collapse
|
||||
self.tree_container.pack_forget()
|
||||
self.collapse_button.configure(text="▶ Scan-Liste")
|
||||
self.scan_list_collapsed.set(True)
|
||||
# Make info panel smaller
|
||||
self.main_frame.columnconfigure(2, weight=0, minsize=200)
|
||||
|
||||
def clear_scan_list(self):
|
||||
"""Clear all items from the scan list"""
|
||||
if messagebox.askyesno("Liste löschen", "Möchten Sie wirklich alle Einträge aus der Scan-Liste löschen?"):
|
||||
for item in self.tree.get_children():
|
||||
self.tree.delete(item)
|
||||
|
||||
def list_video_devices(self, max_devices=10):
|
||||
available = []
|
||||
for i in range(max_devices):
|
||||
@@ -99,65 +346,21 @@ class PfandScanner:
|
||||
self.selected_device_index.set(index)
|
||||
self.init_camera()
|
||||
|
||||
def init_controls(self):
|
||||
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")
|
||||
|
||||
def init_treeview(self):
|
||||
self.tree = ttk.Treeview(self.info_frame, columns=("Time", "Barcode", "Type", "Deposit"), show="headings")
|
||||
for col in ["Time", "Barcode", "Type", "Deposit"]:
|
||||
self.tree.heading(col, text=col)
|
||||
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)
|
||||
def init_camera(self):
|
||||
if hasattr(self, 'cap') and self.cap and self.cap.isOpened():
|
||||
self.cap.release()
|
||||
|
||||
device_index = self.selected_device_index.get()
|
||||
self.cap = cv2.VideoCapture(device_index, cv2.CAP_DSHOW if os.name == 'nt' else 0)
|
||||
|
||||
if self.cap.isOpened():
|
||||
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)
|
||||
self.toggle_autofocus()
|
||||
|
||||
def load_json(self):
|
||||
if os.path.exists(self.data_file):
|
||||
with open(self.data_file, 'r') as f:
|
||||
self.quantities = json.load(f)
|
||||
self.camera_status.configure(text="Status: Verbunden ✓", foreground=self.colors['success'])
|
||||
else:
|
||||
self.quantities = {}
|
||||
|
||||
def save_json(self):
|
||||
with open(self.data_file, 'w') as f:
|
||||
json.dump(self.quantities, f, indent=4)
|
||||
self.camera_status.configure(text="Status: Fehler ✗", foreground=self.colors['danger'])
|
||||
|
||||
def toggle_autofocus(self):
|
||||
if self.cap:
|
||||
@@ -178,8 +381,67 @@ class PfandScanner:
|
||||
return cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
|
||||
cv2.THRESH_BINARY, 11, 2)
|
||||
|
||||
def update_preview(self):
|
||||
def draw_barcode_outline(self, frame, barcodes):
|
||||
if not self.outline_var.get():
|
||||
return frame
|
||||
|
||||
for barcode in barcodes:
|
||||
# Get barcode polygon points
|
||||
points = barcode.polygon
|
||||
if len(points) == 4:
|
||||
# Convert to numpy array
|
||||
pts = np.array([[point.x, point.y] for point in points], np.int32)
|
||||
pts = pts.reshape((-1, 1, 2))
|
||||
|
||||
# Draw colored outline based on barcode type
|
||||
barcode_data = barcode.data.decode('utf-8')
|
||||
if len(barcode_data) == 13:
|
||||
color = (0, 255, 0) # Green for EINWEG
|
||||
elif len(barcode_data) == 8:
|
||||
color = (255, 0, 0) # Blue for MEHRWEG
|
||||
else:
|
||||
color = (0, 165, 255) # Orange for DOSE
|
||||
|
||||
# Draw outline
|
||||
cv2.polylines(frame, [pts], True, color, 3)
|
||||
|
||||
# Add label
|
||||
rect = cv2.boundingRect(pts)
|
||||
label_pos = (rect[0], rect[1] - 10)
|
||||
pfand_type = "EINWEG" if len(barcode_data) == 13 else "MEHRWEG" if len(barcode_data) == 8 else "DOSE"
|
||||
deposit = self.pfand_values.get(pfand_type, 0.00)
|
||||
label = f"{pfand_type}: €{deposit:.2f}"
|
||||
|
||||
# Draw label background
|
||||
(text_width, text_height), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 2)
|
||||
cv2.rectangle(frame, (label_pos[0], label_pos[1] - text_height - 5),
|
||||
(label_pos[0] + text_width, label_pos[1] + 5), color, -1)
|
||||
|
||||
# Draw label text
|
||||
cv2.putText(frame, label, label_pos, cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
|
||||
|
||||
return frame
|
||||
|
||||
def start_threads(self):
|
||||
"""Start camera and processing threads"""
|
||||
self.camera_thread = threading.Thread(target=self.camera_worker, daemon=True)
|
||||
self.camera_thread.start()
|
||||
|
||||
self.process_thread = threading.Thread(target=self.process_worker, daemon=True)
|
||||
self.process_thread.start()
|
||||
|
||||
# Start UI update loop
|
||||
self.update_preview()
|
||||
self.process_queue()
|
||||
|
||||
def camera_worker(self):
|
||||
"""Worker thread for camera capture and processing"""
|
||||
while self.running:
|
||||
try:
|
||||
if not hasattr(self, 'cap') or not self.cap.isOpened():
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
|
||||
ret, frame = self.cap.read()
|
||||
if ret:
|
||||
if not self.autofocus_var.get():
|
||||
@@ -189,63 +451,51 @@ class PfandScanner:
|
||||
if current_time - self.last_process_time >= self.process_interval:
|
||||
processed_frame = self.adjust_image(frame)
|
||||
barcodes = decode(processed_frame) or decode(frame)
|
||||
self.current_barcodes = barcodes
|
||||
|
||||
for barcode in barcodes:
|
||||
barcode_data = barcode.data.decode('utf-8')
|
||||
self.queue.put(barcode_data)
|
||||
self.last_process_time = current_time
|
||||
|
||||
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)
|
||||
# Draw barcode outlines
|
||||
frame_with_outlines = self.draw_barcode_outline(frame.copy(), self.current_barcodes)
|
||||
|
||||
# Put frame in queue for UI thread
|
||||
try:
|
||||
if self.frame_queue.full():
|
||||
self.frame_queue.get_nowait() # Discard old frame
|
||||
self.frame_queue.put(frame_with_outlines, timeout=0.1)
|
||||
except queue.Full:
|
||||
pass # Skip this frame if queue is full
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error in video preview: {e}")
|
||||
print(f"Fehler in der Kamera-Verarbeitung: {e}")
|
||||
time.sleep(0.1)
|
||||
|
||||
self.window.after(10, self.update_preview) # ~100 FPS preview
|
||||
|
||||
def show_product_selection(self, barcode_data):
|
||||
if hasattr(self, 'product_win') and self.product_win.winfo_exists():
|
||||
return
|
||||
|
||||
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] = self.quantities.get(prod, 0) + 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):
|
||||
def process_worker(self):
|
||||
"""Worker thread for barcode processing"""
|
||||
while self.running:
|
||||
try:
|
||||
barcode_data = self.queue.get(timeout=0.1)
|
||||
now = datetime.now()
|
||||
|
||||
# Rate limiting
|
||||
timestamps = self.barcode_times.get(barcode_data, [])
|
||||
timestamps = [t for t in timestamps if now - t <= timedelta(seconds=5)]
|
||||
if len(timestamps) >= 3:
|
||||
return
|
||||
continue
|
||||
timestamps.append(now)
|
||||
self.barcode_times[barcode_data] = timestamps
|
||||
|
||||
current_time = now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
current_time = now.strftime("%H:%M:%S")
|
||||
pfand_type = "EINWEG" if len(barcode_data) == 13 else "MEHRWEG" if len(barcode_data) == 8 else "DOSE"
|
||||
deposit = self.pfand_values.get(pfand_type, 0.00)
|
||||
|
||||
self.tree.insert("", 0, values=(current_time, barcode_data, pfand_type, f"{deposit:.2f}"))
|
||||
# Update UI in main thread
|
||||
self.window.after(0, self.add_to_treeview, current_time, barcode_data, pfand_type, deposit)
|
||||
|
||||
# Show product selection dialog
|
||||
if barcode_data not in self.prompted_barcodes:
|
||||
self.prompted_barcodes.add(barcode_data)
|
||||
self.window.after(0, self.show_product_selection, barcode_data)
|
||||
@@ -253,16 +503,258 @@ class PfandScanner:
|
||||
except queue.Empty:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Error in queue processing: {e}")
|
||||
finally:
|
||||
print(f"Fehler in der Warteschlangenverarbeitung: {e}")
|
||||
|
||||
def add_to_treeview(self, current_time, barcode_data, pfand_type, deposit):
|
||||
"""Add item to treeview (called from main thread)"""
|
||||
# Determine row color
|
||||
row_count = len(self.tree.get_children())
|
||||
tag = 'evenrow' if row_count % 2 == 0 else 'oddrow'
|
||||
|
||||
self.tree.insert("", 0, values=(current_time, barcode_data, pfand_type, f"€{deposit:.2f}"), tags=(tag,))
|
||||
|
||||
# Update statistics
|
||||
self.total_scans += 1
|
||||
self.update_statistics()
|
||||
|
||||
# Sound notification
|
||||
if self.beep_var.get():
|
||||
self.window.bell()
|
||||
|
||||
def update_preview(self):
|
||||
"""Update camera preview (called from main thread)"""
|
||||
try:
|
||||
# Get frame from queue
|
||||
frame = self.frame_queue.get_nowait()
|
||||
|
||||
# Convert and display
|
||||
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
|
||||
img = Image.fromarray(cv2image)
|
||||
|
||||
# Resize to fit label while maintaining aspect ratio
|
||||
label_width = self.camera_label.winfo_width()
|
||||
label_height = self.camera_label.winfo_height()
|
||||
if label_width > 1 and label_height > 1:
|
||||
img.thumbnail((label_width, label_height), Image.Resampling.LANCZOS)
|
||||
|
||||
imgtk = ImageTk.PhotoImage(image=img)
|
||||
self.camera_label.imgtk = imgtk
|
||||
self.camera_label.configure(image=imgtk)
|
||||
|
||||
except queue.Empty:
|
||||
pass # No new frame available
|
||||
except Exception as e:
|
||||
print(f"Fehler in der Video-Vorschau: {e}")
|
||||
|
||||
# Schedule next update
|
||||
if self.running:
|
||||
self.window.after(30, self.update_preview)
|
||||
|
||||
def update_statistics(self):
|
||||
session_time = datetime.now() - self.session_start
|
||||
hours, remainder = divmod(int(session_time.total_seconds()), 3600)
|
||||
minutes, _ = divmod(remainder, 60)
|
||||
time_str = f"{hours:02d}:{minutes:02d}"
|
||||
|
||||
self.session_time_label.configure(text=f"Sitzungsdauer: {time_str}")
|
||||
self.total_scans_label.configure(text=f"Gesamte Scans: {self.total_scans}")
|
||||
self.total_value_label.configure(text=f"Gesamtwert: €{self.total_value:.2f}")
|
||||
|
||||
def show_product_selection(self, barcode_data):
|
||||
if hasattr(self, 'product_win') and self.product_win.winfo_exists():
|
||||
return
|
||||
|
||||
self.product_win = tk.Toplevel(self.window)
|
||||
self.product_win.title("Produkt auswählen")
|
||||
self.product_win.geometry("500x400")
|
||||
self.product_win.minsize(450, 350)
|
||||
self.product_win.resizable(True, True)
|
||||
|
||||
# Center the window
|
||||
self.product_win.transient(self.window)
|
||||
self.product_win.grab_set()
|
||||
|
||||
# Configure grid weights for proper expansion
|
||||
self.product_win.columnconfigure(0, weight=1)
|
||||
self.product_win.rowconfigure(1, weight=1)
|
||||
|
||||
# Header frame
|
||||
header_frame = ttk.Frame(self.product_win, padding="10")
|
||||
header_frame.grid(row=0, column=0, sticky="ew", padx=10, pady=(10, 5))
|
||||
header_frame.columnconfigure(0, weight=1)
|
||||
|
||||
ttk.Label(header_frame, text="Produkt für Barcode auswählen:",
|
||||
style='Heading.TLabel').grid(row=0, column=0, sticky="w")
|
||||
ttk.Label(header_frame, text=f"'{barcode_data}'",
|
||||
style='Info.TLabel', font=('Courier', 10)).grid(row=1, column=0, sticky="w", pady=(0, 5))
|
||||
|
||||
# Main content frame with scrollable area
|
||||
content_frame = ttk.Frame(self.product_win)
|
||||
content_frame.grid(row=1, column=0, sticky="nsew", padx=10, pady=5)
|
||||
content_frame.columnconfigure(0, weight=1)
|
||||
content_frame.rowconfigure(0, weight=1)
|
||||
|
||||
selected_product = tk.StringVar()
|
||||
|
||||
# Create a canvas with scrollbar for the product list
|
||||
# Create a canvas with scrollbar for the product list
|
||||
canvas = tk.Canvas(content_frame, bg='white', highlightthickness=0)
|
||||
scrollbar = ttk.Scrollbar(content_frame, orient="vertical", command=canvas.yview)
|
||||
canvas.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
# Frame inside canvas
|
||||
scrollable_frame = ttk.Frame(canvas)
|
||||
window_id = canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
||||
|
||||
# Update scrollregion when frame contents change
|
||||
scrollable_frame.bind(
|
||||
"<Configure>",
|
||||
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
||||
)
|
||||
|
||||
# Resize inner frame width when canvas resizes
|
||||
def on_canvas_configure(event):
|
||||
canvas.itemconfig(window_id, width=event.width)
|
||||
|
||||
canvas.bind("<Configure>", on_canvas_configure)
|
||||
|
||||
# Place canvas + scrollbar
|
||||
canvas.grid(row=0, column=0, sticky="nsew")
|
||||
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||
|
||||
# Add products to scrollable frame
|
||||
if self.products:
|
||||
for i, product in enumerate(self.products):
|
||||
current_quantity = self.quantities.get(product, 0)
|
||||
price = self.prices.get(product, 0.00)
|
||||
|
||||
product_frame = ttk.Frame(scrollable_frame, padding="5")
|
||||
product_frame.pack(fill="x", pady=2)
|
||||
product_frame.columnconfigure(1, weight=1)
|
||||
|
||||
ttk.Radiobutton(
|
||||
product_frame,
|
||||
text=product,
|
||||
variable=selected_product,
|
||||
value=product
|
||||
).grid(row=0, column=0, sticky="w")
|
||||
|
||||
info_text = f"Aktuell: {current_quantity}, Preis: €{price:.2f}"
|
||||
ttk.Label(product_frame, text=info_text, style='Info.TLabel').grid(row=0, column=1, sticky="e", padx=(10, 0))
|
||||
else:
|
||||
no_products_label = ttk.Label(scrollable_frame, text="Noch keine Produkte definiert.",
|
||||
style='Info.TLabel', justify="center")
|
||||
no_products_label.pack(pady=20)
|
||||
|
||||
# Button frame at the bottom
|
||||
button_frame = ttk.Frame(self.product_win, padding="10")
|
||||
button_frame.grid(row=2, column=0, sticky="ew", padx=10, pady=(5, 10))
|
||||
|
||||
# Configure button frame columns for proper spacing
|
||||
button_frame.columnconfigure(0, weight=1)
|
||||
button_frame.columnconfigure(1, weight=0)
|
||||
button_frame.columnconfigure(2, weight=0)
|
||||
button_frame.columnconfigure(3, weight=0)
|
||||
|
||||
# Helper functions for buttons
|
||||
def confirm():
|
||||
product = selected_product.get()
|
||||
if product:
|
||||
# Update quantity
|
||||
self.quantities[product] = self.quantities.get(product, 0) + 1
|
||||
|
||||
# Update total value with actual product price
|
||||
product_price = self.prices.get(product, 0.00)
|
||||
self.total_value += product_price
|
||||
|
||||
self.save_json()
|
||||
self.update_statistics()
|
||||
self.product_win.destroy()
|
||||
|
||||
# Show confirmation message
|
||||
messagebox.showinfo("Erfolgreich", f"Produkt '{product}' wurde hinzugefügt!")
|
||||
else:
|
||||
messagebox.showwarning("Keine Auswahl", "Bitte wählen Sie ein Produkt aus.")
|
||||
|
||||
def cancel():
|
||||
self.product_win.destroy()
|
||||
|
||||
def add_new_product():
|
||||
new_product = simpledialog.askstring("Neues Produkt", "Name des neuen Produkts:")
|
||||
if new_product and new_product.strip():
|
||||
new_product = new_product.strip()
|
||||
if new_product not in self.products:
|
||||
# Ask for price
|
||||
price_str = simpledialog.askstring("Preis", f"Preis für '{new_product}' (€):")
|
||||
try:
|
||||
price = float(price_str.replace(',', '.')) if price_str else 0.00
|
||||
except:
|
||||
price = 0.00
|
||||
|
||||
self.products.append(new_product)
|
||||
self.prices[new_product] = price
|
||||
|
||||
# Save products
|
||||
try:
|
||||
products_data = {"products": self.products, "prices": self.prices}
|
||||
with open(self.products_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(products_data, f, indent=4, ensure_ascii=False)
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Speichern der Produkte: {e}")
|
||||
|
||||
# Refresh the dialog
|
||||
self.product_win.destroy()
|
||||
self.window.after(0, self.show_product_selection, barcode_data)
|
||||
else:
|
||||
messagebox.showwarning("Produkt existiert", "Dieses Produkt ist bereits vorhanden.")
|
||||
|
||||
# Buttons with proper layout
|
||||
ttk.Button(button_frame, text="Neues Produkt", command=add_new_product).grid(row=0, column=0, sticky="w", padx=5)
|
||||
ttk.Button(button_frame, text="Abbrechen", command=cancel).grid(row=0, column=2, padx=5)
|
||||
ttk.Button(button_frame, text="Bestätigen", command=confirm).grid(row=0, column=3, padx=5)
|
||||
|
||||
# Bind Enter key to confirm
|
||||
self.product_win.bind('<Return>', lambda e: confirm())
|
||||
self.product_win.bind('<Escape>', lambda e: cancel())
|
||||
|
||||
# Set focus to the window
|
||||
self.product_win.focus_set()
|
||||
|
||||
def process_queue(self):
|
||||
try:
|
||||
# This method is now handled by the process_worker thread
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Fehler in der Warteschlangenverarbeitung: {e}")
|
||||
|
||||
# Schedule next check
|
||||
if self.running:
|
||||
self.window.after(100, self.process_queue)
|
||||
|
||||
def on_closing(self):
|
||||
if self.cap and self.cap.isOpened():
|
||||
self.running = False
|
||||
|
||||
# Wait for camera thread to finish
|
||||
if self.camera_thread and self.camera_thread.is_alive():
|
||||
self.camera_thread.join(timeout=1.0)
|
||||
|
||||
if self.process_thread and self.process_thread.is_alive():
|
||||
self.process_thread.join(timeout=1.0)
|
||||
|
||||
try:
|
||||
if hasattr(self, 'cap') and self.cap and self.cap.isOpened():
|
||||
self.cap.release()
|
||||
cv2.destroyAllWindows()
|
||||
except:
|
||||
pass
|
||||
self.window.destroy()
|
||||
|
||||
if __name__ != "__main__":
|
||||
def launch_pfand_scanner():
|
||||
scanner_window = tk.Toplevel()
|
||||
PfandScanner(scanner_window, "µScan V2.2.2")
|
||||
PfandScanner(scanner_window, "µScan V2.3.5")
|
||||
else:
|
||||
# For standalone testing
|
||||
root = tk.Tk()
|
||||
app = PfandScanner(root, "µScan V2.3.5")
|
||||
root.mainloop()
|
||||
@@ -2,6 +2,10 @@
|
||||
# #
|
||||
# AUTOGENERATED BY SETUPKEYTOOL V1.0.03 #
|
||||
# THIS IS SENSITIVE INFORMATION #
|
||||
# #
|
||||
# THIS IS FILLED IN BY THE #
|
||||
# GUI ASSISTANT #
|
||||
# #
|
||||
# @ZockerKatze/@rattatwinko #
|
||||
# #
|
||||
#########################################
|
||||
@@ -11,4 +15,4 @@ from tgtg import TgtgClient
|
||||
# Don't ever give this Info to someone you dont trust!
|
||||
# This is the Private Key to your TGTG Account
|
||||
|
||||
client = TgtgClient(access_token="asdf", refresh_token="asdf", cookie="asdf")
|
||||
client = TgtgClient(access_token="", refresh_token="", cookie="")
|
||||
|
||||
@@ -61,10 +61,10 @@ def ask_for_tokens():
|
||||
|
||||
# Create Tkinter window
|
||||
root = tk.Tk()
|
||||
root.title("Enter API Credentials")
|
||||
root.title("API Kredenzen")
|
||||
|
||||
|
||||
title_label = tk.Label(root, text="Enter your API Credentials", font=("Arial", 14, "bold"))
|
||||
title_label = tk.Label(root, text="Gebe deine API Kredenzen ein!", font=("Arial", 14, "bold"))
|
||||
title_label.grid(row=0, columnspan=2, pady=10)
|
||||
|
||||
# Add labels and entry fields for the tokens and cookie
|
||||
@@ -81,7 +81,7 @@ def ask_for_tokens():
|
||||
cookie_entry.grid(row=3, column=1)
|
||||
|
||||
# Submit button to process the tokens
|
||||
submit_button = tk.Button(root, text="Submit", command=submit_tokens)
|
||||
submit_button = tk.Button(root, text="Speichern", command=submit_tokens)
|
||||
submit_button.grid(row=4, columnspan=2)
|
||||
|
||||
# Keep the window on top
|
||||
|
||||
@@ -1,72 +1,65 @@
|
||||
|
||||
|
||||
|
||||
class todo:
|
||||
def __init__(self):
|
||||
# ------------------------------------------------------------------------------------------------------------------#
|
||||
self.todo: str = """
|
||||
|
||||
TODO:
|
||||
[X] main.py@433 - fix this to actually load the Todo List cause currently its dumb as fuck.
|
||||
- Do this tmrw 11.JUN.25! easy task. at least it should be.
|
||||
[] main.py@GENERAL - refactor to make the ChatGPT Code actually readable.
|
||||
[] @Project_Structure - Fix this shit
|
||||
|
||||
[ ] main.py@GENERAL - refactor to make the ChatGPT Code actually readable.
|
||||
[x] main.py@TODO - Refactor and make it look nicer! PRIO: HIGH
|
||||
[ ] @Project_Structure - Fix this shit
|
||||
- Isnt too bad , we can work with the structure.
|
||||
"""
|
||||
# ------------------------------------------------------------------------------------------------------------------#
|
||||
self.archived_todo: str = """
|
||||
archived_todo list:
|
||||
[X] main.py@433 - fix this to actually load the Todo List cause currently its dumb as fuck.
|
||||
[X] main.py@433 - {FINISHED: 12.JUN.25@21:20} - fix this to actually load the Todo List cause currently its dumb as fuck.
|
||||
- Do this tmrw 11.JUN.25! easy task. at least it should be.
|
||||
|
||||
[X] @ImportStructure - {FINISHED: 13.SEP.25@18:47} - Fix the Import structure, it currently looks like ass. At least the local ImportStructure.
|
||||
- @LocalImportStructure => Fix by end of next week. (KW25)
|
||||
"""
|
||||
# ------------------------------------------------------------------------------------------------------------------#
|
||||
self.pineapple = """
|
||||
⠀⠀⠀⠀⠀⢀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⡿⠛⣷⡄⠀⠀⠀⠀⠀⢀⣀⣤⣤⣶⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠘⠻⣿⣿⢶⣤⣀⠀⠀⠀⢀⣾⠟⠀⠀⠘⢿⡆⠀⣠⣴⠿⣻⣿⠟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠻⣷⡈⠻⣷⣄⠀⣼⡟⠀⠀⠀⠀⠸⣷⣼⠟⠁⣴⠿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⢰⣤⣄⣀⠀⠘⣿⡀⠈⠻⣷⡿⠀⠀⠀⠀⠀⠀⢹⡇⠀⣸⡟⠀⠀⠀⣀⣠⣤⣶⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠉⠻⣿⣟⠿⣿⣿⡄⠀⢸⡇⠀⠀⣤⣿⣦⠀⠸⣿⢰⣿⣤⣴⠿⠛⣻⡿⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠙⢷⣄⠉⠛⢷⣼⡇⠀⣴⠟⠁⢻⣧⡀⣿⡾⠟⠁⠀⣠⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣧⠀⠀⠙⢷⣾⡟⠀⠀⠀⢻⣿⠟⠁⠀⢀⣾⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⢀⣤⣀⠀⠀⢻⣧⠀⠀⢸⣿⠀⠀⠀⠀⠀⢿⡆⠀⢀⣾⠋⠀⢐⣠⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠙⠻⣿⣦⣅⣿⡆⠀⢸⡿⠀⠀⣀⠀⠀⢸⣇⠀⣼⣯⣴⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣝⠻⣷⣄⢸⡇⢀⣾⠿⣷⡀⠘⣿⣴⠟⠋⣴⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢈⣿⣏⠉⠹⣿⣇⣾⠏⠀⠹⣧⣰⡿⠃⢀⣾⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣶⣤⣿⣆⠀⠘⢿⣿⠀⠀⠀⢿⡏⠀⢀⣾⣧⣶⣾⣿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⡝⠻⢷⣤⣸⡧⠀⠀⠀⢸⣧⣴⠟⢋⣴⡟⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣿⡄⠀⠙⠿⠷⠀⠀⠀⣿⠟⠁⢠⣾⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⡶⢿⡿⣿⠿⠿⠿⠿⠿⠿⣿⣷⠿⠾⢿⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⣠⣴⠟⠋⠀⠀⣽⣏⠀⠀⠀⠀⠀⠀⣸⢷⣄⠀⠀⠉⠛⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⢀⣶⠻⡅⠀⠀⣠⡞⠁⠙⢧⠀⠀⠀⠀⡼⠃⠀⠙⢷⣄⠀⣼⠁⠙⢷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⣰⡿⠁⠀⢿⣄⡾⠋⠀⠀⠀⠈⢳⣄⢀⡾⠁⠀⠀⠀⠀⠙⣷⣏⠀⠀⠈⠻⣧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⣼⡟⠀⠀⣀⡶⢻⡀⠀⠀⠀⠀⠀⠀⣹⣿⡀⠀⠀⠀⠀⠀⢠⡟⠈⠛⢦⣀⠀⢹⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⣼⣟⠀⣠⡴⠋⠀⠀⠙⣦⠀⠀⠀⢀⡴⠋⠀⠙⢦⡀⠀⠀⣰⠟⠀⠀⠀⠀⠉⠛⣾⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⣰⣿⡽⣟⠁⠀⠀⠀⠀⠀⠈⢷⣄⡴⠋⠀⠀⠀⠀⠀⠙⢦⣴⠋⠀⠀⠀⠀⠀⠀⢰⡏⠀⢹⣷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⢀⣿⠃⠀⠸⣦⠀⠀⠀⠀⠀⢀⡴⠿⢧⡀⠀⠀⠀⠀⠀⢀⡼⠙⠳⢤⡀⠀⠀⠀⢠⡏⠀⠀⠈⢿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⣸⡟⠀⠀⠀⠘⢧⡀⠀⢀⡴⠋⠀⠀⠈⠹⣦⡀⠀⢀⣴⠏⠁⠀⠀⠀⠙⠲⢤⣠⡏⠀⠀⠀⠀⠸⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⣿⠃⠀⠀⠀⠀⢈⣿⣾⠋⠀⠀⠀⠀⠀⠀⠀⠛⣦⡞⠁⠀⠀⠀⠀⠀⠀⠀⣤⠟⠓⠦⣤⣀⠀⠀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⣿⠀⠀⣀⡤⠖⠋⠀⠙⢷⡀⠀⠀⠀⠀⠀⣠⠞⠁⠙⠳⣤⡀⠀⠀⠀⢀⡼⠋⠀⠀⠀⠀⠈⠙⣳⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⣿⠷⣏⠉⠀⠀⠀⠀⠀⠀⠙⢦⡀⢀⣰⠞⠁⠀⠀⠀⠀⠈⠙⠶⢤⣠⠟⠀⠀⠀⠀⠀⠀⠀⣰⠏⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⣿⠀⠹⣆⠀⠀⠀⠀⠀⠀⢀⣠⠟⢯⣄⠀⠀⠀⠀⠀⠀⠀⠀⣠⠞⠉⠓⢶⣄⡀⠀⠀⠀⡼⠃⠀⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⣿⡆⠀⠈⢳⡄⠀⣀⣤⠞⠋⠁⠀⠀⠉⠳⣄⡀⠀⠀⠀⣠⡞⠃⠀⠀⠀⠀⠀⠉⠛⢳⣾⣥⣀⡀⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⢸⣷⣀⣀⡤⠾⢿⣁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢳⣤⣞⠃⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⠀⠀⠀⢹⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⣿⡏⠁⠀⠀⠀⠙⢶⣀⠀⠀⠀⠀⠀⢀⣠⠶⠋⠁⠉⠳⠦⣄⣀⠀⠀⠀⣠⠟⠀⠀⠀⠀⢀⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠹⣷⡄⠀⠀⠀⠀⠀⠉⠳⣦⣀⣤⠖⠋⠁⠀⠀⠀⠀⠀⠀⠀⠉⢙⣶⢾⣥⣀⠀⠀⠀⠀⣼⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠹⣧⡀⠀⠀⠀⣠⣤⠖⠛⠙⠳⣤⡀⠀⠀⠀⠀⠀⠀⠀⢀⡴⠋⠀⠀⠀⠈⠙⠛⢳⣾⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠹⣷⣶⡛⠉⠁⠀⠀⠀⠀⠀⠀⠉⠳⠦⣄⡀⣀⡴⠞⠉⠀⠀⠀⠀⠀⠀⢀⡴⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠙⢿⣙⢦⣄⡀⠀⠀⠀⠀⠀⠀⢀⣀⡼⠛⠛⠶⢤⣀⣀⠀⠀⠀⣀⡴⢋⣼⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠈⠻⣦⡈⠙⢦⣄⣀⣠⡴⠚⠋⠁⠀⠀⠀⠀⠀⠈⠉⢛⡶⠾⣧⣴⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠈⠻⣷⣾⡛⠙⠳⢦⣄⡀⠀⠀⠀⠀⢀⣠⡴⠚⢉⣠⣴⠟⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠻⠷⣦⣄⣀⣉⣻⣶⢴⣞⣋⣠⣴⡶⠟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠛⠛⠛⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
"""
|
||||
self.changelog: str = """
|
||||
Changelog:
|
||||
13.SEP.25@18:49 => Fixed µScan.
|
||||
- Fixed the UI, made it look better and made the Scanner run better.
|
||||
- Mainly OpenCV as a C extern
|
||||
- Tkinter pulls it together.
|
||||
- Its Threaded, so it runs better.
|
||||
|
||||
Fixed Todo Widget to look better. You can see @main:PfandCalculator/create_todo_list()
|
||||
- Now has a progressbar which counts the elements in the Todo variable, if you check one off [x]. then it counts as finished and the
|
||||
progressbar advances! main.py@PfandCalculator/create_todo_list/return_elements(str) -> tuple[int, int, int]
|
||||
Third returned Element being The percent out of 100, which is done.
|
||||
12.JUN.25@21:23 => Changed the Todo List to be much more readable.
|
||||
12.JUN.25@21:20 => Fixed Todo List Load Bug. Now loads properly. It was a import issue.
|
||||
"""
|
||||
# ------------------------------------------------------------------------------------------------------------------#
|
||||
self.tldr : str = """
|
||||
Quick TLDR:
|
||||
If you see any "ref" in the main.py file you are probably looking at a reference to a certain file.
|
||||
example: ref@todo/main.py , element int(x)
|
||||
This is refering to the todo list with element of X
|
||||
|
||||
# This Project is certainly older than mucapy , But still i declare types now. in todo code xD
|
||||
# Anyways. This function maybe needs fixing. due too toodoo bug (bug1)
|
||||
This can be for other things too, such as other Methods in Classes.
|
||||
example: ref@tgtg_orderchecker/main.py@method/class/variable
|
||||
This is refering to some object.
|
||||
|
||||
if you see any "@" , you are probably seeing a reference to the todo list.
|
||||
for example element 3 in todo (@LocalImportStructure)
|
||||
"""
|
||||
# ---------------------------------------------------------------------------------------------------------------------#
|
||||
|
||||
# Methods to return the things we want.
|
||||
def load_todo(self) -> str:
|
||||
return self.todo
|
||||
|
||||
def load_changelog(self) -> str:
|
||||
return self.changelog
|
||||
|
||||
def load_tldr(self) -> str:
|
||||
return self.tldr
|
||||
|
||||
# toodoo bug may also reside here. idk
|
||||
todo_instance = todo()
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import tempfile
|
||||
import traceback
|
||||
import threading
|
||||
|
||||
GITHUB_REPO_ZIP = "https://github.com/ZockerKatze/pfand_PKG/archive/refs/heads/main.zip"
|
||||
GITHUB_REPO_ZIP = "https://rattatwinko.servecounterstrike.com/gitea/rattatwinko/pfand_PKG/archive/main.zip"
|
||||
IGNORED_FILES = {"key.py"}
|
||||
|
||||
class GitHubUpdater(tk.Toplevel):
|
||||
@@ -49,7 +49,7 @@ class GitHubUpdater(tk.Toplevel):
|
||||
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 = ttk.Label(self, text="Suche nach Updates …", style="Status.TLabel")
|
||||
self.status_label.pack(pady=(0, 10))
|
||||
|
||||
self.frame = ttk.Frame(self)
|
||||
@@ -73,14 +73,14 @@ class GitHubUpdater(tk.Toplevel):
|
||||
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 = 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 = 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 = ttk.Button(self, text="Fehlerdetails anzeigen", command=self.toggle_debug_output)
|
||||
self.toggle_debug_btn.pack()
|
||||
self.toggle_debug_btn.pack_forget()
|
||||
|
||||
@@ -93,10 +93,10 @@ class GitHubUpdater(tk.Toplevel):
|
||||
self.debug_visible = not self.debug_visible
|
||||
if self.debug_visible:
|
||||
self.debug_output.pack()
|
||||
self.toggle_debug_btn.config(text="🔽 Fehlerdetails verbergen")
|
||||
self.toggle_debug_btn.config(text="Fehlerdetails verbergen")
|
||||
else:
|
||||
self.debug_output.pack_forget()
|
||||
self.toggle_debug_btn.config(text="🐞 Fehlerdetails anzeigen")
|
||||
self.toggle_debug_btn.config(text="Fehlerdetails anzeigen")
|
||||
|
||||
def show_root_view(self):
|
||||
self.current_view = "root"
|
||||
@@ -125,7 +125,7 @@ class GitHubUpdater(tk.Toplevel):
|
||||
|
||||
def check_for_updates(self):
|
||||
try:
|
||||
self.status_label.config(text="⬇️ Lade Update herunter...", foreground="#ffb300")
|
||||
self.status_label.config(text="Lade Update herunter...", foreground="#ffb300")
|
||||
self.update_idletasks()
|
||||
|
||||
response = requests.get(GITHUB_REPO_ZIP)
|
||||
@@ -137,13 +137,13 @@ class GitHubUpdater(tk.Toplevel):
|
||||
|
||||
if self.file_differences:
|
||||
self.structure = self.build_structure(self.file_differences)
|
||||
self.status_label.config(text="⚠️ Updates verfügbar", foreground="#e53935")
|
||||
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")
|
||||
self.status_label.config(text="Alles ist aktuell", foreground="#43a047")
|
||||
except Exception:
|
||||
self.status_label.config(text="❌ Fehler beim Laden", foreground="#e53935")
|
||||
self.status_label.config(text="Fehler beim Laden", foreground="#e53935")
|
||||
self.toggle_debug_btn.pack()
|
||||
self.debug_output.insert("1.0", traceback.format_exc())
|
||||
|
||||
@@ -183,7 +183,7 @@ class GitHubUpdater(tk.Toplevel):
|
||||
|
||||
def perform_update(self):
|
||||
self.update_button.config(state='disabled')
|
||||
self.status_label.config(text="🚧 Update läuft...", foreground="#fb8c00")
|
||||
self.status_label.config(text="Update läuft…", foreground="#fb8c00")
|
||||
self.update_idletasks()
|
||||
|
||||
try:
|
||||
@@ -199,10 +199,10 @@ class GitHubUpdater(tk.Toplevel):
|
||||
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
||||
shutil.copy2(src_path, dest_path)
|
||||
|
||||
messagebox.showinfo("✅ Aktualisiert", "Dateien wurden erfolgreich aktualisiert.")
|
||||
messagebox.showinfo("Aktualisiert", "Dateien wurden erfolgreich aktualisiert.")
|
||||
self.destroy()
|
||||
except Exception as e:
|
||||
messagebox.showerror("❌ Fehler", str(e))
|
||||
messagebox.showerror("Fehler", str(e))
|
||||
self.toggle_debug_btn.pack()
|
||||
self.debug_output.insert("1.0", traceback.format_exc())
|
||||
|
||||
@@ -238,7 +238,7 @@ def run_silent_update(master=None):
|
||||
file_differences.append(rel_path)
|
||||
|
||||
if file_differences:
|
||||
result = messagebox.askyesno("🔄 Update verfügbar", "Es sind Updates verfügbar. Möchten Sie aktualisieren?")
|
||||
result = messagebox.askyesno("Update verfügbar", "Es sind Updates verfügbar. Möchten Sie aktualisieren?")
|
||||
if result:
|
||||
updater = GitHubUpdater(master)
|
||||
updater.grab_set()
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"products": ["Flaschen", "Bierflasche", "Kasten", "Dose", "Plastikflasche", "Monster", "Joghurt Glas"], "prices": {"Flaschen": 0.25, "Bierflasche": 0.2, "Kasten": 3.0, "Dose": 0.25, "Plastikflasche": 0.25, "Monster": 0.25, "Joghurt Glas": 0.17}}
|
||||
{"products": ["Flaschen", "Bierflasche", "Kasten", "Dose", "Plastikflasche", "Joghurt Glas"], "prices": {"Flaschen": 0.25, "Bierflasche": 0.2, "Kasten": 3.0, "Dose": 0.25, "Plastikflasche": 0.25, "Joghurt Glas": 0.17}}
|
||||
Reference in New Issue
Block a user