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