feet to meter als model - view - controller realisiert.
This commit is contained in:
		
							
								
								
									
										232
									
								
								tk_first_steps/feet_classes_mvc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								tk_first_steps/feet_classes_mvc.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,232 @@
 | 
			
		||||
#!/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")
 | 
			
		||||
 | 
			
		||||
        # Event Handler (Validator) registrieren
 | 
			
		||||
        self.check_entry_wrapper = self.register(self.validator.check_entry)
 | 
			
		||||
 | 
			
		||||
        self.s = ttk.Style()
 | 
			
		||||
        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
 | 
			
		||||
        self.feet = StringVar()
 | 
			
		||||
        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
 | 
			
		||||
        self.meters = StringVar()
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
        self.frame_statusbar = ttk.Frame(self)
 | 
			
		||||
        self.frame_statusbar['relief'] = 'sunken'
 | 
			
		||||
        self.frame_statusbar['height'] = 12
 | 
			
		||||
        self.frame_statusbar['style'] = 'aFrame.TFrame'
 | 
			
		||||
        self.frame_statusbar.grid_propagate(0)  # Feste Größe an Grid-Packer weitergeben
 | 
			
		||||
        self.frame_statusbar.grid(column=0, row=1, sticky=W + S + E)
 | 
			
		||||
 | 
			
		||||
        # 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()
 | 
			
		||||
		Reference in New Issue
	
	Block a user