@Herr Mo: Das mit dem `self.*` solltest Du aber lernen, weil man für jede nicht-triviale GUI-Anwendung Objektorientierung benötigt. Und das sollte man IMHO am besten verstanden haben *bevor* man mit GUI-Programmierung anfängt, denn sonst muss man objektorientierte Programmierung (OOP) *und* die ereingnisbasierte Programmierung die GUIs mit sich bringen *gleichzeitig* lernen. Es ist IMHO einfacher diese beiden Konzepte nacheinander zu lernen.
Sternchen-Importe sind Böse™. Da holt man sich gerade bei `tkinter` hunderte von Namen ins Modul von denen nur ein Bruchteil tatsächlich benötigt werden. Auch Namen die gar nicht im `tkinter`-Modul definiert werden, sondern von diesem Modul seinerseits für eigene Zwecke importiert werden.
Das ist alles sehr ”gequetscht” geschrieben. Zwischen Funktionsdefinitionen gehören Leerzeilen und nach Kommas, um binäre Operatoren, und um Gleicheitszeichen bei Zuweisungen ausserhalb von Argumentlisten erhöhen Leerzeichen die lesbarkeit.
Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
Das `className`-Argument von `tkinter.Tk` ist nicht dazu da den Fenstertitel zu setzen. Das ist nur ein Nebeneffekt davon den Tk-Widget-Klassennamen auf etwas anderes als "Tk" zu setzen. Zum setzen des Fenstertitels ist die `title()`-Methode da.
Namen sollten dem Leser ohne raten zu müssen und irgendwelchen kryptischen Abkürzungen vermitteln was der Wert dahinter bedeutet. Wenn man also `submit_button` meint, sollte man nicht `bsubmit` schreiben. Die Reihenfolge ist bei sehr vielen Namen in dem Programm auch ”yodamässig”. `label_eingabe` müsste eingentlich `eingabelabel` heissen, `entry_word` müsste `word_entry` heissen, und so weiter. Man sollte nicht „denglisch“ benennen, also nicht `eingabelabel` sondern `eingabebeschriftung` oder `entry_label`. Insgesamt macht englisch mehr Sinn weil auch die Schlüsselworte von Python und die ganzen Funktionen und Module englische Bezeichner verwenden.
`place()` sollte man nicht verwenden. Mit absoluten Positions- und Grössenangaben schon mal gar nicht, aber auch relative Angaben führen zu kaputten, unschönen Anordnungen:
Funktionen und Methoden sollten alles was sie ausser Konstanten benötigen als Argument(e) übergeben bekommen. Da auf Modulebene keine Variablen sind – das ist ja alles in der `main()`-Funktion verschwunden — muss man `word_entry` als Argument an `submit()` übergeben.
`submit()` ist ein zu generischer Name für die Funktion. `start_game()` wäre beispielsweise viel aussagekräftiger.
Weder `guess_word` noch `game_word` werden in der Funktion irgendwo noch mal benutzt. Andererseits sollte man das vielleicht machen statt das Entry aus dem Hauptfenster mehrfach abzufragen obwohl das immer den gleichen Wert liefert.
Um den Inhalt eines Entry zu löschen hofft man nicht das 999 wohl gross genug ist, sondern verwendet `tkinter.END`.
`Tk` ist *das* Hauptfenster. Davon darf es immer nur ein Exemplar geben. Zusätzliche Fenster erstellt man mit `tkinter.Toplevel`.
Ein Fenster sollte besser `window` als `game` heissen.
`config()` direkt nach dem erstellen eines Widget-Objekts ist nur nötig wenn man in der/den Option(en) das Widget-Objekt selbst benötigt. Sonst kann und sollte man die Option(en) direkt beim erstellen schon setzen. Klammern um eine Zahl zu setzen macht auch keinen Sinn.
Die ``while``-Schleife macht so überhaupt gar keinen Sinn weil die immer *genau* *einmal* durchlaufen wird. Schleifen sind dazu da Code mehrfach zu durchlaufen, wenn das immer nur genau einmal passiert, dann ist das keine Schleife. `loop` verschwindet dann auch, weil damit sowieso nichts sinnvolles gemacht wird.
Statt die Buchstaben alle einzeln in einer Liste anzugeben, wäre es weniger Tipparbeit eine Zeichenkette mit allen Buchstaben anzulegen. Und da kann man sich dann auch noch mal fehleranfällige Tipparbeit sparen weil es die Grossbuchstaben von A bis Z im `string`-Modul als Konstante gibt.
`letter` wird durch die ``for``-Schleife an eine Zeichenkette mit einem Buchstaben gebunden und *in* der ``for``-Schleife bindest Du den Namen dann an einen `Button`. Das macht keinen Sinn.
Zwischenstand:
Code: Alles auswählen
#!/usr/bin/env python3
import tkinter as tk
from functools import partial
from string import ascii_uppercase
ALPHABET = ascii_uppercase + "ÄÖÜß"
def start_game(word_entry):
secret_word = word_entry.get()
word_entry.delete(0, tk.END)
window = tk.Toplevel()
window.title("Hangman V1 - The Game")
underscore_label = tk.Label(
window, text="__ " * len(secret_word), font=75
)
underscore_label.pack(side=tk.TOP)
guess_entry = tk.Entry(window)
guess_entry.focus()
guess_entry.pack(side=tk.TOP)
tk.Button(window, text="Raten!").pack(side=tk.TOP)
letter_frame = tk.Frame(window)
for letter in ALPHABET:
letter_button = tk.Button(letter_frame, text=letter)
letter_button["command"] = letter_button.destroy
letter_button.pack(side=tk.LEFT)
letter_frame.pack(side=tk.TOP)
def main():
root = tk.Tk()
root.title("Hangman V1 - Settings")
tk.Label(root, text="Gib das zu suchende Wort ein!").pack(side=tk.TOP)
frame = tk.Frame(root)
word_entry = tk.Entry(frame, width=15)
word_entry.focus()
word_entry.pack(side=tk.LEFT)
tk.Button(
frame, text="Okay!", command=partial(start_game, word_entry)
).pack(side=tk.LEFT)
tk.Button(frame, text="Schließen", command=root.quit).pack(side=tk.LEFT)
frame.pack(side=tk.TOP)
root.mainloop()
if __name__ == "__main__":
main()
Wie gesagt sollte man aber vorher schon OOP drauf haben und auch die Programmlogik ohne die GUI schon damit umgesetzt haben. Wenn das funktioniert, kann man eine GUI drauf setzen.
Es fehlt auch noch eine Überprüfung der Eingabe vor dem Spielstart, also das der Benutzer nur legale Zeichen eingegeben hat und ob er überhaupt etwas eingegeben hat.