Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3280827aac | |||
| e4a1681ede | |||
| b4ff04112a | |||
| 0b7eff5f41 | |||
| e32ae07bd0 | |||
| a6bece502e | |||
| 6eb653e595 | |||
| 354929ef21 | |||
| 095d130ccc | |||
| 55ac8a128f |
64
LICENSE
64
LICENSE
@@ -1,29 +1,47 @@
|
|||||||
MIT License
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
Copyright (c) 2026 Survivalful (https://Survivalful.de/)
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Preamble
|
||||||
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
|
The GNU General Public License is a free, copyleft license for
|
||||||
copies or substantial portions of the Software.
|
software and other kinds of works.
|
||||||
|
|
||||||
Any use of this Software, in whole or in part, must visibly credit the original
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
author: Survivalful (https://Survivalful.de/)
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
This credit must appear in at least one of the following:
|
Some devices are designed to deny users access to install or run
|
||||||
- The application's user interface (e.g. About screen or footer)
|
modified versions of the software inside them, although the manufacturer
|
||||||
- The project's README or documentation
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
- The source code as a comment
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. Finally, any free program is threatened constantly by
|
||||||
|
software patents. We wish to ensure that a free program has all the
|
||||||
|
rights that its distributors can provide, so we will not allow patents to
|
||||||
|
restrict the program.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
The precise terms and conditions for copying, distribution and
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
modification follow.
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
TERMS AND CONDITIONS
|
||||||
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
|
0. Definitions.
|
||||||
SOFTWARE.
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to
|
||||||
|
|||||||
27
README.md
27
README.md
@@ -1,6 +1,9 @@
|
|||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# 🧮 Survivalcalc
|
# 🧮 Survivalcalc
|
||||||
|
|
||||||
A modern desktop calculator with advanced features for area and volume calculations, built with Python and Tkinter.
|
A modern desktop calculator with advanced features for area, volume, and electricity calculations, built with Python and Tkinter.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -9,13 +12,15 @@ A modern desktop calculator with advanced features for area and volume calculati
|
|||||||
* 🔢 **Standard Calculator** – Basic arithmetic with real-time computation
|
* 🔢 **Standard Calculator** – Basic arithmetic with real-time computation
|
||||||
* 📐 **Area Calculations** – Square, rectangle, circle, triangle, trapezoid, cylinder, sphere, cone
|
* 📐 **Area Calculations** – Square, rectangle, circle, triangle, trapezoid, cylinder, sphere, cone
|
||||||
* 📦 **Volume Calculations** – Cube, rectangular prism, cylinder, cone, sphere, tetrahedron, trapezoidal prism
|
* 📦 **Volume Calculations** – Cube, rectangular prism, cylinder, cone, sphere, tetrahedron, trapezoidal prism
|
||||||
|
* ⚡ **Electricity Calculations** – Voltage, current, resistance, and power with various formulas, plus a color band resistance calculator.
|
||||||
* 🌙 **Dark Mode** – Toggle between light and dark themes
|
* 🌙 **Dark Mode** – Toggle between light and dark themes
|
||||||
* 🌍 **Multi-Language Support** – German (DE) and English (EN)
|
* 🌍 **Multi-Language Support** – German (DE) and English (EN)
|
||||||
* 📏 **Unit System** – Support for mm, cm, m, km with automatic conversion
|
* 📏 **Unit System** – Support for pm, nm, μm, mm, cm, dm, m, km, Mm, Gm with automatic conversion
|
||||||
* 🔄 **Adjustable Rounding** – Configure decimal places (0–20)
|
* 🔄 **Adjustable Rounding** – Configure decimal places (0–20)
|
||||||
* 🕓 **Calculation History** – Track last 3 calculations with SQLite storage
|
* 🕓 **Calculation History** – Track last 3 calculations with SQLite storage
|
||||||
* ⚙️ **Persistent Settings** – All preferences saved automatically
|
* ⚙️ **Persistent Settings** – All preferences saved automatically
|
||||||
* ⌨️ **Keyboard Support** – Full keyboard shortcuts for calculator operations
|
* ⌨️ **Keyboard Support** – Full keyboard shortcuts for calculator operations
|
||||||
|
* 🔄 **Automatic Updates** – Notifies you when a new version is available.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -48,6 +53,14 @@ A modern desktop calculator with advanced features for area and volume calculati
|
|||||||
5. Select your units
|
5. Select your units
|
||||||
6. Results update automatically
|
6. Results update automatically
|
||||||
|
|
||||||
|
### Electricity Calculations
|
||||||
|
1. Open **Shapes** menu
|
||||||
|
2. Select **Electricity**
|
||||||
|
3. Choose the quantity to calculate (e.g., Voltage, Current)
|
||||||
|
4. Select the formula you want to use (e.g., U = I · R)
|
||||||
|
5. Enter your values
|
||||||
|
6. Results update automatically
|
||||||
|
|
||||||
### Settings
|
### Settings
|
||||||
- Toggle dark mode
|
- Toggle dark mode
|
||||||
- Enable/disable rounding
|
- Enable/disable rounding
|
||||||
@@ -71,7 +84,7 @@ A modern desktop calculator with advanced features for area and volume calculati
|
|||||||
* **OS**: Windows 7 or later
|
* **OS**: Windows 7 or later
|
||||||
* **RAM**: 50 MB minimum
|
* **RAM**: 50 MB minimum
|
||||||
* **Disk Space**: 10 MB
|
* **Disk Space**: 10 MB
|
||||||
* **Internet**: Required for initial download only
|
* **Internet**: Required for initial download and update checks
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -95,26 +108,24 @@ A modern desktop calculator with advanced features for area and volume calculati
|
|||||||
2. Find **Survivalcalc**
|
2. Find **Survivalcalc**
|
||||||
3. Click **Uninstall**
|
3. Click **Uninstall**
|
||||||
|
|
||||||
Or run **Survivalcalc_Uninstaller.exe** directly.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📬 Support
|
## 📬 Support
|
||||||
|
|
||||||
* 🌐 **Website**: [Github](https://survivalful.de/survivalcalc/)
|
* 🌐 **Website**: [https://Survivalful.de](https://survivalful.de)
|
||||||
* 📧 **Email**: [team@survivalful.de](mailto:team@survivalful.de)
|
* 📧 **Email**: [team@survivalful.de](mailto:team@survivalful.de)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📜 License
|
## 📜 License
|
||||||
|
|
||||||
MIT License © 2026 Survivalful
|
GPL License © 2026 Survivalful
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 Version Info
|
## 📊 Version Info
|
||||||
|
|
||||||
* **Version**: 1.0.0
|
* **Version**: 1.0.2
|
||||||
* **Release**: 24.03.2026
|
* **Release**: 24.03.2026
|
||||||
* **Status**: Stable
|
* **Status**: Stable
|
||||||
|
|
||||||
|
|||||||
292
main.py
292
main.py
@@ -14,7 +14,6 @@ import webbrowser
|
|||||||
VERSION_URL = "https://cloud.survivalful.de/s/ydLwqwXdJEj8XwR/download"
|
VERSION_URL = "https://cloud.survivalful.de/s/ydLwqwXdJEj8XwR/download"
|
||||||
|
|
||||||
def fetch_version_info():
|
def fetch_version_info():
|
||||||
"""Holt die version.json vom Server. Gibt None bei Fehler zurück."""
|
|
||||||
try:
|
try:
|
||||||
with urllib.request.urlopen(VERSION_URL, timeout=5) as response:
|
with urllib.request.urlopen(VERSION_URL, timeout=5) as response:
|
||||||
return json.loads(response.read().decode())
|
return json.loads(response.read().decode())
|
||||||
@@ -22,7 +21,6 @@ def fetch_version_info():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def version_is_newer(latest, current):
|
def version_is_newer(latest, current):
|
||||||
"""Vergleicht zwei Versionsstrings wie '1.2.0' > '1.0.0'."""
|
|
||||||
try:
|
try:
|
||||||
l_parts = list(map(int, latest.split('.')))
|
l_parts = list(map(int, latest.split('.')))
|
||||||
c_parts = list(map(int, current.split('.')))
|
c_parts = list(map(int, current.split('.')))
|
||||||
@@ -129,7 +127,7 @@ LANG = {
|
|||||||
"result_current": "Strom I",
|
"result_current": "Strom I",
|
||||||
"result_resistance": "Wiederstand R",
|
"result_resistance": "Wiederstand R",
|
||||||
"voltage": "Spannung",
|
"voltage": "Spannung",
|
||||||
"ampere": "Strom",
|
"current": "Strom",
|
||||||
"resistance": "Widerstand",
|
"resistance": "Widerstand",
|
||||||
"power": "Leistung",
|
"power": "Leistung",
|
||||||
"u": "Spannung U",
|
"u": "Spannung U",
|
||||||
@@ -267,17 +265,39 @@ def t(key, **kwargs):
|
|||||||
pass
|
pass
|
||||||
return text
|
return text
|
||||||
|
|
||||||
app_version = "1.0.0"
|
def get_install_date():
|
||||||
|
"""Liest das echte Installationsdatum aus der Registry (vom Installer gespeichert)."""
|
||||||
|
try:
|
||||||
|
import winreg
|
||||||
|
key_path = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Survivalcalc"
|
||||||
|
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path)
|
||||||
|
install_date_str = winreg.QueryValueEx(key, "InstallDate")[0]
|
||||||
|
winreg.CloseKey(key)
|
||||||
|
return install_date_str
|
||||||
|
except Exception:
|
||||||
|
# Fallback: Nutze die Änderungszeit der Anwendung
|
||||||
|
try:
|
||||||
|
exe_path = os.path.join(os.environ.get("LOCALAPPDATA", os.path.expanduser("~")), "SurvivalCalc", "main.exe")
|
||||||
|
if os.path.exists(exe_path):
|
||||||
|
mtime = os.path.getmtime(exe_path)
|
||||||
|
return datetime.datetime.fromtimestamp(mtime).strftime("%d.%m.%Y %H:%M")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# Fallback: Aktuelles Datum
|
||||||
|
return datetime.datetime.now().strftime("%d.%m.%Y %H:%M")
|
||||||
|
|
||||||
|
|
||||||
|
app_version = "1.0.2"
|
||||||
app_release = "24.3.2026"
|
app_release = "24.3.2026"
|
||||||
app_status = "Stable"
|
app_status = "Stable"
|
||||||
dev_e_mail = "team@survivalful.de"
|
dev_e_mail = "team@survivalful.de"
|
||||||
dev_website = "https://Survivalful.de/"
|
dev_website = "https://Survivalful.de/"
|
||||||
app_license = "MIT License © 2026 Survivalful"
|
app_license = "GPL License © 2026 Survivalful"
|
||||||
sys_system = platform.system()
|
sys_system = platform.system()
|
||||||
sys_version = platform.release()
|
sys_version = platform.release()
|
||||||
sys_machine = platform.machine()
|
sys_machine = platform.machine()
|
||||||
sys_python = platform.python_version()
|
sys_python = platform.python_version()
|
||||||
install_date = datetime.datetime.now().strftime("%d.%m.%Y %H:%M")
|
install_date = get_install_date()
|
||||||
|
|
||||||
DEFAULT_SETTINGS = {
|
DEFAULT_SETTINGS = {
|
||||||
"bg_dark": True,
|
"bg_dark": True,
|
||||||
@@ -350,37 +370,78 @@ load_settings()
|
|||||||
init_db()
|
init_db()
|
||||||
|
|
||||||
def check_for_updates():
|
def check_for_updates():
|
||||||
"""
|
|
||||||
Überprüft im Hintergrund auf Updates und zeigt bei Erfolg eine Nachricht an.
|
|
||||||
Diese Funktion wird in einem separaten Thread ausgeführt.
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
info = fetch_version_info()
|
info = fetch_version_info()
|
||||||
if not info:
|
if not info:
|
||||||
return
|
return
|
||||||
|
|
||||||
latest_version = info.get("version")
|
latest_version = info.get("version")
|
||||||
changelog = info.get("changelog", "Keine Informationen verfügbar.")
|
changelog = info.get("changelog", "Keine Informationen verfügbar.")
|
||||||
download_url = info.get("download_url")
|
download_url = info.get("installer_url")
|
||||||
update_info_url = info.get("update_info_url")
|
|
||||||
|
|
||||||
if latest_version and version_is_newer(latest_version, app_version):
|
if not (latest_version and version_is_newer(latest_version, app_version)):
|
||||||
|
return
|
||||||
def show_update_dialog():
|
|
||||||
title = t("update_available_title")
|
|
||||||
message = t("update_available_msg", LATEST=latest_version, CHANGELOG=changelog)
|
|
||||||
|
|
||||||
if messagebox.askyesno(title, message):
|
|
||||||
if update_info_url:
|
|
||||||
webbrowser.open(update_info_url)
|
|
||||||
|
|
||||||
root.after(0, show_update_dialog)
|
def show_update_dialog():
|
||||||
|
title = t("update_available_title")
|
||||||
|
message = t("update_available_msg", LATEST=latest_version, CHANGELOG=changelog)
|
||||||
|
|
||||||
|
if not messagebox.askyesno(title, message):
|
||||||
|
return
|
||||||
|
|
||||||
|
win = tk.Toplevel(root)
|
||||||
|
win.title("Update wird heruntergeladen...")
|
||||||
|
win.geometry("360x120")
|
||||||
|
win.resizable(False, False)
|
||||||
|
win.config(bg=bg)
|
||||||
|
win.grab_set()
|
||||||
|
|
||||||
|
tk.Label(win, text=f"Version {latest_version} wird heruntergeladen...",
|
||||||
|
bg=bg, fg=fg, font=("Arial", 11)).pack(pady=(18, 6))
|
||||||
|
|
||||||
|
progress = tk.DoubleVar()
|
||||||
|
bar = tk.Scale(win, variable=progress, from_=0, to=100,
|
||||||
|
orient="horizontal", state="disabled",
|
||||||
|
bg=bg, fg=fg, troughcolor="#2c2c2e",
|
||||||
|
highlightthickness=0, relief="flat", sliderrelief="flat")
|
||||||
|
bar.pack(fill="x", padx=24)
|
||||||
|
|
||||||
|
status_lbl = tk.Label(win, text="0%", bg=bg, fg="#888888", font=("Arial", 10))
|
||||||
|
status_lbl.pack()
|
||||||
|
|
||||||
|
def do_download():
|
||||||
|
try:
|
||||||
|
import tempfile
|
||||||
|
tmp_dir = tempfile.gettempdir()
|
||||||
|
installer_path = os.path.join(tmp_dir, "Survivalcalc_Installer.exe")
|
||||||
|
|
||||||
|
def reporthook(block_num, block_size, total_size):
|
||||||
|
if total_size > 0:
|
||||||
|
pct = min(100, block_num * block_size * 100 / total_size)
|
||||||
|
progress.set(pct)
|
||||||
|
status_lbl.config(text=f"{int(pct)}%")
|
||||||
|
win.update_idletasks()
|
||||||
|
|
||||||
|
urllib.request.urlretrieve(download_url, installer_path, reporthook)
|
||||||
|
|
||||||
|
win.destroy()
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
subprocess.Popen([installer_path])
|
||||||
|
root.quit()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
win.destroy()
|
||||||
|
messagebox.showerror("Fehler", f"Download fehlgeschlagen:\n{e}")
|
||||||
|
|
||||||
|
threading.Thread(target=do_download, daemon=True).start()
|
||||||
|
|
||||||
|
root.after(0, show_update_dialog)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Update-Check fehlgeschlagen: {e}")
|
print(f"Update-Check fehlgeschlagen: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
UNIT_FACTORS = {
|
UNIT_FACTORS = {
|
||||||
"pm": 1e-12, "nm": 1e-9, "μm": 1e-6, "mm": 0.001, "cm": 0.01, "dm": 0.1,
|
"pm": 1e-12, "nm": 1e-9, "μm": 1e-6, "mm": 0.001, "cm": 0.01, "dm": 0.1,
|
||||||
"m": 1.0, "km": 1000, "Mm": 1e6, "Gm": 1e9
|
"m": 1.0, "km": 1000, "Mm": 1e6, "Gm": 1e9
|
||||||
@@ -396,6 +457,27 @@ CURRENT_UNITS = ["pA", "nA", "μA", "mA", "A", "kA", "MA", "GA"]
|
|||||||
RESISTANCE_UNITS = ["pΩ", "nΩ", "μΩ", "mΩ", "Ω", "kΩ", "MΩ", "GΩ"]
|
RESISTANCE_UNITS = ["pΩ", "nΩ", "μΩ", "mΩ", "Ω", "kΩ", "MΩ", "GΩ"]
|
||||||
POWER_UNITS = ["pW", "nW", "μW", "mW", "W", "kW", "MW", "GW"]
|
POWER_UNITS = ["pW", "nW", "μW", "mW", "W", "kW", "MW", "GW"]
|
||||||
|
|
||||||
|
COLOR_DIGITS = {
|
||||||
|
"Black": 0, "Brown": 1, "Red": 2, "Orange": 3, "Yellow": 4,
|
||||||
|
"Green": 5, "Blue": 6, "Violet": 7, "Grey": 8, "White": 9
|
||||||
|
}
|
||||||
|
COLOR_MULTIPLIER = {
|
||||||
|
"Black": 1, "Brown": 10, "Red": 100, "Orange": 1_000, "Yellow": 10_000,
|
||||||
|
"Green": 100_000, "Blue": 1_000_000, "Violet": 10_000_000, "Grey": 100_000_000,
|
||||||
|
"White": 1_000_000_000, "Gold": 0.1, "Silver": 0.01
|
||||||
|
}
|
||||||
|
COLOR_TOLERANCE = {
|
||||||
|
"Brown": "±1%", "Red": "±2%", "Green": "±0.5%", "Blue": "±0.25%",
|
||||||
|
"Violet": "±0.1%", "Grey": "±0.05%", "Gold": "±5%", "Silver": "±10%",
|
||||||
|
"None": "±20%"
|
||||||
|
}
|
||||||
|
|
||||||
|
COLOR_HEX = {
|
||||||
|
"Black": "#000000", "Brown": "#8B4513", "Red": "#FF0000", "Orange": "#FFA500",
|
||||||
|
"Yellow": "#FFFF00", "Green": "#008000", "Blue": "#0000FF", "Violet": "#8A2BE2",
|
||||||
|
"Grey": "#808080", "White": "#FFFFFF", "Gold": "#D4AF37", "Silver": "#C0C0C0", "None": "#CCCCCC"
|
||||||
|
}
|
||||||
|
|
||||||
def half(first, last):
|
def half(first, last):
|
||||||
return first / last
|
return first / last
|
||||||
|
|
||||||
@@ -553,6 +635,7 @@ def rebuild_menu():
|
|||||||
resistance.add_command(label=t("r_u_p"), command=resistance_u_p)
|
resistance.add_command(label=t("r_u_p"), command=resistance_u_p)
|
||||||
resistance.add_command(label=t("r_p_i"), command=resistance_p_i)
|
resistance.add_command(label=t("r_p_i"), command=resistance_p_i)
|
||||||
electricity_menu.add_cascade(label=t("power"),menu=power)
|
electricity_menu.add_cascade(label=t("power"),menu=power)
|
||||||
|
electricity_menu.add_command(label="Widerstand (Farbcode)", command=resistance_color_bands)
|
||||||
power.add_command(label=t("u_i_r"), command=power_u_i)
|
power.add_command(label=t("u_i_r"), command=power_u_i)
|
||||||
power.add_command(label=t("u_r_p"), command=power_i_r)
|
power.add_command(label=t("u_r_p"), command=power_i_r)
|
||||||
power.add_command(label=t("r_p_i"), command=power_u_r)
|
power.add_command(label=t("r_p_i"), command=power_u_r)
|
||||||
@@ -1360,6 +1443,167 @@ def resistance_p_i():
|
|||||||
make_input_row(content_frame, t("i"), var_i, 3, unit_i, CURRENT_UNITS)
|
make_input_row(content_frame, t("i"), var_i, 3, unit_i, CURRENT_UNITS)
|
||||||
result = make_result_label(content_frame, 5)
|
result = make_result_label(content_frame, 5)
|
||||||
|
|
||||||
|
def resistance_color_bands():
|
||||||
|
clear_frame(); content_frame.columnconfigure(0, weight=1)
|
||||||
|
colors_digit = ["Black", "Brown", "Red", "Orange", "Yellow", "Green", "Blue", "Violet", "Grey", "White"]
|
||||||
|
colors_multiplier = ["Black", "Brown", "Red", "Orange", "Yellow", "Green", "Blue", "Violet", "Grey", "White", "Gold", "Silver"]
|
||||||
|
colors_tolerance = ["Brown", "Red", "Green", "Blue", "Violet", "Grey", "Gold", "Silver", "None"]
|
||||||
|
|
||||||
|
b1 = tk.StringVar(value="Brown")
|
||||||
|
b2 = tk.StringVar(value="Black")
|
||||||
|
b3 = tk.StringVar(value="Black")
|
||||||
|
b4 = tk.StringVar(value="Brown")
|
||||||
|
b5 = tk.StringVar(value="Gold")
|
||||||
|
ring_mode = tk.IntVar(value=4)
|
||||||
|
|
||||||
|
def format_value(r):
|
||||||
|
if r >= 1_000_000:
|
||||||
|
return f"{round(r/1_000_000, round_num)} MΩ"
|
||||||
|
if r >= 1_000:
|
||||||
|
return f"{round(r/1_000, round_num)} kΩ"
|
||||||
|
if r >= 1:
|
||||||
|
return f"{round(r, round_num)} Ω"
|
||||||
|
if r >= 0.001:
|
||||||
|
return f"{round(r*1000, round_num)} mΩ"
|
||||||
|
return f"{round(r*1_000_000, round_num)} μΩ"
|
||||||
|
|
||||||
|
def update_bands():
|
||||||
|
positions = bands_5 if ring_mode.get() == 5 else bands_4
|
||||||
|
vars_active = [b1, b2, b3, b4, b5] if ring_mode.get() == 5 else [b1, b2, b3, b5]
|
||||||
|
for idx, item in enumerate(band_items):
|
||||||
|
if idx < len(positions):
|
||||||
|
x1, y1, x2, y2 = positions[idx]
|
||||||
|
canvas.coords(item, x1, y1, x2, y2)
|
||||||
|
color_key = vars_active[idx].get()
|
||||||
|
canvas.itemconfig(item, fill=COLOR_HEX.get(color_key, "#000000"), state="normal")
|
||||||
|
else:
|
||||||
|
canvas.itemconfig(item, state="hidden")
|
||||||
|
|
||||||
|
def calc(*a):
|
||||||
|
try:
|
||||||
|
if ring_mode.get() == 4:
|
||||||
|
d1 = COLOR_DIGITS[b1.get()]
|
||||||
|
d2 = COLOR_DIGITS[b2.get()]
|
||||||
|
mul = COLOR_MULTIPLIER[b3.get()]
|
||||||
|
tol = COLOR_TOLERANCE[b5.get()]
|
||||||
|
r = (d1*10 + d2) * mul
|
||||||
|
else:
|
||||||
|
d1 = COLOR_DIGITS[b1.get()]
|
||||||
|
d2 = COLOR_DIGITS[b2.get()]
|
||||||
|
d3 = COLOR_DIGITS[b3.get()]
|
||||||
|
mul = COLOR_MULTIPLIER[b4.get()]
|
||||||
|
tol = COLOR_TOLERANCE[b5.get()]
|
||||||
|
r = (d1*100 + d2*10 + d3) * mul
|
||||||
|
update_bands()
|
||||||
|
result.config(text=f"R = {format_value(r)} {tol} ({r} Ω)")
|
||||||
|
except Exception:
|
||||||
|
result.config(text="")
|
||||||
|
|
||||||
|
for v in (b1, b2, b3, b4, b5): v.trace_add("write", calc)
|
||||||
|
|
||||||
|
def rebuild_dropdowns():
|
||||||
|
for w in dropdown_widgets:
|
||||||
|
w.destroy()
|
||||||
|
dropdown_widgets.clear()
|
||||||
|
if ring_mode.get() == 4:
|
||||||
|
rows = [
|
||||||
|
("1. Band (Ziffer 1)", b1, colors_digit),
|
||||||
|
("2. Band (Ziffer 2)", b2, colors_digit),
|
||||||
|
("3. Band (Multiplikator)", b3, colors_multiplier),
|
||||||
|
("4. Band (Toleranz)", b5, colors_tolerance),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
rows = [
|
||||||
|
("1. Band (Ziffer 1)", b1, colors_digit),
|
||||||
|
("2. Band (Ziffer 2)", b2, colors_digit),
|
||||||
|
("3. Band (Ziffer 3)", b3, colors_digit),
|
||||||
|
("4. Band (Multiplikator)", b4, colors_multiplier),
|
||||||
|
("5. Band (Toleranz)", b5, colors_tolerance),
|
||||||
|
]
|
||||||
|
for i, (lbl, var, opts) in enumerate(rows):
|
||||||
|
l, o = make_dropdown(dropdown_frame, lbl, var, i, opts)
|
||||||
|
dropdown_widgets.extend([l, o])
|
||||||
|
|
||||||
|
def on_mode_change():
|
||||||
|
rebuild_dropdowns()
|
||||||
|
update_bands()
|
||||||
|
calc()
|
||||||
|
|
||||||
|
make_title_label(content_frame, "Widerstand Farbcode", "", 0)
|
||||||
|
|
||||||
|
mode_frame = tk.Frame(content_frame, bg=bg)
|
||||||
|
mode_frame.grid(row=1, column=0, sticky="w", padx=16, pady=(4, 0))
|
||||||
|
tk.Label(mode_frame, text="Ringe:", bg=bg, fg=fg, font=("Arial", 11)).pack(side="left", padx=(0, 8))
|
||||||
|
for val, label in [(4, "4-Ring"), (5, "5-Ring")]:
|
||||||
|
tk.Radiobutton(
|
||||||
|
mode_frame, text=label, variable=ring_mode, value=val,
|
||||||
|
bg=bg, fg=fg,
|
||||||
|
selectcolor="#2c2c2e" if bg_dark else "#dddddd",
|
||||||
|
activebackground=bg, activeforeground=fg,
|
||||||
|
font=("Arial", 11),
|
||||||
|
command=on_mode_change
|
||||||
|
).pack(side="left", padx=4)
|
||||||
|
|
||||||
|
canvas_width = 180
|
||||||
|
body_left = 40
|
||||||
|
body_right = 140
|
||||||
|
body_top, body_bottom = 30, 50
|
||||||
|
wire_y = 40
|
||||||
|
|
||||||
|
canvas = tk.Canvas(content_frame, height=70, width=canvas_width,
|
||||||
|
bg=bg, highlightthickness=0)
|
||||||
|
canvas.grid(row=2, column=0, sticky="w", padx=16, pady=(10, 4))
|
||||||
|
|
||||||
|
canvas.create_rectangle(body_left, body_top, body_right, body_bottom,
|
||||||
|
fill="#d2b48c", outline="#8b5a2b", width=2)
|
||||||
|
canvas.create_line(body_left, wire_y, 25, wire_y, fill="#8b5a2b", width=3)
|
||||||
|
canvas.create_line(body_right, wire_y, canvas_width-20, wire_y, fill="#8b5a2b", width=3)
|
||||||
|
|
||||||
|
bands_4 = [
|
||||||
|
(body_left+10, body_top, body_left+18, body_bottom),
|
||||||
|
(body_left+28, body_top, body_left+36, body_bottom),
|
||||||
|
(body_left+50, body_top, body_left+58, body_bottom),
|
||||||
|
(body_left+70, body_top, body_left+78, body_bottom),
|
||||||
|
]
|
||||||
|
bands_5 = [
|
||||||
|
(body_left+8, body_top, body_left+15, body_bottom),
|
||||||
|
(body_left+23, body_top, body_left+30, body_bottom),
|
||||||
|
(body_left+38, body_top, body_left+45, body_bottom),
|
||||||
|
(body_left+56, body_top, body_left+63, body_bottom),
|
||||||
|
(body_left+73, body_top, body_left+80, body_bottom),
|
||||||
|
]
|
||||||
|
|
||||||
|
band_items = []
|
||||||
|
for coords in bands_4:
|
||||||
|
item = canvas.create_rectangle(*coords, fill="#000000", outline="black")
|
||||||
|
band_items.append(item)
|
||||||
|
item5 = canvas.create_rectangle(*bands_5[4], fill="#000000", outline="black", state="hidden")
|
||||||
|
band_items.append(item5)
|
||||||
|
|
||||||
|
def make_dropdown(parent, label_text, var, row, options):
|
||||||
|
lbl = tk.Label(parent, text=label_text, bg=bg, fg=fg,
|
||||||
|
font=("Arial", 11), anchor="w")
|
||||||
|
lbl.grid(row=row*2, column=0, sticky="w", pady=(8, 0))
|
||||||
|
om = tk.OptionMenu(parent, var, *options)
|
||||||
|
om.config(bg="#2c2c2e" if bg_dark else "#f0f0f0", fg=fg,
|
||||||
|
activebackground="#3a3a3c", activeforeground=fg,
|
||||||
|
relief="flat", highlightthickness=0,
|
||||||
|
font=("Arial", 11), width=12)
|
||||||
|
om["menu"].config(bg="#2c2c2e" if bg_dark else "#f0f0f0", fg=fg)
|
||||||
|
om.grid(row=row*2+1, column=0, sticky="ew", pady=(2, 2))
|
||||||
|
return lbl, om
|
||||||
|
|
||||||
|
dropdown_frame = tk.Frame(content_frame, bg=bg)
|
||||||
|
dropdown_frame.grid(row=3, column=0, sticky="ew", padx=16, pady=(8, 0))
|
||||||
|
dropdown_frame.columnconfigure(0, weight=1)
|
||||||
|
dropdown_widgets = []
|
||||||
|
|
||||||
|
result = make_result_label(content_frame, 4)
|
||||||
|
|
||||||
|
rebuild_dropdowns()
|
||||||
|
update_bands()
|
||||||
|
calc()
|
||||||
|
|
||||||
def power_u_i():
|
def power_u_i():
|
||||||
clear_frame(); content_frame.columnconfigure(0, weight=1)
|
clear_frame(); content_frame.columnconfigure(0, weight=1)
|
||||||
var_u, var_i = tk.StringVar(), tk.StringVar()
|
var_u, var_i = tk.StringVar(), tk.StringVar()
|
||||||
|
|||||||
Reference in New Issue
Block a user