2021-11-28 21:11:20 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
from tkinter import *
|
|
|
|
from tkinter import ttk
|
|
|
|
|
|
|
|
|
|
|
|
# --------------------------
|
|
|
|
# Validator
|
|
|
|
# --------------------------
|
|
|
|
class EntryValidator:
|
|
|
|
def __init__(self, app):
|
|
|
|
self.app = app
|
|
|
|
|
|
|
|
def check_entry(self, what: str = ""):
|
|
|
|
""" Validiert, ob Eingabe leer oder eine Fließkommazahl ist.
|
|
|
|
|
|
|
|
:param what: str eingegebene Zeichenkette
|
|
|
|
:return: boolean
|
|
|
|
"""
|
|
|
|
self.app.controller.switch_status(1)
|
|
|
|
if what == "":
|
|
|
|
# Nichts eingegeben
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
# DEBUG
|
|
|
|
print(what)
|
|
|
|
what = what.replace(',', '.')
|
|
|
|
float(what)
|
|
|
|
return True
|
|
|
|
except ValueError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
# --------------------------
|
|
|
|
# Model
|
|
|
|
# --------------------------
|
|
|
|
class FeetToMeterData:
|
|
|
|
feet: float
|
|
|
|
meter: float
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.feet = 0.0
|
|
|
|
self.meter = 0.0
|
|
|
|
|
|
|
|
def get_meter(self):
|
|
|
|
return self.meter
|
|
|
|
|
|
|
|
def get_feet(self):
|
|
|
|
return self.feet
|
|
|
|
|
|
|
|
def set_feet(self, newfeet: float = 0.0):
|
|
|
|
self.feet = newfeet
|
|
|
|
self.calculate_meter()
|
|
|
|
return True
|
|
|
|
|
|
|
|
def set_meter(self, newmeter: float = 0.0):
|
|
|
|
self.meter = newmeter
|
|
|
|
return True
|
|
|
|
|
|
|
|
def calculate_meter(self):
|
|
|
|
m_value = 0.3048 * self.feet
|
|
|
|
m_value = round(m_value, 2)
|
|
|
|
self.set_meter(m_value)
|
|
|
|
return True
|
|
|
|
|
|
|
|
def print_data(self):
|
|
|
|
print("{} ft = {} m".format(self.feet, self.meter))
|
|
|
|
|
|
|
|
|
|
|
|
# --------------------------
|
|
|
|
# Controller
|
|
|
|
# --------------------------
|
|
|
|
class FeetToMeterController:
|
|
|
|
def __init__(self, app):
|
|
|
|
self.app = app
|
|
|
|
self.status = 1 # 1 - neue Eingabe, 2 - Eingabe verarbeitet
|
|
|
|
|
|
|
|
def switch_status(self, new_status: int = None):
|
|
|
|
""" Verändert den globalen Status
|
|
|
|
|
|
|
|
:param new_status: neuer Statuswert
|
|
|
|
:return: None
|
|
|
|
"""
|
|
|
|
if not new_status:
|
|
|
|
if self.status == 1:
|
|
|
|
self.status = 2
|
|
|
|
else:
|
|
|
|
self.status = 1
|
|
|
|
else:
|
|
|
|
self.status = new_status
|
|
|
|
|
|
|
|
self.app.update_statusbar(self.status)
|
|
|
|
|
|
|
|
def calculate(self, *args):
|
|
|
|
""" Berechnet aus der eingegebenen Länge in feet die Länge in Metern.
|
|
|
|
|
|
|
|
:param args:
|
|
|
|
:return: None
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
stringvalue = self.app.feet.get()
|
|
|
|
stringvalue = stringvalue.replace(",", ".")
|
|
|
|
value = float(stringvalue)
|
|
|
|
self.app.data.set_feet(value)
|
|
|
|
m_value = self.app.data.get_meter()
|
|
|
|
self.app.meters.set(m_value)
|
|
|
|
# Berechnung abgeschlossen.
|
|
|
|
self.app.data.print_data()
|
|
|
|
self.switch_status(2)
|
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
# --------------------------
|
|
|
|
# View / Main Application
|
|
|
|
# --------------------------
|
|
|
|
class FeetToMeter(Tk):
|
|
|
|
def __init__(self):
|
|
|
|
super().__init__()
|
|
|
|
self.controller = FeetToMeterController(self)
|
|
|
|
self.validator = EntryValidator(self)
|
|
|
|
self.data = FeetToMeterData()
|
|
|
|
|
|
|
|
self.title("Feet to Meters")
|
|
|
|
|
2021-11-28 21:20:57 +00:00
|
|
|
# Textvariablen, um Inhalte der Eingabefelder zu kontrollieren
|
|
|
|
self.feet = StringVar()
|
|
|
|
self.meters = StringVar()
|
|
|
|
|
2021-11-28 21:11:20 +00:00
|
|
|
# Event Handler (Validator) registrieren
|
|
|
|
self.check_entry_wrapper = self.register(self.validator.check_entry)
|
|
|
|
|
2021-11-28 21:20:57 +00:00
|
|
|
# Zugriff auf Stilinformationen
|
2021-11-28 21:11:20 +00:00
|
|
|
self.s = ttk.Style()
|
2021-11-28 21:20:57 +00:00
|
|
|
# Zugriff auf Statusleiste
|
|
|
|
self.frame_statusbar = ttk.Frame(self)
|
|
|
|
|
|
|
|
# Aufbau des Fensters
|
2021-11-28 21:11:20 +00:00
|
|
|
self.create_styles()
|
|
|
|
self.create_widgets()
|
|
|
|
self.bind_events()
|
|
|
|
|
|
|
|
def create_styles(self):
|
|
|
|
""" Erstellen der speziellen Stil-Konfigurationen.
|
|
|
|
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
self.s.configure('aFrame.TFrame', background="yellow")
|
|
|
|
self.s.configure('bFrame.TFrame', background="green")
|
|
|
|
self.s.configure('cFrame.TFrame', background="red")
|
|
|
|
return True
|
|
|
|
|
|
|
|
def create_widgets(self):
|
|
|
|
""" Erstellen der Fenster-Bestandteile.
|
|
|
|
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
# Rahmen im Hauptfenster (aus ttk für Farbanpassung)
|
|
|
|
mainframe = ttk.Frame(self, padding="3 3 12 12")
|
|
|
|
mainframe.grid(column=0, row=0, sticky=N + W + S + E)
|
|
|
|
|
|
|
|
# mainframe darf sich ausdehnen.
|
|
|
|
self.columnconfigure(0, weight=1)
|
|
|
|
self.rowconfigure(0, weight=1)
|
|
|
|
|
|
|
|
# Eingabefeld für Länge in feet
|
|
|
|
feet_entry = ttk.Entry(mainframe, width=7, textvariable=self.feet,
|
|
|
|
validatecommand=(self.check_entry_wrapper, '%P'),
|
|
|
|
validate='key')
|
|
|
|
feet_entry.grid(column=2, row=1, sticky=W + E)
|
|
|
|
|
|
|
|
# Einheit-Label für Eingabefeld der Länge in feet
|
|
|
|
ttk.Label(mainframe, text="feet").grid(column=3, row=1, sticky=W)
|
|
|
|
|
|
|
|
# Label für Ausgabefeld der Länge in Metern
|
|
|
|
ttk.Label(mainframe, text="ist äquivalent zu").grid(column=1, row=2, sticky=E)
|
|
|
|
|
|
|
|
# Einheit-Label für Ausgabefeld der Länge in Metern
|
|
|
|
ttk.Label(mainframe, text="Meter").grid(column=3, row=2, sticky=W)
|
|
|
|
|
|
|
|
# Ausgabefeld für Länge in Metern
|
|
|
|
meters_entry = ttk.Entry(mainframe, width=7, textvariable=self.meters)
|
|
|
|
meters_entry.grid(column=2, row=2, sticky=(W, E))
|
|
|
|
meters_entry.configure(state='readonly') # keine Eingabe, aber selektierbar
|
|
|
|
|
|
|
|
# Button für Berechnung
|
|
|
|
|
|
|
|
button_calc = ttk.Button(mainframe, text="Berechne", command=self.controller.calculate)
|
|
|
|
button_calc.grid(column=3, row=3, sticky=W)
|
|
|
|
|
|
|
|
# schönere Abstände
|
|
|
|
for child in mainframe.winfo_children():
|
|
|
|
child.grid_configure(padx=5, pady=5)
|
|
|
|
|
|
|
|
# Statusbar
|
2021-11-28 21:20:57 +00:00
|
|
|
st = self.frame_statusbar
|
|
|
|
st['relief'] = 'sunken'
|
|
|
|
st['height'] = 12
|
|
|
|
st['style'] = 'aFrame.TFrame'
|
|
|
|
st.grid_propagate(0) # Feste Größe an Grid-Packer weitergeben
|
|
|
|
st.grid(column=0, row=1, sticky=W + S + E)
|
2021-11-28 21:11:20 +00:00
|
|
|
|
|
|
|
# Setze Fokus in Eingabefeld
|
|
|
|
feet_entry.focus()
|
|
|
|
|
|
|
|
def bind_events(self):
|
|
|
|
""" Ereignisse an Kommandos binden.
|
|
|
|
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
self.bind("<Return>", self.controller.calculate)
|
|
|
|
self.bind("<KP_Enter>", self.controller.calculate)
|
|
|
|
self.bind("<Escape>", self.close)
|
|
|
|
|
|
|
|
def update_statusbar(self, status: int = 1):
|
|
|
|
""" Passt die Farbe der Statuszeile an den Status an.
|
|
|
|
|
|
|
|
:return: None
|
|
|
|
"""
|
|
|
|
if status == 1:
|
|
|
|
self.frame_statusbar['style'] = 'aFrame.TFrame'
|
|
|
|
elif status == 2:
|
|
|
|
self.frame_statusbar['style'] = 'bFrame.TFrame'
|
|
|
|
else:
|
|
|
|
self.frame_statusbar['style'] = 'cFrame.TFrame'
|
|
|
|
|
|
|
|
def close(self, event=None):
|
|
|
|
self.destroy()
|
|
|
|
|
|
|
|
|
|
|
|
# --------------------------
|
|
|
|
# Starting Point
|
|
|
|
# --------------------------
|
|
|
|
if __name__ == "__main__":
|
|
|
|
app = FeetToMeter()
|
|
|
|
app.mainloop()
|