Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8634ab0d12 | |||
| 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
|
||||
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:
|
||||
Preamble
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
Any use of this Software, in whole or in part, must visibly credit the original
|
||||
author: Survivalful (https://Survivalful.de/)
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
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:
|
||||
- The application's user interface (e.g. About screen or footer)
|
||||
- The project's README or documentation
|
||||
- The source code as a comment
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
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
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"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
|
||||
|
||||
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
|
||||
* 📐 **Area Calculations** – Square, rectangle, circle, triangle, trapezoid, cylinder, sphere, cone
|
||||
* 📦 **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
|
||||
* 🌍 **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)
|
||||
* 🕓 **Calculation History** – Track last 3 calculations with SQLite storage
|
||||
* ⚙️ **Persistent Settings** – All preferences saved automatically
|
||||
* ⌨️ **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
|
||||
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
|
||||
- Toggle dark mode
|
||||
- Enable/disable rounding
|
||||
@@ -71,7 +84,7 @@ A modern desktop calculator with advanced features for area and volume calculati
|
||||
* **OS**: Windows 7 or later
|
||||
* **RAM**: 50 MB minimum
|
||||
* **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**
|
||||
3. Click **Uninstall**
|
||||
|
||||
Or run **Survivalcalc_Uninstaller.exe** directly.
|
||||
|
||||
---
|
||||
|
||||
## 📬 Support
|
||||
|
||||
* 🌐 **Website**: [Github](https://survivalful.de/survivalcalc/)
|
||||
* 🌐 **Website**: [https://Survivalful.de](https://survivalful.de)
|
||||
* 📧 **Email**: [team@survivalful.de](mailto:team@survivalful.de)
|
||||
|
||||
---
|
||||
|
||||
## 📜 License
|
||||
|
||||
MIT License © 2026 Survivalful
|
||||
GPL License © 2026 Survivalful
|
||||
|
||||
---
|
||||
|
||||
## 📊 Version Info
|
||||
|
||||
* **Version**: 1.0.0
|
||||
* **Version**: 1.0.2
|
||||
* **Release**: 24.03.2026
|
||||
* **Status**: Stable
|
||||
|
||||
|
||||
280
main.py
280
main.py
@@ -11,10 +11,9 @@ import threading
|
||||
import urllib.request
|
||||
import webbrowser
|
||||
|
||||
VERSION_URL = "https://cloud.survivalful.de/s/ydLwqwXdJEj8XwR/download"
|
||||
VERSION_URL = ""
|
||||
|
||||
def fetch_version_info():
|
||||
"""Holt die version.json vom Server. Gibt None bei Fehler zurück."""
|
||||
try:
|
||||
with urllib.request.urlopen(VERSION_URL, timeout=5) as response:
|
||||
return json.loads(response.read().decode())
|
||||
@@ -22,7 +21,6 @@ def fetch_version_info():
|
||||
return None
|
||||
|
||||
def version_is_newer(latest, current):
|
||||
"""Vergleicht zwei Versionsstrings wie '1.2.0' > '1.0.0'."""
|
||||
try:
|
||||
l_parts = list(map(int, latest.split('.')))
|
||||
c_parts = list(map(int, current.split('.')))
|
||||
@@ -129,7 +127,7 @@ LANG = {
|
||||
"result_current": "Strom I",
|
||||
"result_resistance": "Wiederstand R",
|
||||
"voltage": "Spannung",
|
||||
"ampere": "Strom",
|
||||
"current": "Strom",
|
||||
"resistance": "Widerstand",
|
||||
"power": "Leistung",
|
||||
"u": "Spannung U",
|
||||
@@ -267,17 +265,39 @@ def t(key, **kwargs):
|
||||
pass
|
||||
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_status = "Stable"
|
||||
dev_e_mail = "team@survivalful.de"
|
||||
dev_website = "https://Survivalful.de/"
|
||||
app_license = "MIT License © 2026 Survivalful"
|
||||
app_license = "GPL License © 2026 Survivalful"
|
||||
sys_system = platform.system()
|
||||
sys_version = platform.release()
|
||||
sys_machine = platform.machine()
|
||||
sys_python = platform.python_version()
|
||||
install_date = datetime.datetime.now().strftime("%d.%m.%Y %H:%M")
|
||||
install_date = get_install_date()
|
||||
|
||||
DEFAULT_SETTINGS = {
|
||||
"bg_dark": True,
|
||||
@@ -350,10 +370,6 @@ load_settings()
|
||||
init_db()
|
||||
|
||||
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:
|
||||
info = fetch_version_info()
|
||||
if not info:
|
||||
@@ -361,18 +377,64 @@ def check_for_updates():
|
||||
|
||||
latest_version = info.get("version")
|
||||
changelog = info.get("changelog", "Keine Informationen verfügbar.")
|
||||
download_url = info.get("download_url")
|
||||
update_info_url = info.get("update_info_url")
|
||||
download_url = info.get("installer_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)
|
||||
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)
|
||||
|
||||
@@ -380,7 +442,6 @@ def check_for_updates():
|
||||
print(f"Update-Check fehlgeschlagen: {e}")
|
||||
|
||||
|
||||
|
||||
UNIT_FACTORS = {
|
||||
"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
|
||||
@@ -396,6 +457,27 @@ CURRENT_UNITS = ["pA", "nA", "μA", "mA", "A", "kA", "MA", "GA"]
|
||||
RESISTANCE_UNITS = ["pΩ", "nΩ", "μΩ", "mΩ", "Ω", "kΩ", "MΩ", "GΩ"]
|
||||
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):
|
||||
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_p_i"), command=resistance_p_i)
|
||||
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_r_p"), command=power_i_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)
|
||||
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():
|
||||
clear_frame(); content_frame.columnconfigure(0, weight=1)
|
||||
var_u, var_i = tk.StringVar(), tk.StringVar()
|
||||
|
||||
Reference in New Issue
Block a user