===== Beispiele ===== ==== EAN-Prüfung ==== === Tkinter/TTK === Die EAN-Prüfung ist ein Beispiel für eine einfache GUI-Anwendung. Sie hat ein Entry-Widget für die Eingabe einer EAN und ein Text-Widget für die mehrzeilige Ausgabe von Ergebnissen. {{de:eanpruefung.png}} Für den Button "Prüfen" wird diese Ereignismethode programmiert: def bPruefen_Command(self): # Eingabe EAN = self.eEANCV.get() #Verarbeitung if not EAN.isdigit(): self.ausgeben('Eine EAN darf keine anderen Zeichen als Ziffern enthalten!') if len(EAN) == 13: self.ausgeben('Länge der EAN: 13') self.ausgeben('Eingegebene Prüfziffer: ' + EAN[12]) Summe = 0 Index = 0 Faktor = 1 while Index < 12: Summe = Summe + int(EAN[Index])*Faktor Index = Index + 1 if Faktor == 1: Faktor = 3 else: Faktor = 1 if Summe % 10 == 0: berechnetePrüfziffer = 0 else: berechnetePrüfziffer = 10 - Summe % 10 if int(EAN[12]) == berechnetePrüfziffer: self.ausgeben('Die Prüfziffer ' + EAN[12] + ' ist korrekt!') else: self.ausgeben('Die eingegebene Prüfziffer ' + EAN[12] + ' ist inkorrekt!') self.ausgeben('Die korrekte Prüfziffer ist ' + str(berechnetePrüfziffer)) else: self.ausgeben('Die eingegebene EAN hat ' + str(len(EAN)) + ' Ziffern!') self.ausgeben('Sie muss aus 13 Ziffern bestehen.') Gemäß dem EVA-Prinzip wird zunächst die Eingabe aus dem Entry-Widget eingelesen. Zum Einlesen benutzt man die dem Entry-Widget zugeordnete Kontrollvariable eEANCV. Ihr Wert kann zur Eingabe mit eENACV.get() gelesen und mit eEANCV.set(Wert) ausgegeben werden. Mit EAN = self.eEANCV.get() wird also die in das Entry-Widget eingegebene EAN in die lokale **str(ing)** Variable EAN eingelesen und kann dann verarbeitet werden. Im Verarbeitungsteil wird geprüft, ob 13 Ziffern eingegeben wurden und dann die Prüfziffernberechnung durchgeführt. Die Ausgaben erfolgen in das Text-Widget mit der Bezeichnung //Ausgabe//. Es verfügt über sehr leistungsfähige Methoden, weswegen schon die Ausgabe einer einzelnen Zeile nicht ganz einfach ist. Für die Ausgabe wird daher eine eigene Methode //ausgeben()// benutzt, um die Programmierung zu vereinfachen. def ausgeben(self, Zeile): self.Ausgabe.insert('end', Zeile + '\n') Damit wird in das Text-Widget //Ausgabe// am Ende eine Zeile eingefügt und diese mit dem Steuerzeichen '\n' (NewLine) beendet. Besteht eine Ausgabezeile aus mehreren Teilstrings, so müssen diese mit "+" zusammengesetzt werden. Zahlen müssen zuvor mittels **str**() in einen String umgewandelt werden. **Download:** *[[https://www.guipy.de/examples/de/ean.zip |ean.zip]] === Qt Variante der EAN-Prüfung === In Qt haben wir ein LineEdit-Widget für die Eingabe und ein PlainTextWidget für die Ausgabe. Um die EAN zu lesen, verwenden wir die Methode //text()// des LineEdit-Widgets. EAN = self.leEAN.text() Die Ausgabe ist in Qt einfacher als in Tkinter/TTK: def output(self, line): self.Output.appendPlainText(line) **Download**: *[[https://www.guipy.de/examples/de/qtean.zip|qtean.zip]] ==== Auto ==== Dieses Beispiel eignet sich für die Einführung in die objektorientierte Programmierung. Es werden Tankanzeige und Kilometerstand eines Autos modelliert. Die eigentliche Modellierung findet mit dem Klassenmodellierer statt. Das Kennzeichen ist nicht unbedingt erforderlich, es dient zu leichteren Unterscheidung von Autos und zur Verwendung eines weiteren Datentyps. {{de:classdesigner.png}} Das Ergebnis kann interaktiv im UML-Fenster getestet werden. Dabei verstehen die Schülerinnen und Schüler den Unterschied zwischen den Konzepten //Klasse// und //Objekt//. {{de:intro.png}} Im nächsten Schritt wird die Klasse //Auto// als Fachkonzept in einem GUI-Programm verwendet. {{de:auto.png}} Das GUI-Programm nutzt die Auto-Klasse. Dazu muss diese importiert werden. Ein Auto wird erzeugt und als Attribut //auto1// der GUI-Klasse zur weiteren Verwendung bereitgestellt. from Auto import * ... self.auto1 = Auto('DA RR 1013', 47, 7.3) === Tkinter/TTK === Beim Tanken wird die in das Entry-Widget eingegebene Menge über die get()-Methode der zum Widget gehörenden Kontrollvariablen //eMengeCV// eingelesen und mittels //float// in den benötigten Datentyp konvertiert. def bTanken_Command(self): # Eingabe über die GUI Menge = float(self.eMengeCV.get()) # Verarbeitung self.auto1.tanken(Menge) # Ausgabe self.anzeigen() Dann wird gemäß dem EVA-Prinzip die eingegebene Menge in der Methode //tanken()// verarbeitet. Abschließend erfolgt die Ausgabe des Ergebnisses in einer eigenen Methode //anzeigen()//. Die Werte für Kennzeichen, Tankinhalt und Kilometerstand werden über die get()-Methoden der Klasse Auto abgerufen und an die set()-Methoden der Kontrollvariablen eKennzeichenCV, eTankinhaltCV und eKilometerstandCV übergeben: def anzeigen(self): self.eKennzeichenCV.set(self.auto1.get_Kennzeichen()) self.eTankinhaltCV.set(self.auto1.get_Tankinhalt()) self.eKilometerstandCV.set(self.auto1.get_Kilometerstand()) self.lAuto.place(x = self.auto1.get_Kilometerstand(), y = 160) Dem Label-Widget //lAuto// wurde im Objektinspektor über das Attribut //Image// ein Auto-Bild zugewiesen. Mit der place()-Methode wird die x-Position des Autos auf den Kilometerstand gesetzt. **Download** *[[https://www.guipy.de/examples/de/auto.zip |auto.zip]] === Qt === Beim Tanken wird die eingegebene Menge über die text()-Methode des LineEdit-Widgets eingelesen und mittels //float// in den benötigten Datentyp konvertiert. def bTanken_clicked(self, checked): # Eingabe über die GUI Menge = float(self.leMenge.text()) # Verarbeitung self.auto1.tanken(Menge) # Ausgabe self.anzeigen() Dann wird gemäß dem EVA-Prinzip die eingegebene Menge in der Methode //tanken()// verarbeitet. Abschließend erfolgt die Ausgabe des Ergebnisses in einer eigenen Methode //anzeigen()//. Die Werte für Kennzeichen, Tankinhalt und Kilometerstand werden über die get()-Methoden der Klasse Auto abgerufen und an die setText()-Methoden der der LineEdit-Widgets für Kennzeichen, Tankinhalt und Kilometerstand übergeben: self.leKennzeichen.setText(self.auto1.get_Kennzeichen()) self.leTankinhalt.setText(str(self.auto1.get_Tankinhalt())) self.leKilometerstand.setText(str(self.auto1.get_Kilometerstand())) self.lAuto.move(self.auto1.get_Kilometerstand(), 160) Dem Label-Widget //lAuto// wurde im Objektinspektor über das Attribut //Pixmap// ein Auto-Bild zugewiesen. Mit der move()-Methode wird die x-Position des Autos auf den Kilometerstand gesetzt. **Download** *[[https://www.guipy.de/examples/de/qtauto.zip |qtauto.zip]] ==== Verkettete Liste ==== Die Programmierung dynamische Datenstrukturen stellt eine erhebliche Herausforderung dar, denn man muss das Konzept der Verkettung mittels Verweisen verstehen und komplexe Operationen mit Verweisen durchführen. Neu dabei sind Variablen die Werte haben, welche Verweise auf Objekte bzw. Adressen sind. In diesem Beispiel betrachten wir eine einfach verkettete lineare Liste. Die Liste selbst wird als Klasse //VerketteteListe// modelliert mit Knoten als Listenelementen. Als Attribut hat sie einen Verweis auf den //Anfang// der Liste. Die Klasse Knoten kann im ersten Attribut Daten speichern kann und im Attribut //Nächster// den Verweis auf einen nächsten Knoten der verketteten Liste. Hat ein Knoten keinen nächsten Knoten, so erhält dieses Attribut den Wert //None//. class Knoten: def __init__(self, Daten): self.Daten = Daten self.Nächster = None Die Implementierung einer Einfügeoperation ist schwierig, weil vier Fälle zu unterscheiden sind: - Die Liste ist leer. - Es soll am Anfang eingefügt werden. - Es soll am Ende eingefügt werden. - Es soll zwischen zwei Knoten eingefügt werden. Die beiden ersten Fälle lassen sich mit der Methode //einfügen_AmAnfang()// implementieren: def einfügen_AmAnfang(self, einKnoten): einKnoten.Nächster = self.Anfang self.Anfang = einKnoten Im UML-Fenster lässt sich die Implementierung testen. Dazu erzeugt man eine verkettete Liste und einen Knoten, ruft über das Objekt //verketteteliste1// die Methode //einfügen_AmAnfang()// auf und gibt für den Parameter einKnoten den Wert //knoten1// an. {{de:liste1.png}} Zum Einfügen am Ende der Liste muss man sich mit einer Schleife und einer Cursor-Variablen zum letzten Element durchhangeln. Dort wird der neue Knoten angefügt. def einfügen_AmEnde(self, einKnoten): if self.Anfang is None: self.Anfang = einKnoten else: Cursor = self.Anfang while Cursor.Nächster is not None: Cursor = Cursor.Nächster Cursor.Nächster = einKnoten Am schwierigsten ist das Einfügen vor einem Knoten, denn der neue Knoten muss dann hinter dem vorhergehenden Knoten eingefügt werden. Neben dem Cursor braucht man dazu einen weiteren Verweise auf den vorherigen Knoten. Der Knoten vor dem einfügt werden soll wird durch seine Daten bestimmt. def einfügen_vor(self, Daten, einKnoten): if self.Anfang.Daten == Daten: self.einfügen_amAnfang(einKnoten) return vorheriger_Knoten = self.Anfang Cursor = vorheriger_Knoten.Nächster while Cursor is not None and Cursor.Daten != Daten: vorheriger_Knoten = Cursor Cursor = Cursor.Nächster if Cursor is not None: einKnoten.Nächster = Cursor vorheriger_Knoten.Nächster = einKnoten else: raise Exception("Knoten mit Daten '%s' nicht gefunden" % Daten) Vergleichbar schwierig ist das Löschen eines Knotens, denn zum Löschen muss der vorherige Knoten mit dem nachfolgenden verbunden werden. def löschen(self, Daten): if self.Anfang.Daten == Daten: self.Anfang = self.Anfang.Nächster else: vorheriger_Knoten = self.Anfang Cursor = vorheriger_Knoten.Nächster while Cursor is not None and Cursor.Daten != Daten: vorheriger_Knoten = Cursor Cursor = Cursor.Nächster if Cursor is not None: vorheriger_Knoten.Nächster = Cursor.Nächster Durch interaktives Testen im UML-Fenster kann man die Funktionsfähigkeit der implementierten Methoden sehr gut prüfen, denn das Ergebnis wird unmittelbar im Objektdiagramm visualisiert. Man erkennt sofort, ob eine Methode wie gedacht funktioniert hat. Im Bild wurde zum Testen der //löschen()//-Methode die Liste mit den Elementen 'a, 'b' und 3 erzeugt und dann der Knoten mit Daten = 'b' gelöscht. Das hat funktioniert, denn die Liste besteht nur noch aus den beiden Elementen 'a' und 3. {{de:liste.png}} **Wichtiger Hinweis** Im UML-Fenster werden zur Identifizierung der Objekte deren Adressen benutzt. Im Beispiel also die Adresse 0x03350E98 für das Objekt knoten1. >>> knoten1 = Knoten('a') >>> print(knoten1) <__main__.Knoten object at 0x03350E98> Implementieren Sie daher keine %%__repr__%%-Funktion für ihre Klassen, weil diese Adressen dann nicht mehr zur Verfügung stehen. **Download**: *[[https://www.guipy.de/examples/de/liste.zip |liste.zip]]