Lineare Gleichungen sind eine grundlegende Form von mathematischen Gleichungen, die in vielen verschiedenen Anwendungen auftreten. Sie beschreiben eine Beziehung zwischen Variablen, die linear miteinander verbunden sind. Eine lineare Gleichung hat in der Regel die Form:
ax + b = c
Hier sind die Hauptkomponenten einer linearen Gleichung:
1. Variablen (‘x’): Dies ist der unbekannte Wert, den du herausfinden möchtest. Es ist die Variable, die in der Gleichung gesucht wird.
2. Koeffizienten (‘a’ und ‘b’): Dies sind bekannte Zahlen, die die lineare Beziehung zwischen der Variablen ‘x’ und dem konstanten Wert ‘c’ beschreiben.
- Der Koeffizient ‘a’ ist der Koeffizient der Variablen ‘x’. Er zeigt an, um wie viel sich ‘x’ ändert, wenn sich ‘x’ um 1 ändert.
- Der Koeffizient ‘b’ ist ein Konstantenterm, der den Schnittpunkt der linearen Funktion mit der y-Achse darstellt. Es ist der Wert von ‘x’, wenn ‘x’ gleich null ist.
3. Konstante (‘c’): Dies ist der bekannte Wert, zu dem die linke Seite der Gleichung gleich sein soll.
Das Ziel beim Lösen einer linearen Gleichung besteht darin, den Wert der Variablen ‘x’ zu finden, der die Gleichung wahr macht. In anderen Worten, du suchst den Wert von ‘x’, der die Gleichung ‘ax + b = c’ erfüllt.
Hier sind einige grundlegende Schritte, um eine lineare Gleichung zu lösen:
1. Isolieren der Variablen ‘x’ Versuche, die Variable ‘x’ auf einer Seite der Gleichung zu isolieren, indem du mathematische Operationen anwendest, um die Gleichung zu vereinfachen. Ziel ist es, ‘x’ alleine auf einer Seite der Gleichung zu haben.
2. Berechnen des Wertes von ‘x’: Nachdem die Gleichung in der Form ‘x = ...’ vorliegt, berechne den Wert von ‘x’.
3. Überprüfen der Lösung: Setze den berechneten Wert von ‘x’ in die ursprüngliche Gleichung ein, um sicherzustellen, dass sie wahr ist.
Beispiel:
Angenommen, wir haben die Gleichung ‘2x + 3 = 7’. Um ‘x’ zu isolieren, subtrahieren wir zuerst 3 von beiden Seiten der Gleichung:
2x + 3 - 3 = 7 - 3
2x = 4
Jetzt teilen wir beide Seiten der Gleichung durch 2, um ‘x’ zu isolieren:
(2x)/2 = 4/2
x = 2
Die Lösung der Gleichung ist ‘x = 2’, und wenn wir diese Lösung in die ursprüngliche Gleichung einsetzen (‘2 * 2 + 3 = 7’), ergibt sich ‘4 + 3 = 7’, was wahr ist. Daher ist ‘x = 2’ die richtige Lösung.
Klingt jetzt vielleicht ein klein wenig kompliziert das in ein Programm umzuwandeln, ist es jedoch nicht. Die einfachste Möglichkeit ist es die Gleichung einfach numerisch zu lösen, das heißt wir setzen einfach Werte für x ein und machen das so lange bis die Gleichung stimmt.
Das ganze ist nicht sehr effizient und kann vor allem bei großen Werten etwas langwierig sein.
Eine andere Herausforderung ist das sogenannte parsen der Formel. Mit ‘4x+6=10’ fängt der Computer nichts an - es ist für ihn einfach ein String. Wir müssen die Formel zerlegen, damit wir sie für ein Programm verständlich machen können.
Dank der vielen Bibliotheken von Python und einer, für unsere Linearen Gleichungen Übungsprogramm, können wir es einfach halten.
Für unsere Zwecke ist die Bibliothek SymPy bestens geeignet.
SymPy ist eine Open-Source-Bibliothek und frei verfügbar.
SymPy ist eine leistungsstarke Python-Bibliothek für symbolische Mathematik. Sie bietet eine umfassende Sammlung von Funktionen und Werkzeugen, die es ermöglichen, mathematische Berechnungen in Python auf symbolischer Ebene durchzuführen. Im Gegensatz zu numerischer Mathematik, bei der Werte berechnet werden, ermöglicht symbolische Mathematik die Manipulation von mathematischen Ausdrücken, Variablen und Symbolen, um komplexe mathematische Probleme zu lösen.
Mit SymPy kannst du eine Vielzahl von mathematischen Aufgaben erledigen, darunter:
Nun aber genug mit der ganzen Theorie fangen wir an in die Tasten zu hauen und uns einen Trainer für die linearen Gleichungen zu schreiben.
Als erstes müssen wir die SymPy in unserer Umgebung installieren. Dazu öffnen wir die Eingabeaufforderung und geben
“python -m pip install sympy”
ein.
Nach wenigen Sekunden ist es installiert.
So nun können wir anfangen
import random
import sympy as sp
Wir importieren die zwei benötigten Bibliotheken. Sympy geben wir einen sogenannten Alias damit wir keine Hornhaut an den Finger bekommen.
Als nächstes müssen wir drei Variablen (a,b,c) deklarieren und diese dann mit Zufallszahlen befüllen.
Das ganze packen wir gleich in eine Funktion
def lineare_gleichung_loesen():
# Eingabe der Gleichung
a = random.randint(1, 10)
b = random.randint(1, 10)
c = random.randint(1, 10)
print(f"Loese die linearen Gleichung '{a}x + {b} = {c}'")
Der Einfachheit nehmen wir für a, b und c Zahlen von 1 bis 10.
Das Programm würde jetzt z.B. 1x+5=5 ausgeben.
Nun muss der User diese Gleichung lösen und die Antwort eingeben.
user_input = input(f"x= ")
Nun müssen wir nun noch dem Programm das Lösen der Gleichung beibringen.
Dazu müssen wir mit SymPy die Gleichung erstellen.
# Gleichung erstellen
x = sp.symbols('x')
gleichung = sp.Eq(a * x + b, c)
Das war ja ganz einfach, oder?
Nun lassen wir die Gleichung von SymPy lösen.
# Gleichung lösen
loesung = sp.solve(gleichung, x)
So nun ist unser kleiner Trainer fertig
Das ganze Programm
import random
from fractions import Fraction
import sympy as sp
def lineare_gleichung_loesen():
# Eingabe der Gleichung
a = random.randint(1, 10)
b = random.randint(1, 10)
c = random.randint(1, 10)
print(f"Loese die linearen Gleichung '{a}x + {b} = {c}'")
user_input = input(f"x= ")
# Gleichung erstellen
x = sp.symbols('x')
gleichung = sp.Eq(a * x + b, c)
# Gleichung lösen
loesung = sp.solve(gleichung, x)
# Ergebnis ausgeben
print(f"Die Loesung der Gleichung {a}x + {b} = {c} ist x =", loesung)
if float(Fraction(user_input)) == float(loesung[0]):
print("RICHTIG!!");
else:
print ("Leider falsch!");
if __name__ == "__main__":
while True:
lineare_gleichung_loesen()
weitermachen = input("Willst du eine weitere Gleichung loesen? (Ja/Nein): ").lower()
if weitermachen != "ja":
break
Die deutsche Sprache ist bekannt für ihre Grammatik, von vielen - nicht nur Nicht-Muttersprachlern - gefürchtet.
Es gibt einige Regeln und Konventionen, die die Bildung von Wörtern, Sätzen und Texten erst zu einem Erlebnis machen.
An erster Stelle gibt es die Artikel. Im Gegensatz zum Englischen gibt es im Deutschen grundlegend drei: der, die, das. Dies sind die bestimmten Artikel. Daneben gibt es unbestimmte und Nullartikel, um das Geschlecht, den Numerus (Singular oder Plural) und den Kasus (Nominativ, Genitiv, Dativ, Akkusativ) von Substantiven anzuzeigen.
Zum Beispiel: "der Tisch" (der bestimmte Artikel im Nominativ), "ein Buch" (der unbestimmte Artikel im Akkusativ). Eine Beispiel für die Verwendung des Nullartikels ist: “Ich wohne in Österreich.”
Substantive sind Hauptwörter, die Personen, Orte, Dinge oder Konzepte bezeichnen. Sie haben ein grammatisches Geschlecht (männlich, weiblich, sächlich) und können im Singular oder Plural stehen.
Adjektive beschreiben Substantive und passen sich in Geschlecht, Numerus und Kasus an das zugehörige Substantiv an. Zum Beispiel: "großer Tisch" (männlich, Nominativ), "große Blumen" (weiblich, Nominativ).
Verben sind Handlungs- oder Tätigkeitswörter und bilden die Grundlage für Sätze. Sie können verschiedene Zeiten (Präsens, Präteritum, Perfekt, Futur), Modi (Indikativ, Konjunktiv) und Personen (ich, du, er/sie/es) haben.
Personalpronomen ersetzen Substantive und variieren je nach Geschlecht, Numerus und Kasus. Zum Beispiel: "ich" (Nominativ), "mich" (Akkusativ).
Präpositionen sind Wörter, die die Beziehung zwischen Substantiven und anderen Elementen im Satz beschreiben. Sie regieren bestimmte Kasus und können den Ort, die Zeit oder die Art und Weise angeben. Zum Beispiel: "auf dem Tisch" (Lokalkasus), "nach der Schule" (Temporal).
Verben werden im Deutschen konjugiert, was bedeutet, dass sie ihre Form je nach Person und Zeit ändern. Die Konjugation folgt bestimmten Mustern, die für verschiedene Verbklassen unterschiedlich sein können.
Die deutsche Satzstruktur folgt einem festen Wortstellungsmuster, bei dem das finite Verb in Position 2 im Hauptsatz steht. Die Reihenfolge der anderen Satzteile kann variieren, abhängig von Betonung und Kontext.
So mit diesem geballten Wissen können wir uns einen Trainer überlegen. Da wir möglichst viel abdecken wollen, überlegen wir uns erst einmal mögliche Fragen:
So auf die Schnelle fallen uns folgende Fragen ein:
Was ist der bestimmte Artikel für 'Blume'?
Was ist der unbestimmte Artikel für 'Haus'?
Gib das Präteritum von 'kommen'.
Bilde den Plural von 'Kind'.
Was ist der Akkusativ von 'sie'?
Was ist der Dativ von 'er'?
Einfach wäre es nun die Fragen und die Antworten in eine Datei zu schrieben und wie bei den vorhergehenden Trainern. Dies ist aber nicht so schön. Schöner ist es die Fragen und Antworten in eine Datenbank zu schreiben.
Einfach ausgedrückt ist eine Datenbank einfach ein Ort, wo beliebige Daten gespeichert werden. Diese Sammlung von Daten ist strukturiert und effizient gespeichert, abgerufen, verwaltet und aktualisiert werden. Alle großen Apps wie Facebook, X, Amazon haben im Hintergrund Datenbanken laufen.
Wir verwenden für unseren Grammatik Trainer eine kleine simple Datenbank namens SQLite3. Die ist für uns vollkommen ausreichend. Das gute ist sie ist in Python schon vorhanden. Wir können also gleich loslegen.
Wie immer müssen wir die gewünschte Bibliothek importieren.
import sqlite3
Wir haben die erste Hürde überwunden und können die Datenbank anlegen.
Dies machen wir mit dem connect Befehl. Wenn es die Datenbank nicht gibt wird sie einfach angelegt, darum müssen wir uns nicht kümmern.
# Verbindung zur SQLite-Datenbank herstellen oder erstellen
conn = sqlite3.connect("deutscheGrammatik.db")
So als nächstes müssen wir einen sogenannten Cursor definieren mit dem wir dann sogenannte SQL Befehle absetzen können.
Aber vorher ein kurzer Exkurs:
SQL (Structured Query Language) ist eine spezielle Programmiersprache, die für die Verwaltung und Abfrage von relationalen Datenbanken entwickelt wurde. Hier ist eine kurze Einführung in SQL:
Das mag jetzt kompliziert klingen und jetzt noch eine zweite Programmiersprache dazu, aber für unsere Zwecke reichen einige wenige Befehle - also keine Angst.
Wir brauchen um unsere Fragen und die Antworten zu speichern eine Tabelle. Wir nennen sie Fragen. In dieser Tabelle brauchen wir zwei Spalten, eine für die Frage und eine für die Antwort. Jede Tabelle sollte einen eindeutigen Schlüsselwert, den sogenannten Key, haben. So ein Key kann eine Zahl, aber auch ein Text sein - die meisten Datenbanken tun sich mit einer Zahl aber am einfachsten. Also definieren wir noch eine Key und nennen sie id.
Anhand unserer Überlegungen können, wir nun ein SQL basteln das uns die Tabelle erstellt.
Zu diesem Zweck gibt es den SQL Befehl CREATE TABLE - dann brauchen wir noch den Namen der Tabelle den wir anlegen wollen, also Fragen.
CREATE TABLE Fragen
Es folgt nun der Block in dem wir definieren, wie die Tabelle ausschauen soll, also unsere Splaten.
Zuerst der Key, der eine Zahl (Integer) ist und ein sogenannter PRIMARY KEY ist
Id INTEGER PRIMARY KEY,
Dann die frage die einen Text enthalten wird
frage TEXT
Und die dazugehörige Antwort
antwort Text
Eine Sache fehlt uns noch. Wir wollen, das die Tabelle nur einmal erstellt wird. Dafür gibt es den Befehl IF NOT EXISTS. Dies müssen wir nach CREATE TABLE einfügen.
Unser fertiges SQL lautet also:
CREATE TABLE IF NOT EXISTS Fragen ( id INTEGER PRIMARY KEY, frage TEXT, antwort TEXT)
Dieses SQL könnten wir nun direkt in einer Datenbank wie MSSQL oder Oracle mit leichter Modifikation des Syntax laufen lassen und es würde uns eine wunderschöne Tabelle erstellen.
Wir wollen das ganze in unserem Programm laufen lassen und deshalb verpacken wir das so:
# Tabelle für die Fragen erstellen, wenn sie noch nicht existiert
cursor.execute('''CREATE TABLE IF NOT EXISTS Fragen (
id INTEGER PRIMARY KEY,
frage TEXT UNIQUE,
antwort TEXT
)''')
Als nächstes müssen wir eine Funktion programmieren, mit der wir Fragen und Antworten unserer Datenbank, genauer der Tabelle, hinzufügen können. Um eine neue Frage bzw. Antwort der Tabelle hinzufügen zu können, müssen wir den “insert” Befehl auf der Datenbank ausführen.
Der “insert” Befehl ist eigentlich recht einfach.
Die grundlegende Syntax des INSERT-Befehls sieht folgendermaßen aus:
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);
Der INSERT-Befehl in SQL wird verwendet, um neue Datensätze in eine Tabelle einer Datenbank einzufügen. Er ermöglicht das Hinzufügen von Daten in eine vorhandene Tabelle, wobei die Daten in die entsprechenden Spalten eingefügt werden. Hier ist eine Beschreibung des INSERT-Befehls in SQL:
Die grundlegende Syntax des INSERT-Befehls sieht folgendermaßen aus:
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);
table_name: Hier wird der Name der Tabelle angegeben, in die die Daten eingefügt werden sollen.
column1, column2, column3, ...: Dies sind die Namen der Spalten in der Tabelle, in die Daten eingefügt werden sollen. Sie sollten in Klammern nach table_name aufgelistet werden.
VALUES: Dieses Schlüsselwort signalisiert den Beginn der Werte, die in die Tabelle eingefügt werden.
value1, value2, value3, ...: Hier werden die tatsächlichen Werte angegeben, die in die entsprechenden Spalten eingefügt werden. Die Reihenfolge der Werte sollte der Reihenfolge der Spalten entsprechen.
Angenommen wir wollen nun die Frage “Was ist der unbestimmte Artikel für 'Haus'? “ mit der Antwort “ein” unserer Datenbank hinzufügen, dann würde unser INSERT Befehl so aussehen:
INSERT INTO FRAGEN (frage, antwort) VALUES (‘Was ist der unbestimmte Artikel für “Haus”? ‘,’ein’)
Was fällt uns gleich auf. Es fehlt ein Feld - die id! Das ist jedoch kein Fehler, sondern eine Eigenart. Die Spalte id da wir sie als Primary Key deklariert haben, wird der Wert von SQLite automatisch beim insert vergeben und wir sind fein raus weil wir uns darum nicht kümmern müssen.
In unserem Code definieren wir nun eine Funktion namens frage_hinzufuegen und führen unser insert aus.
# Eine Frage / Antwort der Tabelle hinzufügen
def frage_hinzufuegen (frage, antwort):
cursor.execute("INSERT INTO Fragen (frage, antwort, ) VALUES (?, ?)",
(frage, antwort))
conn.commit()
Print(f"Die Frage '{frage}' wurde zur Datenbank hinzugefügt.")
Wichtig ist das wir conn.commit() aufrufen, damit wir das ganze auch auf der Datenbank ausgeführt wird.
So als nächstes müssen wir nun eine Funktion bauen, die einen zufälligen Datensatz aus unserer Tabelle lesen.
Dazu gibt es in SQL den SELECT-Befehl. SELECT wird verwendet, um Daten aus einer Datenbank abzurufen. Er ermöglicht es, bestimmte Spalten aus einer oder mehreren Tabellen auszuwählen und optional Filterkriterien anzuwenden. Hier ist die grundlegende Syntax des SELECT-Befehls:
SELECT column1, column2, ...
FROM table_name
WHERE condition;
1. `SELECT column1, column2, ...`: Hier können wir die Namen der Spalten angeben, die wir aus der Tabelle abrufen möchten. Mit `SELECT *` können wir alle Spalten abrufen.
2. `FROM table_name`: In diesem Teil des SQL Befehls definieren wir aus welcher Tabelle wir die Daten abrufen möchsten.
3. `WHERE condition` (optional): Dieser Teil des Befehls ermöglicht es uns, Bedingungen festzulegen, die die ausgewählten Datensätze einschränken. Nur die Datensätze, die die angegebene Bedingung erfüllen, werden zurückgegeben. Wenn wir keine Bedingung angeben, werden alle Datensätze zurück geliefert.
4. ‘ORDER by column1, column2,….’ (optional): Mit dieser Anweisung können wir das Ergebnis sortieren. Wenn wir mehr als eine Spalte angeben dann wird zuerst nach der ersten Spalte, dann nach der zweiten, usw. Sortiert.
Zum Beispiel, wenn Sie alle Spalten aus der Tabelle aus unserer Tabelle Fragen ausgeben werden, würde der SQL-Befehl so aussehen:
SELECT * FROM Fragen;
Nach diesem kurzen Exkurs kehren wir wieder zurück zu unserem Programm. Wir wollen ja nicht alle sondern nur eine zufällige Zeile auslesen.
Dazu bietet SQLite einen Befehl ORDER BY RANDOM().
Wenn wir unser Select von oben um ein ORDER BY RANDOM() erweitern, so bekommen wir unsere Fragen in zufälliger Reihenfolge.
Das ist schon nah dran an dem was wir wollen, jedoch brauchen wir ja nur eine. Dazu können wir den SQL Befehl LIMIT 1 hinzufügen.
So nun müssen wir das ganze nur noch in eine Python Funktion packen.
def frage_auslesen():
conn = sqlite3.connect("Gramatikfragen.db")
# Ein Cursor-Objekt erstellen, um Datenbankabfragen auszuführen
cursor = conn.cursor()
cursor.execute("Select * from FRAGEN ORDER BY RANDOM() LIMIT 1")
frage = cursor.fetchone()
conn.close()
return frage
Eine Funktion fehlt uns noch, die was uns die Frage stellt. Die klopfen wir nach Schema F rein:
def frage_stellen():
# Zufällige Auswahl einer Frage
rowid,frage, antwort = frage_auslesen()
print(frage)
benutzerantwort = input("Antwort: ").strip().lower()
if benutzerantwort == antwort:
print("Richtig!\n")
return True
else:
print(f"Falsch. Die richtige Antwort ist '{antwort}'.\n")
return False
Alles zusammengefügt ergibt
import sqlite3
def init():
fragen = [
("Was ist der bestimmte Artikel für 'Stuhl'?", "der"),
("Was ist der unbestimmte Artikel für 'Buch'?", "ein"),
("Gib das Präteritum von 'haben'.", "hatte"),
("Bilde den Plural von 'Tisch'.", "Tische"),
("Was ist der Akkusativ von 'ich'?", "mich"),
("Was ist der Dativ von 'du'?", "dir"),
("Welches Verb passt in die Lücke: 'Ich ____ gerne Pizza.' (essen)", "esse"),
("Bilde den Komparativ von 'gut'.", "besser"),
("Bilde den Superlativ von 'viel'.", "am meisten"),
("Was ist das Partizip II von 'arbeiten'?", "gearbeitet"),
("Was ist der Genitiv von 'das Buch'?", "des Buches"),
("Was ist das Reflexivpronomen für 'wir'?", "uns"),
("Was ist der Imperativ für 'kommen' (du-Form)?", "komm"),
("Bilde einen Satz im Konjunktiv II: 'gehen' (er/sie/es).", "er/sie/es ginge")
]
# Verbindung zur SQLite-Datenbank herstellen oder erstellen
conn = sqlite3.connect("Gramatikfragen.db")
# Ein Cursor-Objekt erstellen, um Datenbankabfragen auszuführen
cursor = conn.cursor()
# Tabelle für die Fragen erstellen, wenn sie noch nicht existiert
cursor.execute("CREATE TABLE IF NOT EXISTS Fragen (id INTEGER PRIMARY KEY,frage TEXT UNIQUE,antwort TEXT)");
conn.close();
#Demo Fragen hinzufügen
for val in fragen:
frage_hinzufuegen(val[0],val[1])
# Eine Frage / Antwort der Tabelle hinzufügen
def frage_hinzufuegen (frage, antwort):
conn = sqlite3.connect("Gramatikfragen.db")
# Ein Cursor-Objekt erstellen, um Datenbankabfragen auszuführen
cursor = conn.cursor()
cursor.execute("INSERT INTO Fragen (frage, antwort ) VALUES (?, ?) ON CONFLICT(frage) DO Update SET antwort=excluded.antwort",
(frage, antwort))
conn.commit()
conn.close()
def frage_auslesen():
conn = sqlite3.connect("Gramatikfragen.db")
# Ein Cursor-Objekt erstellen, um Datenbankabfragen auszuführen
cursor = conn.cursor()
cursor.execute("Select * from FRAGEN ORDER BY RANDOM() LIMIT 1")
frage = cursor.fetchone()
conn.close()
return frage
def frage_stellen():
# Zufällige Auswahl einer Frage
rowid,frage, antwort = frage_auslesen()
print(frage)
benutzerantwort = input("Antwort: ").strip().lower()
if benutzerantwort == antwort:
print("Richtig!\n")
return True
else:
print(f"Falsch. Die richtige Antwort ist '{antwort}'.\n")
return False
def main():
print("Willkommen zum Deutsch Grammatik Übungsprogramm!\n")
init()
anzahl_fragen = int(input("Wie viele Fragen möchtest du beantworten? "))
richtig = 0;
for _ in range(anzahl_fragen):
if frage_stellen() == True:
richtig = richtig+1
print(f"Du hast von {anzahl_fragen} Fragen {richtig} richtig beantwortet. Vielen Dank!")
if __name__ == "__main__":
main()
Grundsätzlich ist das Periodensystem der chemischen Elemente eine strukturierte und tabellarische Darstellung aller bekannten chemischen Elemente.
Bereits in der Antike hatten die Menschen schon Kenntnisse über chemische Elemente wie Gold, Silber, Eisen und Kupfer. Im Laufe der Jahrhunderte sammelten Gelehrte Daten über verschiedene Stoffe - sie alle konnten jedoch kein Muster finden.
Erst im späten 18. Jahrhundert legte der französische Chemiker Antoine Lavoisier den Grundstein für die moderne Chemie, indem er das Gesetz der Erhaltung der Masse formulierte und die chemischen Elemente neu klassifizierte. Er identifizierte und benannte einige der elementaren Bestandteile der Materie.
Im Jahr 1864 stellte der englische Chemiker John Newlands eine Liste der Elemente vor und ordnete sie nach ihrem Atomgewicht. Er bemerkte, dass die Elemente in Achtelgruppen (Oktaven) mit ähnlichen Eigenschaften angeordnet zu sein schienen. Seine Ideen wurden jedoch nicht weit verbreitet akzeptiert.
Fünf Jahre später verfasste der russische Chemiker Dimitri Mendelejew das erste umfassende Periodensystems. Er ordnete die bekannten Elemente in einer Tabelle nach aufsteigendem Atomgewicht und identifizierte Muster und Periodizitäten in ihren Eigenschaften. Mendelejew erkannte, dass es noch unbekannte Elemente geben musste, die in die leeren Stellen in seiner Tabelle passten. Er sagte die Eigenschaften dieser Elemente vorher und nannte sie "Ekasilicium" und "Ekaaluminium". Diese Vorhersagen erwiesen sich später als richtig und führten zur Entdeckung von Germanium und Gallium.
Der britische Physiker Henry Moseley trug im frühen 20. Jahrhundert wesentlich zur Verbesserung des Periodensystems bei, indem er das Konzept der Atomnummer einführte. Er ordnete die Elemente nach ihrer Ordnungszahl (Anzahl der Protonen im Kern) an, anstatt nach dem Atomgewicht. Dies führte zu einer genaueren und logischeren Anordnung der Elemente im Periodensystem.
Das Periodensystem ist zu einem grundlegenden Werkzeug für die Chemie geworden und ermöglicht das Verständnis der Beziehungen zwischen den Elementen und die Vorhersage ihres Verhaltens. Es ist eines der wichtigsten Konzepte in der Chemie und wird kontinuierlich erforscht und erweitert, um unser Verständnis der chemischen Welt zu vertiefen.
Da wir aus Interesse oder der Schule wegen uns das Perioden System auswendig erlernen können und dies auch noch Spaß machen sollte, bauen wir uns ein Periodensystem Quiz.
In den bisherigen Beispielen haben wir uns nur auf der Text Ebene bewegt und uns um das GUI (das User Interface) eigentlich nicht gekümmert. Jedoch haben wir viel dabei gelernt. Als ersten Schritt zu unserem Quiz schauen wir uns einmal eine UI - Bibliothek an.
Die Bibliothek die wir verwenden werden ist PySimpleGUI.
PySimpleGUI ist eine Python-Bibliothek, die entwickelt wurde, um die Erstellung von grafischen Benutzeroberflächen (GUIs) einfach und intuitiv zu gestalten.
Der Name "Simple" in PySimpleGUI spiegelt das Hauptziel der Bibliothek wider: Sie soll einfach zu erlernen und zu verwenden sein. Die API ist so gestaltet, dass sie leicht verständlich ist und weniger Boilerplate-Code erfordert als einige andere GUI-Bibliotheken.

Zuerst müssen wir das Paket installieren. Im Visual Studio öffnen wir unser Projekt und öffnen unter Python-Umgebung Pyhton 3.9.
Dort machen wir einen Rechts-Click und wählen Python Pakete verwalten aus. Nun geben wir im Eingabefeld PySimpleGUI ein. Unten erscheint nun eine Liste und wir wählen PySimpleGUI aus. Wie von Geisterhand wird es vom Visual Studio installiert.
Den schwierigsten Teil haben wir nun erfolgreich hinter uns gebracht. Nun können wir mit dem lustigen Part, dem Programmieren, beginnen.
Als erstes machen wir zum Einstieg einfach ein Fenster mit einem Button.
import PySimpleGUI as sg
layout = [
[sg.Button('Close', button_color=('white', 'black'), key='close')]
]
window = sg.Window("Periodensystem", layout, auto_size_buttons=False, default_button_element_size=(12,1), use_default_focus=False, finalize=True)
while True:
event, values = window.read(timeout=100)
if event == "close":
break
if event == sg.WINDOW_CLOSED:
break
window.close()
So schauen wir uns den Code näher an:
`import PySimpleGUI as sg`: Dieser Befehl importiert die PySimpleGUI-Bibliothek und stellt sie als `sg` zur Verfügung, was die Verwendung von PySimpleGUI-Elementen im Code erleichtert.
Unser Augenmerk richtet sich auf die Variabel `layout`. In dieser Liste ist die die Struktur des GUI-Layouts definiert. In unserem Fall enthält sie eine Liste mit einer Schaltfläche (Button) namens "Close". Die Schaltfläche wird in einem separaten Unterlayout definiert, um das Layout flexibler zu gestalten.
Mit `window` wird ein Fensterobjekt erstellt und initialisiert. Der Name des Fensters lautet "Periodensystem".
Das `layout` wird dem Fenster übergeben, und verschiedene Parameter werden gesetzt, um das Layout und das Verhalten des Fensters zu konfigurieren, z.B., die Größe der Schaltfläche, den Fokus und die Verwendung der Standardtasten. Das `finalize=True` stellt sicher, dass das Layout endgültig erstellt wird.
Die `while`-Schleife sorgt dafür, dass das GUI-Fenster geöffnet bleibt und Benutzereingaben überwacht wird.
Die Zeile `event, values = window.read(timeout=100)` liest Ereignisse (Events) und Werte (Values) aus dem GUI-Fenster. Das `timeout=100` sorgt dafür, dass die `window.read`-Funktion alle 100 Millisekunden aufgerufen wird, um auf Benutzeraktionen zu reagieren. Das Ergebnis von `window.read()` wird in den Variablen `event` und `values` gespeichert.
Die nächsten beiden `if`-Anweisungen prüfen auf verschiedene Ereignisse:
- `if event == "close":` überprüft, ob der Benutzer auf den "Close"-Button geklickt hat. Wenn dies der Fall ist, wird die Schleife mit `break` beendet, und das GUI-Fenster wird geschlossen.
- `if event == sg.WINDOW_CLOSED:` prüft, ob das GUI-Fenster auf eine andere Weise geschlossen wurde (z.B., durch Klicken auf das Schließen-Symbol oben rechts im Fenster). In diesem Fall wird die Schleife ebenfalls mit `break` beendet, und das GUI-Fenster wird geschlossen.
Schließlich wird `window.close()` aufgerufen, um das GUI-Fenster ordnungsgemäß zu schließen, bevor das Programm beendet wird.
Unser Beispiel macht schon mal ein Fenster auf. Nun müssen wir uns überlegen wie wir unser Quiz anlegen wollen.
Das einfachste ist wir fragen den User nach dem Namen, des Elements also zum Beispiel Eisen und der User muss auf das Element klicken - also Fe.
Das Periodensystem ist ja in Spalten und Zeilen aufgebaut. Wir brauchen jedoch die Daten, diese können wir jedoch zusammenstellen. Dem Visual Studio Projekt ist eine beigefügt.
Sie schaut so aus:
Ordnungszahl,Element,Symbol,Atomgewicht,Periode,Gruppe,Phase,Stabilster Kristall,Typ,Ionenradius,Atomradius,Elektronegativität,Erstes Ionisierungspotential,Dichte,Schmelzpunkt (K),Siedepunkt (K),Isotope,Entdecker,Entdeckungsjahr,Spezifische Wärmekapazität,Elektronenkonfiguration,Anzeigezeile,Anzeigespalte
1,Hydrogen,H,1.00794,1,1,gas,,Nonmetal,0.012,0.79,2.2,13.5984,0.00008988,14.175,20.28,3,Cavendish,1766,14.304,1s1,1,1
2,Helium,He,4.002602,1,18,gas,,Noble Gas,,0.49,,24.5874,0.0001785,,4.22,5,Janssen,1868,5.193,1s2,1,18
3,Lithium,Li,6.941,2,1,solid,bcc,Alkali Metal,0.76,2.1,0.98,5.3917,0.534,453.85,1615,5,Arfvedson,1817,3.582,[He] 2s1,2,1
4,Beryllium,Be,9.012182,2,2,solid,hex,Alkaline Earth Metal,0.35,1.4,1.57,9.3227,1.85,1560.15,2742,6,Vaulquelin,1798,1.825,[He] 2s2,2,2
5,Boron,B,10.811,2,13,solid,rho,Metalloid,0.23,1.2,2.04,8.298,2.34,2573.15,4200,6,Gay-Lussac,1808,1.026,[He] 2s2 2p1,2,13
Das ganze ist eine CSV Datei. Wir richten unser Augenmerk auf die letzten zwei Spalten Anzeigespalte und Anzeigespalte. Mit diesen zwei Variablen können wir das Periodensystem leicht aufbauen.
Als erstes schreiben wir eine Funktion, mit der wir die Daten einlesen können. Dazu verwenden wir eine neue Bibliothek namens CSV.
Das CSV (Comma-Separated Values) Paket ist eine Python-Bibliothek, die es ermöglicht, CSV-Dateien in Python zu lesen und zu schreiben. CSV-Dateien sind eine gängige Möglichkeit, Daten in tabellarischer Form zu speichern und auszutauschen, wobei die Werte in den Spalten durch Kommas oder andere Trennzeichen getrennt sind.
Mit dem CSV Paket können wir CSV Dateien lesen und in Datenstrukturen wie Listen oder Dictionaries speichern. Dies erleichtert die Verarbeitung und Analyse von Daten aus externen Quellen.
Beispiel zum Lesen einer CSV-Datei:
import csv
with open('datei.csv', 'r', newline='') as datei:
csv_reader = csv.reader(datei)
for zeile in csv_reader:
print(zeile)
Mit dem `csv` Paket können wir auch Daten aus Python in eine CSV-Datei schreiben. Dies ist nützlich, um Ergebnisse oder Analysen in einem einfach zu lesenden Format zu speichern.
Beispiel zum Schreiben in eine CSV-Datei:
import csv
daten = [
['Name', 'Alter', 'Stadt'],
['Alice', 30, 'Graz'],
['Bob', 25, 'München']
]
with open('ergebnisse.csv', 'w', newline='') as datei:
csv_writer = csv.writer(datei)
csv_writer.writerows(daten)
Für unsere Periodensystem.csv schaut der Code zum Einlesen wie folgt aus:
def PeriodensystemEinlesen():
# Dateiname der CSV-Datei
csv_datei = 'PeriodicTableofElements.csv'
# Liste zum Speichern der Daten
daten = []
# Öffne die CSV-Datei und lies sie ein
with open(csv_datei, 'r', newline='',encoding='latin-1') as datei:
csv_reader = csv.DictReader(datei)
for zeile in csv_reader:
daten.append(zeile)
return daten
Diese Funktion gibt uns alle Felder des Periodensystems zurück.
Nun müssen wir noch das Layout aufbauen. Das Periodensystem ist Gott-Sei-Dank recht stabil (außer im CERN finden sie ein neues Element) und, wie schon erwähnt, in Spalten und Zeilen aufgeteilt. Das macht uns das Leben einfacher
ps_elemente = PeriodensystemEinlesen();
layout = [[NULL for _ in range(18)] for _ in range(9)]
for row in range(9):
for col in range(18):
element = findeElementAnPos(ps_elemente,row+1,col+1)
if element != NULL:
layout[row][col] = sg.Button(element['Symbol'], size=(4, 2))
else:
layout[row][col] = sg.Push(background_color='gray')
Wir definieren uns eine Variable “layout”, die wir mit NULL (also Nichts) initialisieren. Das Array “layout” ist zweidimensional also Zeilen und Spalten. Nun iterieren wir mit den Variablen row und col durch unser layout.
Die Funktion findeElementAnPos sucht uns das richtige Element an der Position. Da nicht an jeder Position ein Element ist, verwenden wir sg.Push um eine “Lücke” zu machen.
Wenn wir an der Position ein Element gefunden haben, dann erstellen wir einen Button.
Abschließend fügen wir noch ein paar Zeilen ein um dem User, also uns, die Möglichkeit zu geben, entweder zu Spielen oder Infos über das Element zu bekommen.
layout.insert(0, [sg.Radio('Info', "Modus", default=True,enable_events=True, size=(10,1), k='-Info-'), sg.Radio('Spiel', "Modus", default=False,enable_events=True, size=(10,1), k='-Spiel-')])
layout.insert(0,[sg.Button(SPIELBUTTON_TEXT_STARTEN,key=SPIELBUTTON,visible=False), sg.Text('', key=FRAGE,visible=False, font='_ 16')])
layout.insert(0,[sg.Text('Periodensystem', font='_ 20')])
Schauen wir uns das Ganze genauer an.
In der Zeile
layout.insert(0, [sg.Radio('Info', "Modus", default=True, enable_events=True, size=(10,1), k='-Info-'), sg.Radio('Spiel', "Modus", default=False, enable_events=True, size=(10,1), k='-Spiel-')])
Fügen wir eine horizontale Zeile (Reihe) zum GUI-Layout hinzugefügt, diese enthält zwei Radiobuttons ('Info' und 'Spiel'), die wir in einer gemeinsamen Gruppe mit dem Namen "Modus" platzieren.
Wir wollen damit folgendes erreichen. Wenn wir unser Programm auf Info stellen und wir einen Button mit einem Element drücken, dann wird ein PopUp aufgemacht und die Infos über das Element ausgegeben. Wenn wir in den Spiele Modus wechseln, müssen wir das Element finden, nachdem wir gefragt werden.
Der 'Info'-Radiobutton ist standardmäßig ausgewählt (default=True).
Damit wir im Code auch mitbekommen, müssen wir beide Radiobuttons so konfigurieren, dass sie Ereignisse auslösen (enable_events=True), wenn sie ausgewählt werden.
In der nächsten Zeile fügen wir ein paar (noch) unsichtbare Elemente für den Spiele Modus hinzu:
layout.insert(0, [sg.Button(SPIELBUTTON_TEXT_STARTEN, key=SPIELBUTTON, visible=False), sg.Text('', key=FRAGE, visible=False, font='_ 16')]):
Das unsichtbare Button-Element (sg.Button, visible=False) mit dem angegebenen Text (SPIELBUTTON_TEXT_STARTEN = eine Konstante, die wir definiert haben) und einem Schlüssel (Key) SPIELBUTTON.
layout.insert(0, [sg.Text('Periodensystem', font='_ 20')])
Zu guter Letzt fügen wir noch die Überschrift ein. Da wir unser Layout mit den ganzen Elementen schon gebaut haben, müssen wir um etwas vor den Buttons einzufügen eben Insert und die Position 0 verwenden.
Das ganze schaut dann so aus:

Nun wollen wir unserem Programm die Info Funktion beibringen.
Da wir im “Info” Modus starten deklarieren wir eine Variable Spielen und setzen sie auf False.
Wenn nun ein Element Button gedrückt wird haben wir zwei Möglichkeiten - entweder es ist die Antwort auf eine Frage oder eben wir wollen Informationen zu dem Element.
element = findeElement(ps_elemente,event)
if element != NULL:
if Spielen == True:
Gesamt = Gesamt +1;
if (element['Symbol'] == AktuelleFrage['Symbol']):
sg.popup("Richtig!!")
Richtig = Richtig+1
else:
sg.popup("Falsch",f"Leider falsch!!\nDie richtige Antwort ist {AktuelleFrage['Symbol']} - {AktuelleFrage['Element']}\nDu hast {element['Symbol']} mit der Ordnungszahl {element['Atomic Number']} ausgewähl! ")
AktuelleFrage = random.choice(ps_elemente)
window[FRAGE].update(f"Welches Atom hat die Ordnungszahl {AktuelleFrage['Atomic Number']} ?")
else:
InfosAnzeigen(element)
In unserer Variable Event ist das Element enthalten, auf das wir geklickt haben. Wir suchen also mit der Funktion findeElement aus alles unseren geladenen Elementen das Element heraus. Wenn unsere Variable Spielen False ist, dann rufen wir die Funktion Infos Anziegen auf.
def infosAnzeigen(element):
elementlayout = []
for item in element:
elementlayout.append([sg.Text(item, size=(30, 1)),sg.Text(element[item])])
layout=[[sg.Column(elementlayout,scrollable=True,expand_x=True,expand_y=True)], [sg.Button('Ok')]]
window = sg.Window('Element Info', layout,resizable=True, grab_anywhere=False, size=(800, 480), return_keyboard_events=True, finalize=True)
window.BringToFront()
while True:
event, values = window.read()
if event in (None, 'Ok'):
break
Zuerst erstellen wir einen leeren Layout C0ntainer namens elementlayout, um die Layout-Elemente für die PySimpleGUI-Oberfläche zu speichern.
Nun durchlaufen wir eine Schleife für jedes Element im element-Dictionary. Für jedes Element wird ein Textfeld (sg.Text) mit einer festen Größe von 30x1 und ein weiteres Textfeld erstellt, das den Wert des aktuellen Elements enthält. Dieses Textpaar fügen wir elementlayout-Liste hinzu.
Das eigentliche GUI-Layout erstellen wir aus einer Spalte (sg.Column), die die gesammelten Elementlayouts enthält. Diese Spalte ist scrollbar, und die Größe kann sowohl horizontal als auch vertikal expandieren. Einen OK-Button fügen wir ebenfalls hinzu.
Als nächstes definieren wir ein PySimpleGUI-Fenster (sg.Window) mit unserem Layout, mit einer festen Größe von 800x480 Pixeln, der Option zur Größenänderung, der Option "grab_anywhere=False" (um das Verschieben des Fensters zu deaktivieren) und der Möglichkeit, Tastatureingaben zu verarbeiten. Nun sagen wir PySimpleGUI das wir das Fenster im Vordergrund platziert (window.BringToFront()) haben wollen.
Zum Schluss warten wir bis das Fenster geschlossen wird oder wir den OK Button drücken.
So nun zu unserem Spiel.
Wenn wir unseren Radio Button auf Spielen stellen wird die Variable auf Spielen = True umgestellt und wir machen ein paar Felder sichtbar bzw. unsichtbar.
window[SPIELBUTTON].update(visible=True)
window[SPIELBUTTON].update(SPIELBUTTON_TEXT_STARTEN)
window[FRAGE].update(visible=True)
Die update Methode verhilft uns das GUI zu verändern. Wir können entweder, wie in der ersten Zeile ein unsichtbares Element sichtbar machen (visible = True) oder aber auch einen Text ändern, wie in Zeile zwei.
Wir haben nun den Text des SPIELBUTTONs auf “Spiel starten” gestellt. Wenn dieser nun gedrückt wird, so soll das Spiel mit der ersten Frage gestartet werden.
Bei nochmaligen Drücken wird das Spiel gestoppt.
if event == SPIELBUTTON:
Spielen = not Spielen
if (Spielen == True):
window[FRAGE].update(visible=True)
AktuelleFrage = random.choice(ps_elemente)
window[SPIELBUTTON].update(SPIELBUTTON_TEXT_STOPPEN)
window[FRAGE].update(f"Welches Atom hat die Ordnungszahl {AktuelleFrage['Atomic Number']} ?")
else:
prozent = Richtig / Gesamt * 100
SpielerText = "Weiter so Du wirst immer besser!"
if prozent > 30:
SpielerText = "Sehr gut!"
if prozent > 50:
SpielerText = "Wunderbar! Du bist am bestem Weg zum Experten!"
if prozent > 70:
SpielerText = "Viel fehlt nicht mehr und Du bist Experte!"
if prozent > 80:
SpielerText = "Du bist Experte!"
sg.popup("Ergebnis",f"{SpielerText}\nDu hast {Gesamt} Fragen beantwortet davon {Richtig} Richtig!")
window[SPIELBUTTON].update(SPIELBUTTON_TEXT_STARTEN)
window[FRAGE].update(visible=False)
Gesamt =0
Richtig = 0
Wenn der Spieler mit dem Spiel aufhören will, so drückt er “Spiel beenden”. Wir geben ihm dann eine kleine Statistik und motivierende Worte aus.
Dazu verwenden wir die Funktion popup. Mit der ist es ein leichtes Nachrichten, Fehlermeldungen oder ähnliches dem User anzeigen zu lassen.
Wenn wir alles zusammenfügen erhalten wir den folgenden Code:
#encoding: latin-1
from asyncio.windows_events import NULL
import random
import csv
import PySimpleGUI as sg
def PeriodensystemEinlesen():
# Dateiname der CSV-Datei
csv_datei = 'PeriodicTableofElements.csv'
# Liste zum Speichern der Daten
daten = []
# Öffne die CSV-Datei und lies sie ein
with open(csv_datei, 'r', newline='',encoding='latin-1') as datei:
csv_reader = csv.DictReader(datei)
for zeile in csv_reader:
daten.append(zeile)
return daten
def findeElementAnPos(elemente,zeile,spalte):
for row in elemente:
if int(row['Display Row']) == zeile and int(row['Display Column']) == spalte:
return row
return NULL
def findeElement(elemente,symbol):
for row in elemente:
if row['Symbol'] == symbol:
return row
return NULL
def infosAnzeigen(element):
elementlayout = []
for item in element:
elementlayout.append([sg.Text(item, size=(30, 1)),sg.Text(element[item])])
layout=[[sg.Column(elementlayout,scrollable=True,expand_x=True,expand_y=True)], [sg.Button('Ok')]]
window = sg.Window('Element Info', layout,resizable=True, grab_anywhere=False, size=(800, 480), return_keyboard_events=True, finalize=True)
window.BringToFront()
while True:
event, values = window.read()
if event in (None, 'Ok'):
break
SPIELBUTTON="--spiel--"
SPIELBUTTON_TEXT_STARTEN = "Spiel starten"
SPIELBUTTON_TEXT_STOPPEN = "Spiel beenden"
FRAGE = "--frage--"
AktuelleFrage = NULL
ps_elemente = PeriodensystemEinlesen();
layout = [[NULL for _ in range(18)] for _ in range(9)]
for row in range(9):
for col in range(18):
element = findeElementAnPos(ps_elemente,row+1,col+1)
if element != NULL:
layout[row][col] = sg.Button(element['Symbol'], size=(4, 2))
else:
layout[row][col] = sg.Push(background_color='gray')
layout.insert(0, [sg.Radio('Info', "Modus", default=True,enable_events=True, size=(10,1), k='-Info-'), sg.Radio('Spiel', "Modus", default=False,enable_events=True, size=(10,1), k='-Spiel-')])
layout.insert(0,[sg.Button(SPIELBUTTON_TEXT_STARTEN,key=SPIELBUTTON,visible=False), sg.Text('', key=FRAGE,visible=False, font='_ 16')])
layout.insert(0,[sg.Text('Periodensystem', font='_ 20')])
sg.theme('DarkAmber')
Spielen = False
Richtig = 0
Gesamt = 0
window = sg.Window("Periodensystem", layout,background_color='gray')
i=0
while True:
event,value = window.read()
if event == "close":
break
if event == sg.WINDOW_CLOSED:
break
if event == "-Spiel-":
window[SPIELBUTTON].update(visible=True)
window[SPIELBUTTON].update(SPIELBUTTON_TEXT_STARTEN)
window[FRAGE].update(visible=True)
if event == "-Info-":
window[SPIELBUTTON].update(visible=False)
window[FRAGE].update(visible=False)
if event == SPIELBUTTON:
Spielen = not Spielen
if (Spielen == True):
window[FRAGE].update(visible=True)
AktuelleFrage = random.choice(ps_elemente)
window[SPIELBUTTON].update(SPIELBUTTON_TEXT_STOPPEN)
window[FRAGE].update(f"Welches Atom hat die Ordnungszahl {AktuelleFrage['Atomic Number']} ?")
else:
prozent = Richtig / Gesamt * 100
SpielerText = "Weiter so Du wirst immer besser!"
if prozent > 30:
SpielerText = "Sehr gut!"
if prozent > 50:
SpielerText = "Wunderbar! Du bist am bestem Weg zum Experten!"
if prozent > 70:
SpielerText = "Viel fehlt nicht mehr und Du bist Experte!"
if prozent > 80:
SpielerText = "Du bist Experte!"
sg.popup("Ergebnis",f"{SpielerText}\nDu hast {Gesamt} Fragen beantwortet davon {Richtig} Richtig!")
window[SPIELBUTTON].update(SPIELBUTTON_TEXT_STARTEN)
window[FRAGE].update(visible=False)
Gesamt =0
Richtig = 0
element = findeElement(ps_elemente,event)
if element != NULL:
if Spielen == True:
Gesamt = Gesamt +1;
if (element['Symbol'] == AktuelleFrage['Symbol']):
sg.popup("Richtig!!")
Richtig = Richtig+1
else:
sg.popup("Falsch",f"Leider falsch!!\nDie richtige Antwort ist {AktuelleFrage['Symbol']} - {AktuelleFrage['Element']}\nDu hast {element['Symbol']} mit der Ordnungszahl {element['Atomic Number']} ausgewähl! ")
AktuelleFrage = random.choice(ps_elemente)
window[FRAGE].update(f"Welches Atom hat die Ordnungszahl {AktuelleFrage['Atomic Number']} ?")
else:
infosAnzeigen(element)
window.close()
Eigentlich haben wir ja schon einen Trainer für die Irrgeular Words geschrieben, der sich ja, wenn wir das abstrahieren, sich nicht wahnsinnig von dem Bruchrechnungstrainer unterscheidet. Viele Elemente sind gleich: Auswahl der Frage - User Eingabe - Vergleich. Wir könnten nun mit ein paar wenigen Modifikationen und einer neuen Datei in Windeseile so einen Trainer für ein neues Themengebiet basteln. Doch wollen wir das? Im Grunde spricht nichts dagegen. So für die Fingerübung ist es gut geeignet. Es gibt wie überall auch in der Softwareentwicklung Prinzipien und eines davon ist das DRY.
Das DRY-Prinzip steht für "Don't Repeat Yourself" (auf Deutsch: "Wiederhole dich nicht"). Es handelt sich um eine grundlegende Software-Entwicklungsregel, die darauf abzielt, die Wartbarkeit, Lesbarkeit und Effizienz von Code zu verbessern, indem Wiederholungen vermieden werden.
Das Prinzip besagt im Wesentlichen, dass man denselben Code nicht mehrmals schreiben sollte. Stattdessen sollte man den Code an einer einzigen Stelle platzieren und ihn von anderen Teilen des Programms aus wiederverwenden. Das DRY-Prinzip unterstützt die Modularität und fördert die Verwendung von Funktionen, Methoden, Klassen oder Modulen, um häufig verwendete Funktionalitäten zu kapseln.
Eine Methode dieses Prinzip umzusetzen ist Objekt Orientierte Programmierung.
Die grundlegenden Konzepte der objektorientierten Programmierung sind:
Das mag nun vielleicht alles sehr kompliziert klingen, ist es jedoch nicht.
Als erstes überlegen wir uns wie wir die Daten, also Vokabel usw. speichern wollen. Wir haben ja schon für unseren Grammatik Trainer die Sqlite Datenbank verwendet. Dies werden wir auch für unseren Vokabel Trainer verwenden.
Da wir ja nicht nur Vokabel, sondern eigentlich alles was wir als Frage Antwort Spiel lernen wollen, in unserem Vokabel Trainer speichern wollen
Also fangen wir mal mit einer Klasse an - der Einfachheit halber nennen wir sie Trainer und wir erzeugen dazu eine neue Datei namens Trainer.py und fangen an zu schrieben.
Nun müssen wir uns überlegen, welche Funktionen wir benötigen.
Unser Trainer, soll auch die Fragen verwalten können.
Also brauchen wir eine Funktion fürs speichern (create,insert), bearbeiten (update) und
dem lesen. Daneben noch eine Funktion die wir üben nennen.
Hier der Code:
import sqlite3
import random
from typing import List, Tuple, Optional
class Trainer:
"""
Universeller Trainer für Frage-Antwort basierte Lernspiele.
Kann für Vokabeln, Grammatik, Mathe oder andere Themenbereiche verwendet werden.
"""
def __init__(self, db_name: str, table_name: str):
"""
Initialisiert den Trainer mit Datenbankname und Tabellenname.
Args:
db_name: Name der SQLite Datenbankdatei
table_name: Name der Tabelle für dieses Themengebiet
"""
self.db_name = db_name
self.table_name = table_name
self.verbindung = None
self._datenbank_initialisieren()
def _datenbank_initialisieren(self):
"""
Erstellt die Datenbank und Tabelle falls sie nicht existieren.
Private Methode (durch _ gekennzeichnet).
"""
try:
self.verbindung = sqlite3.connect(self.db_name)
cursor = self.verbindung.cursor()
# Tabelle erstellen falls nicht vorhanden
cursor.execute(f'''
CREATE TABLE IF NOT EXISTS {self.table_name} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
frage TEXT NOT NULL,
antwort TEXT NOT NULL,
schwierigkeitsgrad INTEGER DEFAULT 1,
richtig_beantwortet INTEGER DEFAULT 0,
falsch_beantwortet INTEGER DEFAULT 0,
erstellt_am TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
self.verbindung.commit()
print(f"Datenbank '{self.db_name}' und Tabelle '{self.table_name}' bereit.")
except sqlite3.Error as e:
print(f"Fehler beim Initialisieren der Datenbank: {e}")
def speichern(self, frage: str, antwort: str, schwierigkeitsgrad: int = 1) -> bool:
"""
Speichert eine neue Frage-Antwort Kombination in der Datenbank.
Args:
frage: Die Frage die gestellt werden soll
antwort: Die korrekte Antwort
schwierigkeitsgrad: Schwierigkeit von 1-5 (Standard: 1)
Returns:
bool: True wenn erfolgreich gespeichert, False bei Fehler
"""
try:
cursor = self.verbindung.cursor()
cursor.execute(f'''
INSERT INTO {self.table_name} (frage, antwort, schwierigkeitsgrad)
VALUES (?, ?, ?)
''', (frage, antwort, schwierigkeitsgrad))
self.verbindung.commit()
print(f"Erfolgreich gespeichert: '{frage}' -> '{antwort}'")
return True
except sqlite3.Error as e:
print(f"Fehler beim Speichern: {e}")
return False
def bearbeiten(self, id: int, neue_frage: str = None, neue_antwort: str = None,
neuer_schwierigkeitsgrad: int = None) -> bool:
"""
Bearbeitet eine existierende Frage-Antwort Kombination.
Args:
id: ID des zu bearbeitenden Eintrags
neue_frage: Neue Frage (optional)
neue_antwort: Neue Antwort (optional)
neuer_schwierigkeitsgrad: Neuer Schwierigkeitsgrad (optional)
Returns:
bool: True wenn erfolgreich bearbeitet, False bei Fehler
"""
try:
cursor = self.verbindung.cursor()
# Zuerst prüfen ob der Eintrag existiert
cursor.execute(f"SELECT * FROM {self.table_name} WHERE id = ?", (id,))
if not cursor.fetchone():
print(f"Eintrag mit ID {id} nicht gefunden.")
return False
# Update-Statement dynamisch erstellen
update_felder = []
werte = []
if neue_frage is not None:
update_felder.append("frage = ?")
werte.append(neue_frage)
if neue_antwort is not None:
update_felder.append("antwort = ?")
werte.append(neue_antwort)
if neuer_schwierigkeitsgrad is not None:
update_felder.append("schwierigkeitsgrad = ?")
werte.append(neuer_schwierigkeitsgrad)
if not update_felder:
print("Keine Änderungen angegeben.")
return False
werte.append(id) # ID für WHERE-Klausel
query = f"UPDATE {self.table_name} SET {', '.join(update_felder)} WHERE id = ?"
cursor.execute(query, werte)
self.verbindung.commit()
print(f"Eintrag mit ID {id} erfolgreich bearbeitet.")
return True
except sqlite3.Error as e:
print(f"Fehler beim Bearbeiten: {e}")
return False
def lesen(self, schwierigkeitsgrad: Optional[int] = None, limit: Optional[int] = None) -> List[Tuple]:
"""
Liest Frage-Antwort Paare aus der Datenbank.
Args:
schwierigkeitsgrad: Filtert nach bestimmtem Schwierigkeitsgrad (optional)
limit: Begrenzt die Anzahl der Ergebnisse (optional)
Returns:
List[Tuple]: Liste von Tupeln (id, frage, antwort, schwierigkeitsgrad, ...)
"""
try:
cursor = self.verbindung.cursor()
query = f"SELECT * FROM {self.table_name}"
params = []
if schwierigkeitsgrad is not None:
query += " WHERE schwierigkeitsgrad = ?"
params.append(schwierigkeitsgrad)
if limit is not None:
query += " LIMIT ?"
params.append(limit)
cursor.execute(query, params)
ergebnisse = cursor.fetchall()
return ergebnisse
except sqlite3.Error as e:
print(f"Fehler beim Lesen: {e}")
return []
def loeschen(self, id: int) -> bool:
"""
Löscht einen Eintrag aus der Datenbank.
Args:
id: ID des zu löschenden Eintrags
Returns:
bool: True wenn erfolgreich gelöscht, False bei Fehler
"""
try:
cursor = self.verbindung.cursor()
cursor.execute(f"DELETE FROM {self.table_name} WHERE id = ?", (id,))
if cursor.rowcount == 0:
print(f"Eintrag mit ID {id} nicht gefunden.")
return False
self.verbindung.commit()
print(f"Eintrag mit ID {id} erfolgreich gelöscht.")
return True
except sqlite3.Error as e:
print(f"Fehler beim Löschen: {e}")
return False
def ueben(self, anzahl_fragen: int = 10, schwierigkeitsgrad: Optional[int] = None):
"""
Startet eine Übungseinheit mit zufälligen Fragen.
Args:
anzahl_fragen: Anzahl der zu stellenden Fragen
schwierigkeitsgrad: Filtert nach bestimmtem Schwierigkeitsgrad (optional)
"""
fragen = self.lesen(schwierigkeitsgrad=schwierigkeitsgrad)
if not fragen:
print("Keine Fragen in der Datenbank gefunden!")
return
# Zufällige Auswahl der Fragen
if len(fragen) < anzahl_fragen:
print(f"Nur {len(fragen)} Fragen verfügbar. Alle werden verwendet.")
ausgewaehlte_fragen = fragen
else:
ausgewaehlte_fragen = random.sample(fragen, anzahl_fragen)
richtige_antworten = 0
print(f"\n=== Übung gestartet mit {len(ausgewaehlte_fragen)} Fragen ===\n")
for i, (id, frage, korrekte_antwort, schwierigkeit, richtig, falsch, erstellt) in enumerate(ausgewaehlte_fragen, 1):
print(f"Frage {i}/{len(ausgewaehlte_fragen)}: {frage}")
benutzer_antwort = input("Deine Antwort: ").strip()
if benutzer_antwort.lower() == korrekte_antwort.lower():
print("✓ Richtig!\n")
richtige_antworten += 1
self._statistik_aktualisieren(id, richtig=True)
else:
print(f"✗ Falsch! Die richtige Antwort war: {korrekte_antwort}\n")
self._statistik_aktualisieren(id, richtig=False)
# Endergebnis anzeigen
prozent = (richtige_antworten / len(ausgewaehlte_fragen)) * 100
print(f"=== Übung beendet ===")
print(f"Richtige Antworten: {richtige_antworten}/{len(ausgewaehlte_fragen)} ({prozent:.1f}%)")
if prozent >= 80:
print("🎉 Ausgezeichnet!")
elif prozent >= 60:
print("👍 Gut gemacht!")
else:
print("💪 Weiter üben!")
def _statistik_aktualisieren(self, id: int, richtig: bool):
"""
Aktualisiert die Statistiken für eine Frage.
Private Methode zur internen Verwendung.
Args:
id: ID der Frage
richtig: True wenn richtig beantwortet, False wenn falsch
"""
try:
cursor = self.verbindung.cursor()
if richtig:
cursor.execute(f'''
UPDATE {self.table_name}
SET richtig_beantwortet = richtig_beantwortet + 1
WHERE id = ?
''', (id,))
else:
cursor.execute(f'''
UPDATE {self.table_name}
SET falsch_beantwortet = falsch_beantwortet + 1
WHERE id = ?
''', (id,))
self.verbindung.commit()
except sqlite3.Error as e:
print(f"Fehler beim Aktualisieren der Statistik: {e}")
def statistik_anzeigen(self):
"""
Zeigt Statistiken über die gespeicherten Fragen an.
"""
try:
cursor = self.verbindung.cursor()
cursor.execute(f'''
SELECT
COUNT(*) as gesamt,
AVG(schwierigkeitsgrad) as durchschnittliche_schwierigkeit,
SUM(richtig_beantwortet) as gesamt_richtig,
SUM(falsch_beantwortet) as gesamt_falsch
FROM {self.table_name}
''')
stats = cursor.fetchone()
gesamt, avg_schwierigkeit, richtig, falsch = stats
print(f"\n=== Statistiken für {self.table_name} ===")
print(f"Gesamt Fragen: {gesamt}")
print(f"Durchschnittliche Schwierigkeit: {avg_schwierigkeit:.1f}")
print(f"Richtig beantwortet: {richtig}")
print(f"Falsch beantwortet: {falsch}")
if richtig + falsch > 0:
erfolgsrate = (richtig / (richtig + falsch)) * 100
print(f"Erfolgsrate: {erfolgsrate:.1f}%")
except sqlite3.Error as e:
print(f"Fehler beim Anzeigen der Statistiken: {e}")
def schliessen(self):
"""
Schließt die Datenbankverbindung.
"""
if self.verbindung:
self.verbindung.close()
print("Datenbankverbindung geschlossen.")
# Beispiel für die Verwendung der Trainer-Klasse
if __name__ == "__main__":
# Beispiel 1: Vokabeltrainer
vokabel_trainer = Trainer("lerntrainer.db", "vokabeln")
# Einige Vokabeln hinzufügen
vokabel_trainer.speichern("Hello", "Hallo", 1)
vokabel_trainer.speichern("Goodbye", "Auf Wiedersehen", 1)
vokabel_trainer.speichern("Beautiful", "Schön", 2)
# Beispiel 2: Mathe-Trainer
mathe_trainer = Trainer("lerntrainer.db", "mathematik")
# Einige Mathe-Aufgaben hinzufügen
mathe_trainer.speichern("2 + 3 = ?", "5", 1)
mathe_trainer.speichern("7 * 8 = ?", "56", 2)
mathe_trainer.speichern("144 / 12 = ?", "12", 2)
# Übung starten
print("Möchtest du Vokabeln (v) oder Mathe (m) üben?")
auswahl = input("Deine Wahl: ").lower()
if auswahl == 'v':
vokabel_trainer.ueben(5)
vokabel_trainer.statistik_anzeigen()
elif auswahl == 'm':
mathe_trainer.ueben(3)
mathe_trainer.statistik_anzeigen()
# Verbindungen schließen
vokabel_trainer.schliessen()
mathe_trainer.schliessen()
Diese Trainer Klasse verkörpert perfekt das DRY-Prinzip, das du beschrieben hast. Anstatt für jeden neuen Lernbereich (Vokabeln, Grammatik, Mathematik) separaten Code zu schreiben, haben wir eine universelle Lösung geschaffen.
Die wichtigsten objektorientierten Konzepte in unserer Implementierung:
Verkapselung: Die interne Datenbanklogik ist in der Klasse versteckt. Der Benutzer muss sich keine Gedanken über SQL-Befehle oder Datenbankverbindungen machen - er ruft einfach speichern() oder ueben() auf.
Wiederverwendbarkeit: Ein und dieselbe Klasse kann für völlig verschiedene Themenbereiche verwendet werden. Ob Englisch-Vokabeln, Geschichtsdaten oder Chemie-Formeln - die Logik bleibt identisch.
Erweiterbarkeit: Durch die modulare Struktur können wir später einfach neue Funktionen hinzufügen, ohne bestehenden Code zu ändern. Beispielsweise könnten wir eine export_zu_csv() Methode ergänzen.
Die private Methode _statistik_aktualisieren() zeigt auch das Konzept der Datenverbergung - sie ist nur für interne Zwecke gedacht und sollte nicht direkt von außen aufgerufen werden.
So können wir nun problemlos einen Französisch-Trainer, Geschichts-Trainer oder jeden anderen Frage-Antwort-basierten Trainer erstellen, ohne auch nur eine Zeile Code zu duplizieren:
franzoesisch_trainer = Trainer("sprachen.db", "franzoesisch")
geschichte_trainer = Trainer("schule.db", "geschichte")
Das ist die Macht der objektorientierten Programmierung - einmal gut durchdacht und implementiert, spart sie uns unzählige Stunden Entwicklungszeit und macht unseren Code wartbarer und fehlerresistenter.
Der Pomodoro-Ansatz ist eigentlich total simpel und genial zugleich. Die Idee stammt von einem italienischen Studenten namens Francesco Cirillo, der in den 80ern Probleme hatte, sich aufs Lernen zu konzentrieren. Er schnappte sich einen dieser kleinen Küchenwecker in Tomatenform (Pomodoro = italienisch für Tomate) und dachte sich: "Ich arbeite jetzt einfach mal 25 Minuten am Stück, ohne Ablenkung."
So funktioniert's:
Du stellst den Timer auf 25 Minuten und arbeitest nur an einer einzigen Aufgabe. Kein Handy checken, keine E-Mails, kein "ach, ich schaue mal schnell was bei YouTube" - einfach nur fokussiert an der einen Sache dran bleiben. Wenn der Timer klingelt, machst du 5 Minuten Pause. Steh auf, trink was, guck aus dem Fenster, aber arbeite nicht weiter.
Nach vier solcher "Pomodoros" (also 4 x 25 Minuten Arbeit + 3 x 5 Minuten Pause) machst du eine längere Pause von 15-30 Minuten.
Warum funktioniert das so gut?
Unser Gehirn mag keine endlosen Aufgaben. "Ich muss heute das ganze Projekt fertig machen" fühlt sich überwältigend an. Aber "ich arbeite jetzt 25 Minuten daran" ist machbar. Es ist wie bei einem Marathon - du denkst nicht an die 42 Kilometer, sondern nur an den nächsten Kilometer.
Plus: Du wirst überrascht sein, wie viel du in 25 Minuten schaffen kannst, wenn du wirklich fokussiert bist. Keine Multitasking-Spielchen, keine Ablenkungen - pure Konzentration.
Der Trick ist auch, dass du nach 25 Minuten meistens gar nicht aufhören willst, weil du im Flow bist. Aber die Pause ist wichtig - sie verhindert, dass dein Gehirn ermüdet und hält die Motivation hoch.
Probier's einfach mal aus - du brauchst nur einen Timer und ein bisschen Disziplin oder einfach ein cooles Python Programm:
Also lass und loslegen zuerst bauen wir eine Klasse die wir PomodoroTimer
class PomodoroTimer:
def __init__(self):
self.arbeitszeit = 25 * 60 # 25 Minuten in Sekunden
self.timer_laeuft = False
self.arbeitszeit ist eine "Eigenschaft" unseres Timers - jeder Timer "merkt" sich seine Arbeitszeit
Das self bedeutet "dieser spezielle Timer" - wichtig bei Klassen!
Nun bauen wir unsere Methoden, also die wichtigste unseren countdown:
def countdown(self, dauer: int, beschreibung: str, typ: str = "arbeit"):
self.timer_laeuft = True
verbleibend = dauer
while verbleibend > 0 and self.timer_laeuft:
# Hier läuft der Countdown
verbleibend -= 1
time.sleep(1) # 1 Sekunde warten
def countdown(self, ...) - das self ist immer der erste Parameter
dauer: int bedeutet "dauer soll eine Zahl sein" (Type Hints - hilft beim Verstehen)
typ: str = "arbeit" bedeutet "wenn nichts angegeben wird, nimm 'arbeit'"
Nun kommen wir zu unserm Herzen die While-Schleife:
while verbleibend > 0 and self.timer_laeuft:
# Bildschirm aktualisieren
self.clear_screen()
print(f"Zeit: {self.formatiere_zeit(verbleibend)}")
verbleibend -= 1 # Eine Sekunde abziehen
time.sleep(1) # Eine Sekunde warten
while = "Solange diese Bedingung wahr ist, mache weiter"
verbleibend > 0 = "Solange noch Zeit übrig ist"
and self.timer_laeuft = "UND der Timer läuft noch"
time.sleep(1) = "Warte 1 Sekunde" - ohne das würde alles zu schnell ablaufen
Nun wollen wir die Ausgabe der Zeit verbessern:
def formatiere_zeit(self, sekunden: int) -> str:
minuten = sekunden // 60 # Ganzzahl-Division
sek = sekunden % 60 # Rest der Division (Modulo)
return f"{minuten:02d}:{sek:02d}"
Dazu wenden wir Tricks an
Eine Besonderheit hat unser Code: Wir machen zwei Dinge gleichzeitig. Das schaffen wir mit Threads und das ganze nennt man Threading.
Normaler Python Code läuft linear also eine Sache nach der anderen - eigentlich so was wir mit dem Pomodoro Timer erreichen wollen - uns nicht von der Sache ablenken lassen. Aber in unserem Code wollen wir den Timer laufen lassen und auf Tasten hören. Dazu müssen wir unserem Programm zwei Dinge gleichzeitig beibringen. Das machen wir mit
listener_thread = threading.Thread(target=self.tastatur_listener, daemon=True)
listener_thread.start()
Thread startet eine "parallele Spur"
daemon=True bedeutet "stirb mit dem Hauptprogramm"
Aber was passiert wenn ein Fehler auftritt? Wenn ein Fehler passiert beendet sich das Programm einfach und “wirft” eine so genannte Exception. Das ist meist nicht gut. Daher gibt es Strukturen, die es zulassen, das wir den Fehler behandeln können. Das ganze nennt man Exception Handling - klingt gut oder?
try:
zeit = int(input("Neue Zeit in Minuten: "))
if 1 <= zeit <= 120:
return zeit
except ValueError:
print("❌ Bitte eine gültige Zahl eingeben.")
return None
int("abc") würde das Programm zum Absturz bringen
try: = "Versuche das"
except ValueError: = "Falls ein ValueError auftritt, mache das stattdessen"
So stürzt unser Programm nie ab bei falschen Eingaben
Nun hier das ganze Programm:
import time
import threading
import os
import sys
from datetime import datetime
from typing import Optional
class PomodoroTimer:
"""
Ein vollständiger Pomodoro-Timer mit allen klassischen Features.
25 Min Arbeit -> 5 Min Pause -> Nach 4 Zyklen: 30 Min lange Pause
"""
def __init__(self):
# Zeiten in Sekunden (für Tests kannst du die Werte reduzieren)
self.arbeitszeit = 25 * 60 # 25 Minuten
self.kurze_pause = 5 * 60 # 5 Minuten
self.lange_pause = 30 * 60 # 30 Minuten
# Status-Variablen
self.aktuelle_session = 0
self.completed_pomodoros = 0
self.timer_laeuft = False
self.pause_timer = False
self.timer_thread = None
# Für Statistiken
self.session_start = None
self.tagesstatistik = []
def clear_screen(self):
"""Bildschirm leeren für bessere Übersicht"""
os.system('cls' if os.name == 'nt' else 'clear')
def zeige_banner(self):
"""Zeigt einen schönen Banner"""
print("🍅" * 50)
print(" P O M O D O R O T I M E R")
print("🍅" * 50)
print()
def formatiere_zeit(self, sekunden: int) -> str:
"""Formatiert Sekunden zu MM:SS Format"""
minuten = sekunden // 60
sek = sekunden % 60
return f"{minuten:02d}:{sek:02d}"
def spiele_ton(self, typ: str = "normal"):
"""
Spielt einen Benachrichtigungston (plattformabhängig)
Typ: 'normal', 'pause', 'fertig'
"""
try:
if os.name == 'nt': # Windows
import winsound
if typ == "pause":
winsound.Beep(800, 500) # Höherer Ton für Pause
elif typ == "fertig":
# Drei Töne für "Session komplett"
for _ in range(3):
winsound.Beep(1000, 300)
time.sleep(0.1)
else:
winsound.Beep(600, 800) # Normaler Ton
else: # Linux/Mac
# Einfacher Systemton
os.system('echo -e "\a"')
except:
# Fallback: Text-basierte "Töne"
if typ == "pause":
print("🔔 BEEP BEEP! Zeit für eine Pause!")
elif typ == "fertig":
print("🎉 DING DING DING! Session komplett!")
else:
print("⏰ DING! Zeit um!")
def countdown(self, dauer: int, beschreibung: str, typ: str = "arbeit"):
"""
Haupt-Countdown Funktion
Args:
dauer: Dauer in Sekunden
beschreibung: Was gerade läuft (z.B. "Arbeitszeit")
typ: "arbeit", "kurze_pause", "lange_pause"
"""
self.timer_laeuft = True
verbleibend = dauer
# Icon basierend auf Typ
if typ == "arbeit":
icon = "💼"
elif typ == "kurze_pause":
icon = "☕"
else:
icon = "🏖️"
while verbleibend > 0 and self.timer_laeuft:
if not self.pause_timer:
self.clear_screen()
self.zeige_banner()
print(f"{icon} {beschreibung}")
print(f"Pomodoro #{self.aktuelle_session + 1}")
print(f"Heute abgeschlossen: {self.completed_pomodoros}")
print()
# Große Zeitanzeige
zeit_str = self.formatiere_zeit(verbleibend)
print("┌" + "─" * 20 + "┐")
print(f"│ {zeit_str} │")
print("└" + "─" * 20 + "┘")
print()
# Fortschrittsbalken
fortschritt = (dauer - verbleibend) / dauer
balken_laenge = 30
gefuellt = int(fortschritt * balken_laenge)
balken = "█" * gefuellt + "░" * (balken_laenge - gefuellt)
prozent = int(fortschritt * 100)
print(f"[{balken}] {prozent}%")
print()
# Steuerung
print("Steuerung: [SPACE] Pause/Weiter | [S] Stopp | [R] Reset")
print(" [Q] Beenden | [ENTER] für Menü")
verbleibend -= 1
time.sleep(1)
if self.timer_laeuft: # Nur wenn nicht manuell gestoppt
# Session beenden
if typ == "arbeit":
self.completed_pomodoros += 1
self.tagesstatistik.append({
'zeit': datetime.now().strftime("%H:%M"),
'typ': 'Pomodoro',
'dauer': self.arbeitszeit // 60
})
self.spiele_ton("normal")
elif typ == "kurze_pause":
self.spiele_ton("pause")
else:
self.spiele_ton("fertig")
def tastatur_listener(self):
"""
Lauscht auf Tastatureingaben während der Timer läuft
Läuft in separatem Thread
"""
while self.timer_laeuft:
try:
if os.name == 'nt':
import msvcrt
if msvcrt.kbhit():
taste = msvcrt.getch().decode('utf-8').lower()
self.handle_input(taste)
else:
# Linux/Mac - einfachere Variante
pass
except:
pass
time.sleep(0.1)
def handle_input(self, taste: str):
"""Behandelt Tastatureingaben"""
if taste == ' ': # Leertaste für Pause/Weiter
self.pause_timer = not self.pause_timer
if self.pause_timer:
print("\n⏸️ Timer pausiert - Drücke SPACE zum Fortsetzen")
else:
print("\n▶️ Timer läuft weiter...")
elif taste == 's': # S für Stopp
self.timer_laeuft = False
print("\n🛑 Timer gestoppt!")
elif taste == 'r': # R für Reset
self.reset_session()
elif taste == 'q': # Q für Beenden
self.timer_laeuft = False
print("\n👋 Pomodoro Timer beendet!")
sys.exit()
def starte_arbeitszeit(self):
"""Startet eine 25-minütige Arbeitsphase"""
if self.session_start is None:
self.session_start = datetime.now()
print(f"🍅 Starte Pomodoro #{self.aktuelle_session + 1}")
print("Konzentriere dich auf EINE Aufgabe!")
input("\nDrücke ENTER wenn du bereit bist...")
# Keyboard listener in separatem Thread starten
listener_thread = threading.Thread(target=self.tastatur_listener, daemon=True)
listener_thread.start()
self.countdown(self.arbeitszeit, "🍅 ARBEITSZEIT - Stay focused!", "arbeit")
if self.timer_laeuft:
self.aktuelle_session += 1
# Entscheiden was als nächstes kommt
if self.aktuelle_session % 4 == 0:
self.starte_lange_pause()
else:
self.starte_kurze_pause()
def starte_kurze_pause(self):
"""Startet eine 5-minütige Pause"""
print("\n☕ Zeit für eine kurze Pause!")
print("Steh auf, bewege dich, trink was!")
input("Drücke ENTER um die Pause zu starten...")
self.countdown(self.kurze_pause, "☕ KURZE PAUSE - Entspann dich!", "kurze_pause")
if self.timer_laeuft:
print("\n⚡ Pause vorbei! Bereit für den nächsten Pomodoro?")
input("Drücke ENTER um weiterzumachen...")
self.starte_arbeitszeit()
def starte_lange_pause(self):
"""Startet eine 30-minütige lange Pause"""
print("\n🏖️ Fantastisch! Du hast 4 Pomodoros geschafft!")
print("Zeit für eine längere Pause - du hast sie dir verdient!")
input("Drücke ENTER um die lange Pause zu starten...")
self.countdown(self.lange_pause, "🏖️ LANGE PAUSE - Richtig entspannen!", "lange_pause")
if self.timer_laeuft:
print("\n🔥 Wow! Du bist wirklich produktiv heute!")
print("Möchtest du eine neue Session starten?")
antwort = input("(j/n): ").lower()
if antwort == 'j':
self.aktuelle_session = 0 # Reset für neue 4er-Runde
self.starte_arbeitszeit()
else:
self.zeige_tagesstatistik()
def reset_session(self):
"""Setzt die aktuelle Session zurück"""
self.timer_laeuft = False
self.aktuelle_session = 0
self.pause_timer = False
print("\n🔄 Session zurückgesetzt!")
def zeige_tagesstatistik(self):
"""Zeigt die Statistiken des Tages"""
self.clear_screen()
print("📊 DEINE HEUTIGE BILANZ")
print("=" * 40)
if self.session_start:
dauer = datetime.now() - self.session_start
print(f"Session-Dauer: {str(dauer).split('.')[0]}")
print(f"Abgeschlossene Pomodoros: {self.completed_pomodoros}")
print(f"Gesamte Arbeitszeit: {self.completed_pomodoros * 25} Minuten")
if self.completed_pomodoros > 0:
print(f"Durchschnitt pro Stunde: {self.completed_pomodoros * 25 / max(1, (datetime.now() - self.session_start).seconds / 3600):.1f} Min")
print("\n📈 Verlauf:")
for eintrag in self.tagesstatistik:
print(f" {eintrag['zeit']} - {eintrag['typ']} ({eintrag['dauer']} Min)")
if self.completed_pomodoros >= 8:
print("\n🏆 CHAMPION! 8+ Pomodoros sind Weltklasse!")
elif self.completed_pomodoros >= 4:
print("\n🌟 SUPER! Sehr produktiver Tag!")
elif self.completed_pomodoros >= 1:
print("\n👍 GUT! Ein guter Anfang!")
print("\n" + "=" * 40)
def zeige_menu(self):
"""Hauptmenü des Pomodoro Timers"""
while True:
self.clear_screen()
self.zeige_banner()
print("Was möchtest du tun?")
print()
print("1. 🍅 Pomodoro starten")
print("2. ⚙️ Einstellungen")
print("3. 📊 Heute's Statistik")
print("4. ❓ Hilfe")
print("5. 👋 Beenden")
print()
auswahl = input("Deine Wahl (1-5): ").strip()
if auswahl == '1':
self.starte_arbeitszeit()
elif auswahl == '2':
self.einstellungen_menu()
elif auswahl == '3':
self.zeige_tagesstatistik()
input("\nDrücke ENTER um zurück zum Menü zu gehen...")
elif auswahl == '4':
self.zeige_hilfe()
elif auswahl == '5':
self.zeige_tagesstatistik()
print("\n👋 Bis bald! Bleib produktiv!")
break
else:
print("🤔 Ungültige Auswahl. Bitte wähle 1-5.")
time.sleep(1)
def einstellungen_menu(self):
"""Einstellungen für den Timer"""
while True:
self.clear_screen()
print("⚙️ EINSTELLUNGEN")
print("=" * 30)
print(f"1. Arbeitszeit: {self.arbeitszeit // 60} Minuten")
print(f"2. Kurze Pause: {self.kurze_pause // 60} Minuten")
print(f"3. Lange Pause: {self.lange_pause // 60} Minuten")
print("4. Zurück zum Hauptmenü")
print()
auswahl = input("Was möchtest du ändern? (1-4): ").strip()
if auswahl == '1':
neue_zeit = self.eingabe_zeit("Arbeitszeit")
if neue_zeit:
self.arbeitszeit = neue_zeit * 60
elif auswahl == '2':
neue_zeit = self.eingabe_zeit("Kurze Pause")
if neue_zeit:
self.kurze_pause = neue_zeit * 60
elif auswahl == '3':
neue_zeit = self.eingabe_zeit("Lange Pause")
if neue_zeit:
self.lange_pause = neue_zeit * 60
elif auswahl == '4':
break
def eingabe_zeit(self, typ: str) -> Optional[int]:
"""Hilfsfunktion für Zeiteingabe"""
try:
zeit = int(input(f"Neue {typ} in Minuten (1-120): "))
if 1 <= zeit <= 120:
print(f"✅ {typ} auf {zeit} Minuten geändert!")
time.sleep(1)
return zeit
else:
print("❌ Bitte eine Zahl zwischen 1 und 120 eingeben.")
time.sleep(1)
except ValueError:
print("❌ Bitte eine gültige Zahl eingeben.")
time.sleep(1)
return None
def zeige_hilfe(self):
"""Zeigt Hilfe und Tipps"""
self.clear_screen()
print("❓ HILFE & TIPPS")
print("=" * 40)
print()
print("🍅 WAS IST POMODORO?")
print("25 Min fokussiert arbeiten → 5 Min Pause")
print("Nach 4 Pomodoros → 30 Min lange Pause")
print()
print("💡 TIPPS FÜR MAXIMALE PRODUKTIVITÄT:")
print("• Wähle VOR dem Start eine konkrete Aufgabe")
print("• Schalte Handy stumm & schließe andere Apps")
print("• Bei Unterbrechungen: notiere sie kurz und mach weiter")
print("• In Pausen: aufstehen, sich bewegen, nicht am Bildschirm bleiben")
print("• Sei nicht zu hart zu dir - Übung macht den Meister!")
print()
print("⌨️ STEUERUNG WÄHREND DES TIMERS:")
print("SPACE - Timer pausieren/fortsetzen")
print("S - Timer stoppen")
print("R - Session zurücksetzen")
print("Q - Programm beenden")
print()
input("Drücke ENTER um zurück zum Menü zu gehen...")
# Hauptprogramm
if __name__ == "__main__":
try:
timer = PomodoroTimer()
timer.zeige_menu()
except KeyboardInterrupt:
print("\n\n👋 Pomodoro Timer beendet!")
except Exception as e:
print(f"\n❌ Ein Fehler ist aufgetreten: {e}")
print("Bitte starte das Programm neu.")