519 lines
23 KiB
Python
519 lines
23 KiB
Python
import json
|
||
import os
|
||
import tkinter as tk
|
||
from tkinter import ttk, filedialog, messagebox
|
||
from dataclasses import dataclass, asdict
|
||
from typing import Dict, List, Any
|
||
|
||
APP_TITLE = "MultiFlow UI + Schritt-für-Schritt-Anleitung"
|
||
DEFAULT_OUTPUT = "multiflow_anleitung.txt"
|
||
|
||
|
||
@dataclass
|
||
class ProtocolData:
|
||
# Allgemein
|
||
geraete_nummer: str = "18DC0023"
|
||
zaehler_bez: str = "?"
|
||
personal_nr: str = "000001"
|
||
software_version: str = "5.01C5.270E"
|
||
datum_uhrzeit: str = "06.09.2023 14:34"
|
||
|
||
# Produktdefinitionen (laut Anforderung fix)
|
||
produkt_1_name: str = "Heizöl EL"
|
||
produkt_1_art: str = "Flüssigprodukt"
|
||
produkt_1_ptb: str = "1"
|
||
produkt_1_einheit: str = "Liter"
|
||
|
||
produkt_2_name: str = "Diesel"
|
||
produkt_2_art: str = "Flüssigprodukt"
|
||
produkt_2_ptb: str = "2"
|
||
produkt_2_einheit: str = "Liter"
|
||
|
||
produkt_3_name: str = "Super E5"
|
||
produkt_3_art: str = "Flüssigprodukt"
|
||
produkt_3_ptb: str = "15"
|
||
produkt_3_einheit: str = "Liter"
|
||
|
||
# Veränderbare technische Parameter
|
||
sensor_verschiebung_0c: float = 0.0
|
||
sensor_verschiebung_100c: float = 0.0
|
||
sensor_ausgeschaltet: bool = False
|
||
|
||
kompensation_typ: str = "VCF"
|
||
kompensation_temp_c: float = 15.0
|
||
produktgruppe: str = "Raffi. Öl"
|
||
mittlere_dichte: float = 836.0
|
||
|
||
meter_faktor_1: float = 1.0
|
||
meter_faktor_2: float = 1.0
|
||
meter_faktor_3: float = 1.0
|
||
meter_faktor_4: float = 1.0
|
||
durchfluss_rate_1: float = 1000.0
|
||
durchfluss_rate_2: float = 0.0
|
||
durchfluss_rate_3: float = 0.0
|
||
durchfluss_rate_4: float = 0.0
|
||
|
||
min_temp_c: float = -20.0
|
||
max_temp_c: float = 50.0
|
||
aenderungsfaktor: float = 0.0
|
||
|
||
minimum_abgaben: int = 0
|
||
minimal_vorlauf_l: float = 200.0
|
||
minimal_volumen_l: float = 23.0
|
||
preiskorrektur: bool = True
|
||
additiv_ausweisen: bool = False
|
||
auto_abgabe_stop_min: int = 0
|
||
zusatz_pruefung: str = "#############"
|
||
|
||
pulswertigkeit_l: float = 1.0
|
||
max_durchfluss: float = 800.0
|
||
max_fehlerpulse: int = 4
|
||
sensor_typ: str = "TK-Typ"
|
||
min_durchfluss: float = 20.0
|
||
drehrichtung: int = 0
|
||
|
||
drucker_auswahl: str = "TM-U295"
|
||
drucker_protokoll: bool = False
|
||
max_fehlversuche: int = 0
|
||
|
||
entgasung_steuerung: str = "Aus"
|
||
abfuell_sicherung: bool = False
|
||
ventil_steuerung: str = "Basis-Steuerung"
|
||
|
||
# Protokoll-/Zählerwerte: bewusst read-only / nicht editierbar
|
||
unkompensiertes_volumen_l: float = 16995.0
|
||
kompensiertes_volumen_l: float = 17211.0
|
||
kompensierte_masse_kg: float = 14309.0
|
||
additive_gesamt_l: float = 0.0
|
||
nicht_berechnet_l: float = 0.0
|
||
|
||
|
||
class MultiFlowApp:
|
||
def __init__(self, root: tk.Tk):
|
||
self.root = root
|
||
self.root.title(APP_TITLE)
|
||
self.data = ProtocolData()
|
||
self.variables: Dict[str, Any] = {}
|
||
self._build_ui()
|
||
self._load_defaults_into_ui()
|
||
|
||
def _build_ui(self):
|
||
self.root.geometry("1180x820")
|
||
self.root.minsize(1020, 720)
|
||
|
||
main = ttk.Frame(self.root, padding=12)
|
||
main.pack(fill="both", expand=True)
|
||
|
||
title = ttk.Label(
|
||
main,
|
||
text="MultiFlow – variable Parameter pflegen und Anleitung erzeugen",
|
||
font=("Segoe UI", 15, "bold"),
|
||
)
|
||
title.pack(anchor="w", pady=(0, 8))
|
||
|
||
info = ttk.Label(
|
||
main,
|
||
text=(
|
||
"Annahme in dieser Version: Produktdefinitionen bleiben fest. "
|
||
"Veränderbar sind nur technische Parameter und Kalibrierwerte. "
|
||
"Summenstände / Gesamtzähler gelten als reine Protokollwerte und werden nicht geändert."
|
||
),
|
||
wraplength=1100,
|
||
justify="left",
|
||
)
|
||
info.pack(anchor="w", pady=(0, 10))
|
||
|
||
toolbar = ttk.Frame(main)
|
||
toolbar.pack(fill="x", pady=(0, 10))
|
||
ttk.Button(toolbar, text="Neu / Standardwerte", command=self.reset_defaults).pack(side="left", padx=(0, 6))
|
||
ttk.Button(toolbar, text="JSON laden", command=self.load_json).pack(side="left", padx=6)
|
||
ttk.Button(toolbar, text="JSON speichern", command=self.save_json).pack(side="left", padx=6)
|
||
ttk.Button(toolbar, text="Anleitung als TXT exportieren", command=self.export_manual).pack(side="left", padx=6)
|
||
ttk.Button(toolbar, text="Variablen prüfen", command=self.show_variable_summary).pack(side="left", padx=6)
|
||
|
||
self.notebook = ttk.Notebook(main)
|
||
self.notebook.pack(fill="both", expand=True)
|
||
|
||
self.tab_allgemein = ttk.Frame(self.notebook, padding=10)
|
||
self.tab_kalibrierung = ttk.Frame(self.notebook, padding=10)
|
||
self.tab_geraet = ttk.Frame(self.notebook, padding=10)
|
||
self.tab_readonly = ttk.Frame(self.notebook, padding=10)
|
||
self.tab_manual = ttk.Frame(self.notebook, padding=10)
|
||
|
||
self.notebook.add(self.tab_allgemein, text="Allgemein")
|
||
self.notebook.add(self.tab_kalibrierung, text="Kalibrierwerte")
|
||
self.notebook.add(self.tab_geraet, text="Gerät / Sensorik")
|
||
self.notebook.add(self.tab_readonly, text="Nur Anzeige")
|
||
self.notebook.add(self.tab_manual, text="Vorschau Anleitung")
|
||
|
||
self._build_general_tab()
|
||
self._build_calibration_tab()
|
||
self._build_device_tab()
|
||
self._build_readonly_tab()
|
||
self._build_manual_tab()
|
||
|
||
def _add_entry(self, parent, row, label, key, width=18):
|
||
ttk.Label(parent, text=label).grid(row=row, column=0, sticky="w", padx=(0, 10), pady=4)
|
||
var = tk.StringVar()
|
||
ent = ttk.Entry(parent, textvariable=var, width=width)
|
||
ent.grid(row=row, column=1, sticky="w", pady=4)
|
||
self.variables[key] = var
|
||
return ent
|
||
|
||
def _add_check(self, parent, row, label, key):
|
||
var = tk.BooleanVar()
|
||
chk = ttk.Checkbutton(parent, text=label, variable=var)
|
||
chk.grid(row=row, column=0, columnspan=2, sticky="w", pady=4)
|
||
self.variables[key] = var
|
||
return chk
|
||
|
||
def _build_general_tab(self):
|
||
left = ttk.LabelFrame(self.tab_allgemein, text="Allgemeine Daten", padding=10)
|
||
left.pack(side="left", fill="both", expand=True, padx=(0, 8))
|
||
|
||
self._add_entry(left, 0, "Geräte-Nummer", "geraete_nummer")
|
||
self._add_entry(left, 1, "Zähler-Bezeichnung", "zaehler_bez")
|
||
self._add_entry(left, 2, "Personal-Nr.", "personal_nr")
|
||
self._add_entry(left, 3, "Software-Version", "software_version")
|
||
self._add_entry(left, 4, "Datum / Uhrzeit", "datum_uhrzeit")
|
||
|
||
right = ttk.LabelFrame(self.tab_allgemein, text="Feste Produktdefinitionen (nur Anzeige)", padding=10)
|
||
right.pack(side="left", fill="both", expand=True)
|
||
|
||
text = tk.Text(right, height=20, width=58, wrap="word")
|
||
text.pack(fill="both", expand=True)
|
||
text.insert(
|
||
"1.0",
|
||
"Produkt 1: Heizöl EL / Flüssigprodukt / PTB 1 / Liter\n"
|
||
"Produkt 2: Diesel / Flüssigprodukt / PTB 2 / Liter\n"
|
||
"Produkt 3: Super E5 / Flüssigprodukt / PTB 15 / Liter\n\n"
|
||
"Diese Werte sind in dieser Programmversion absichtlich fest, weil du geschrieben hast, "
|
||
"dass die Produktdefinitionen immer gleich sind."
|
||
)
|
||
text.configure(state="disabled")
|
||
|
||
def _build_calibration_tab(self):
|
||
left = ttk.LabelFrame(self.tab_kalibrierung, text="Temperatur / Kompensation", padding=10)
|
||
left.pack(side="left", fill="both", expand=True, padx=(0, 8))
|
||
|
||
self._add_entry(left, 0, "Temperatur-Verschiebung bei 0 °C", "sensor_verschiebung_0c")
|
||
self._add_entry(left, 1, "Temperatur-Verschiebung bei 100 °C", "sensor_verschiebung_100c")
|
||
self._add_check(left, 2, "Sensor ausgeschaltet", "sensor_ausgeschaltet")
|
||
self._add_entry(left, 3, "Kompensationstyp", "kompensation_typ")
|
||
self._add_entry(left, 4, "Kompensationstemperatur °C", "kompensation_temp_c")
|
||
self._add_entry(left, 5, "Produktgruppe", "produktgruppe")
|
||
self._add_entry(left, 6, "Mittlere Dichte", "mittlere_dichte")
|
||
self._add_entry(left, 7, "Min. Temperatur °C", "min_temp_c")
|
||
self._add_entry(left, 8, "Max. Temperatur °C", "max_temp_c")
|
||
self._add_entry(left, 9, "Änderungsfaktor", "aenderungsfaktor")
|
||
|
||
right = ttk.LabelFrame(self.tab_kalibrierung, text="Meterfaktoren / Durchflussraten", padding=10)
|
||
right.pack(side="left", fill="both", expand=True)
|
||
|
||
self._add_entry(right, 0, "Meter-Faktor 1", "meter_faktor_1")
|
||
self._add_entry(right, 1, "Meter-Faktor 2", "meter_faktor_2")
|
||
self._add_entry(right, 2, "Meter-Faktor 3", "meter_faktor_3")
|
||
self._add_entry(right, 3, "Meter-Faktor 4", "meter_faktor_4")
|
||
self._add_entry(right, 4, "Durchfluss-Rate 1", "durchfluss_rate_1")
|
||
self._add_entry(right, 5, "Durchfluss-Rate 2", "durchfluss_rate_2")
|
||
self._add_entry(right, 6, "Durchfluss-Rate 3", "durchfluss_rate_3")
|
||
self._add_entry(right, 7, "Durchfluss-Rate 4", "durchfluss_rate_4")
|
||
|
||
def _build_device_tab(self):
|
||
left = ttk.LabelFrame(self.tab_geraet, text="Geräteeinstellungen", padding=10)
|
||
left.pack(side="left", fill="both", expand=True, padx=(0, 8))
|
||
|
||
self._add_entry(left, 0, "Minimum Abgaben", "minimum_abgaben")
|
||
self._add_entry(left, 1, "Minimal-Vorlauf (L)", "minimal_vorlauf_l")
|
||
self._add_entry(left, 2, "Minimal-Volumen (L)", "minimal_volumen_l")
|
||
self._add_check(left, 3, "Preiskorrektur aktiv", "preiskorrektur")
|
||
self._add_check(left, 4, "Additiv ausweisen", "additiv_ausweisen")
|
||
self._add_entry(left, 5, "Auto-Abgabe-Stop (min)", "auto_abgabe_stop_min")
|
||
self._add_entry(left, 6, "Zusatzprüfung", "zusatz_pruefung")
|
||
self._add_entry(left, 7, "Ventil-Steuerung", "ventil_steuerung")
|
||
self._add_entry(left, 8, "Entgasung-Steuerung", "entgasung_steuerung")
|
||
self._add_check(left, 9, "Abfüll-Sicherung", "abfuell_sicherung")
|
||
|
||
right = ttk.LabelFrame(self.tab_geraet, text="Pulszähler / Drucker", padding=10)
|
||
right.pack(side="left", fill="both", expand=True)
|
||
|
||
self._add_entry(right, 0, "Pulswertigkeit (L)", "pulswertigkeit_l")
|
||
self._add_entry(right, 1, "Max. Durchfluss", "max_durchfluss")
|
||
self._add_entry(right, 2, "Max. Fehlerpulse", "max_fehlerpulse")
|
||
self._add_entry(right, 3, "Sensor-Typ", "sensor_typ")
|
||
self._add_entry(right, 4, "Min. Durchfluss", "min_durchfluss")
|
||
self._add_entry(right, 5, "Drehrichtung", "drehrichtung")
|
||
self._add_entry(right, 6, "Drucker-Auswahl", "drucker_auswahl")
|
||
self._add_check(right, 7, "Drucker-Protokoll aktiv", "drucker_protokoll")
|
||
self._add_entry(right, 8, "Max. Fehlversuche", "max_fehlversuche")
|
||
|
||
def _build_readonly_tab(self):
|
||
frame = ttk.LabelFrame(self.tab_readonly, text="Nur Anzeige – typische Protokoll-/Summenstände", padding=10)
|
||
frame.pack(fill="both", expand=True)
|
||
|
||
cols = ("parameter", "wert", "hinweis")
|
||
tree = ttk.Treeview(frame, columns=cols, show="headings", height=12)
|
||
tree.pack(fill="both", expand=True)
|
||
tree.heading("parameter", text="Parameter")
|
||
tree.heading("wert", text="Beispielwert")
|
||
tree.heading("hinweis", text="Hinweis")
|
||
|
||
rows = [
|
||
("Unkompensiertes Volumen", "16995 L", "Read-only; Summenzähler"),
|
||
("Kompensiertes Volumen", "17211 L", "Read-only; Summenzähler"),
|
||
("Kompensierte Masse", "14309 kg", "Read-only; Summenzähler"),
|
||
("Additive Gesamt", "0.0 L", "Read-only; Summenzähler"),
|
||
("Nicht berechnet", "0.0 L", "Read-only; Summenzähler"),
|
||
("Seriennummer", "16CH0111", "Gerätekennung; nicht Arbeitsparameter"),
|
||
("Siegelzahl", "000013", "Eich-/Sicherheitsinformation"),
|
||
("Berichtsdatum", "06.09.2023 14:34", "Ausdruckwert; kein Sollparameter"),
|
||
]
|
||
for row in rows:
|
||
tree.insert("", "end", values=row)
|
||
|
||
note = ttk.Label(
|
||
frame,
|
||
text=(
|
||
"Einschätzung für deine UI: Summenstände wie 'Unkompensiertes Volumen' sollten in der Regel nicht editierbar sein. "
|
||
"Das sind Gesamtzähler bzw. Protokollwerte. Veränderbar sind eher Kalibrier- und Geräteeinstellungen."
|
||
),
|
||
wraplength=1050,
|
||
justify="left",
|
||
)
|
||
note.pack(anchor="w", pady=(10, 0))
|
||
|
||
def _build_manual_tab(self):
|
||
wrapper = ttk.Frame(self.tab_manual)
|
||
wrapper.pack(fill="both", expand=True)
|
||
self.manual_text = tk.Text(wrapper, wrap="word")
|
||
self.manual_text.pack(side="left", fill="both", expand=True)
|
||
scroll = ttk.Scrollbar(wrapper, orient="vertical", command=self.manual_text.yview)
|
||
scroll.pack(side="right", fill="y")
|
||
self.manual_text.configure(yscrollcommand=scroll.set)
|
||
|
||
btns = ttk.Frame(self.tab_manual)
|
||
btns.pack(fill="x", pady=(8, 0))
|
||
ttk.Button(btns, text="Vorschau aktualisieren", command=self.update_manual_preview).pack(side="left")
|
||
|
||
def _load_defaults_into_ui(self):
|
||
for key, value in asdict(self.data).items():
|
||
var = self.variables.get(key)
|
||
if var is None:
|
||
continue
|
||
if isinstance(var, tk.BooleanVar):
|
||
var.set(bool(value))
|
||
else:
|
||
var.set(str(value))
|
||
self.update_manual_preview()
|
||
|
||
def _read_ui_into_data(self):
|
||
current = asdict(self.data)
|
||
for key, old_value in current.items():
|
||
var = self.variables.get(key)
|
||
if var is None:
|
||
continue
|
||
raw = var.get()
|
||
try:
|
||
if isinstance(old_value, bool):
|
||
current[key] = bool(raw)
|
||
elif isinstance(old_value, int) and not isinstance(old_value, bool):
|
||
current[key] = int(float(raw))
|
||
elif isinstance(old_value, float):
|
||
current[key] = float(str(raw).replace(",", "."))
|
||
else:
|
||
current[key] = str(raw)
|
||
except Exception as exc:
|
||
raise ValueError(f"Ungültiger Wert für {key}: {raw}") from exc
|
||
self.data = ProtocolData(**current)
|
||
|
||
def reset_defaults(self):
|
||
self.data = ProtocolData()
|
||
self._load_defaults_into_ui()
|
||
messagebox.showinfo(APP_TITLE, "Standardwerte geladen.")
|
||
|
||
def save_json(self):
|
||
try:
|
||
self._read_ui_into_data()
|
||
except ValueError as exc:
|
||
messagebox.showerror(APP_TITLE, str(exc))
|
||
return
|
||
path = filedialog.asksaveasfilename(
|
||
defaultextension=".json",
|
||
filetypes=[("JSON-Dateien", "*.json")],
|
||
title="Parameter als JSON speichern",
|
||
)
|
||
if not path:
|
||
return
|
||
with open(path, "w", encoding="utf-8") as f:
|
||
json.dump(asdict(self.data), f, ensure_ascii=False, indent=2)
|
||
messagebox.showinfo(APP_TITLE, f"Gespeichert:\n{path}")
|
||
|
||
def load_json(self):
|
||
path = filedialog.askopenfilename(
|
||
filetypes=[("JSON-Dateien", "*.json")],
|
||
title="Parameter-JSON laden",
|
||
)
|
||
if not path:
|
||
return
|
||
try:
|
||
with open(path, "r", encoding="utf-8") as f:
|
||
data = json.load(f)
|
||
self.data = ProtocolData(**data)
|
||
self._load_defaults_into_ui()
|
||
messagebox.showinfo(APP_TITLE, f"Geladen:\n{path}")
|
||
except Exception as exc:
|
||
messagebox.showerror(APP_TITLE, f"Datei konnte nicht geladen werden:\n{exc}")
|
||
|
||
def _build_manual(self) -> str:
|
||
d = self.data
|
||
lines: List[str] = []
|
||
lines.append("MULTIFLOW – SCHRITT-FÜR-SCHRITT-ANLEITUNG ZUR PARAMETERPFLEGE")
|
||
lines.append("=" * 68)
|
||
lines.append("")
|
||
lines.append("Wichtige Annahmen dieser Anleitung:")
|
||
lines.append("- Die Produktdefinitionen bleiben unverändert.")
|
||
lines.append("- Geändert werden nur technische Parameter und Kalibrierwerte.")
|
||
lines.append("- Summenstände wie unkompensiertes Volumen sind nur Anzeigewerte.")
|
||
lines.append("")
|
||
lines.append("Welche Parameter in dieser UI als variabel behandelt werden:")
|
||
lines.extend([
|
||
f"- Temperatur-Verschiebung bei 0 °C: {d.sensor_verschiebung_0c}",
|
||
f"- Temperatur-Verschiebung bei 100 °C: {d.sensor_verschiebung_100c}",
|
||
f"- Sensor ausgeschaltet: {'Ja' if d.sensor_ausgeschaltet else 'Nein'}",
|
||
f"- Kompensationstyp: {d.kompensation_typ}",
|
||
f"- Kompensationstemperatur: {d.kompensation_temp_c} °C",
|
||
f"- Produktgruppe: {d.produktgruppe}",
|
||
f"- Mittlere Dichte: {d.mittlere_dichte}",
|
||
f"- Meter-Faktoren 1–4: {d.meter_faktor_1}, {d.meter_faktor_2}, {d.meter_faktor_3}, {d.meter_faktor_4}",
|
||
f"- Durchfluss-Raten 1–4: {d.durchfluss_rate_1}, {d.durchfluss_rate_2}, {d.durchfluss_rate_3}, {d.durchfluss_rate_4}",
|
||
f"- Temperaturgrenzen: {d.min_temp_c} bis {d.max_temp_c} °C",
|
||
f"- Änderungsfaktor: {d.aenderungsfaktor}",
|
||
f"- Minimal-Vorlauf: {d.minimal_vorlauf_l} L",
|
||
f"- Minimal-Volumen: {d.minimal_volumen_l} L",
|
||
f"- Preiskorrektur: {'Ja' if d.preiskorrektur else 'Nein'}",
|
||
f"- Additiv ausweisen: {'Ja' if d.additiv_ausweisen else 'Nein'}",
|
||
f"- Auto-Abgabe-Stop: {d.auto_abgabe_stop_min} min",
|
||
f"- Pulswertigkeit: {d.pulswertigkeit_l} L",
|
||
f"- Max. Durchfluss: {d.max_durchfluss}",
|
||
f"- Max. Fehlerpulse: {d.max_fehlerpulse}",
|
||
f"- Sensor-Typ: {d.sensor_typ}",
|
||
f"- Min. Durchfluss: {d.min_durchfluss}",
|
||
f"- Drehrichtung: {d.drehrichtung}",
|
||
f"- Drucker-Auswahl: {d.drucker_auswahl}",
|
||
f"- Drucker-Protokoll: {'Ja' if d.drucker_protokoll else 'Nein'}",
|
||
f"- Max. Fehlversuche: {d.max_fehlversuche}",
|
||
f"- Ventil-Steuerung: {d.ventil_steuerung}",
|
||
f"- Entgasung-Steuerung: {d.entgasung_steuerung}",
|
||
f"- Abfüll-Sicherung: {'Ja' if d.abfuell_sicherung else 'Nein'}",
|
||
])
|
||
lines.append("")
|
||
lines.append("Nicht variabel in dieser UI:")
|
||
lines.extend([
|
||
f"- Unkompensiertes Volumen: {d.unkompensiertes_volumen_l} L (nur Summenzähler)",
|
||
f"- Kompensiertes Volumen: {d.kompensiertes_volumen_l} L (nur Summenzähler)",
|
||
f"- Kompensierte Masse: {d.kompensierte_masse_kg} kg (nur Summenzähler)",
|
||
"- Produktdefinitionen Heizöl EL / Diesel / Super E5 (in dieser Version fest)",
|
||
])
|
||
lines.append("")
|
||
lines.append("SCHRITT-FÜR-SCHRITT FÜR EINE UNGELERNTE KRAFT")
|
||
lines.append("-" * 68)
|
||
steps = [
|
||
"1. Starten Sie das Programm 'MultiFlow UI + Schritt-für-Schritt-Anleitung'.",
|
||
"2. Klicken Sie oben auf den Reiter 'Kalibrierwerte'.",
|
||
"3. Tragen Sie bei 'Temperatur-Verschiebung bei 0 °C' den gewünschten Wert ein.",
|
||
"4. Tragen Sie bei 'Temperatur-Verschiebung bei 100 °C' den gewünschten Wert ein.",
|
||
"5. Prüfen Sie, ob das Häkchen 'Sensor ausgeschaltet' gesetzt ist. Nur setzen, wenn der Sensor wirklich deaktiviert werden soll.",
|
||
"6. Tragen Sie den gewünschten Kompensationstyp ein, zum Beispiel VCF.",
|
||
"7. Tragen Sie die Kompensationstemperatur ein, normalerweise 15,0 °C.",
|
||
"8. Tragen Sie die mittlere Dichte ein, wenn diese geändert werden soll.",
|
||
"9. Geben Sie die Meter-Faktoren 1 bis 4 ein. Wenn nur ein Meter-Faktor benutzt wird, lassen Sie Faktor 2 bis 4 auf 1,0 und die Raten 2 bis 4 auf 0.",
|
||
"10. Geben Sie die Durchfluss-Rate 1 ein. Diese ist meist die maximale Durchflussmenge.",
|
||
"11. Klicken Sie auf den Reiter 'Gerät / Sensorik'.",
|
||
"12. Prüfen oder ändern Sie dort Minimal-Vorlauf, Minimal-Volumen, Pulswertigkeit, max. Durchfluss und Sensor-Typ.",
|
||
"13. Wenn ein Parameter nicht geändert werden soll, lassen Sie den vorhandenen Wert einfach stehen.",
|
||
"14. Klicken Sie oben auf 'Variablen prüfen'. Kontrollieren Sie die Zusammenfassung im Hinweisfenster.",
|
||
"15. Wenn alles richtig ist, klicken Sie auf 'JSON speichern', damit die Werte dokumentiert werden.",
|
||
"16. Klicken Sie danach auf 'Anleitung als TXT exportieren'.",
|
||
f"17. Speichern Sie die Datei an einem leicht auffindbaren Ort, zum Beispiel auf dem Desktop als '{DEFAULT_OUTPUT}'.",
|
||
"18. Öffnen Sie den Reiter 'Vorschau Anleitung', wenn Sie die automatisch erzeugte Anleitung direkt lesen möchten.",
|
||
"19. Die Summenstände im Reiter 'Nur Anzeige' dienen nur zur Kontrolle. Diese Werte bitte nicht als Eingabewerte behandeln.",
|
||
"20. Beenden Sie das Programm erst, wenn JSON und Anleitung erfolgreich gespeichert wurden.",
|
||
]
|
||
lines.extend(steps)
|
||
lines.append("")
|
||
lines.append("Praxishinweis:")
|
||
lines.append(
|
||
"Wenn du später doch weitere Felder editierbar machen willst, kannst du sie im Code sehr einfach von 'Nur Anzeige' "
|
||
"nach 'Gerät / Sensorik' oder 'Kalibrierwerte' verschieben."
|
||
)
|
||
return "\n".join(lines)
|
||
|
||
def update_manual_preview(self):
|
||
try:
|
||
self._read_ui_into_data()
|
||
except ValueError as exc:
|
||
messagebox.showerror(APP_TITLE, str(exc))
|
||
return
|
||
manual = self._build_manual()
|
||
self.manual_text.delete("1.0", "end")
|
||
self.manual_text.insert("1.0", manual)
|
||
|
||
def export_manual(self):
|
||
try:
|
||
self._read_ui_into_data()
|
||
except ValueError as exc:
|
||
messagebox.showerror(APP_TITLE, str(exc))
|
||
return
|
||
manual = self._build_manual()
|
||
path = filedialog.asksaveasfilename(
|
||
defaultextension=".txt",
|
||
initialfile=DEFAULT_OUTPUT,
|
||
filetypes=[("Textdateien", "*.txt")],
|
||
title="Anleitung exportieren",
|
||
)
|
||
if not path:
|
||
return
|
||
with open(path, "w", encoding="utf-8") as f:
|
||
f.write(manual)
|
||
messagebox.showinfo(APP_TITLE, f"Anleitung gespeichert:\n{path}")
|
||
|
||
def show_variable_summary(self):
|
||
try:
|
||
self._read_ui_into_data()
|
||
except ValueError as exc:
|
||
messagebox.showerror(APP_TITLE, str(exc))
|
||
return
|
||
d = self.data
|
||
summary = (
|
||
"Als veränderbar eingeordnet:\n\n"
|
||
"- Temperaturversätze\n"
|
||
"- Kompensation / Dichte / Temperaturgrenzen\n"
|
||
"- Meter-Faktoren und Durchfluss-Raten\n"
|
||
"- Minimal-Vorlauf / Minimal-Volumen\n"
|
||
"- Pulszähler- und Sensorwerte\n"
|
||
"- Drucker- und Ventileinstellungen\n\n"
|
||
"Nicht veränderbar in dieser Version:\n\n"
|
||
"- Unkompensiertes Volumen\n"
|
||
"- Kompensiertes Volumen\n"
|
||
"- Kompensierte Masse\n"
|
||
"- Additive Gesamt\n"
|
||
"- Produktdefinitionen\n\n"
|
||
f"Aktuell eingetragene Temperaturverschiebung: 0 °C = {d.sensor_verschiebung_0c}, 100 °C = {d.sensor_verschiebung_100c}"
|
||
)
|
||
messagebox.showinfo("Variable Parameter", summary)
|
||
|
||
|
||
def main():
|
||
root = tk.Tk()
|
||
style = ttk.Style(root)
|
||
try:
|
||
style.theme_use("clam")
|
||
except tk.TclError:
|
||
pass
|
||
app = MultiFlowApp(root)
|
||
root.mainloop()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|