@fschober: Ein Fehler ist das Du den falschen Namen an `del_button()` übergibst. Du nimmst da `name`, was ja durch die Schleife verändert wird und zu dem Zeitpunkt wo der Button geklickt wird nur dann den richtigen Wert hat, wenn der `name` vom letzten Schleifendurchlauf zufällig der richtige ist. Du müsstest an der Stelle das Argument vom ``lambda``-Ausdruck benutzen das im Schleifendurchlauf an das `name` aus dem jeweiligen Schleifendurchlauf gebunden wird. An der Stelle würde ich aber auch eher `functools.partial` empfehlen statt des ``lambda``-mit-default-wert-Tricks.
Der zweite Fehler ist das Du bei jedem `rel_buttons_update()` keine Buttons aktualisierst, sondern jedes mal neue erzeugst und die in die gleichen Zellen setzt wie die aus dem vorherigen Durchlauf. Das *er*setzt die *nicht*, sondern stapelt die immer mehr werdenden Buttons einfach nur übereinander so das die zuletzt hinzugefügten die davor überdecken. Wenn Du dann welche löscht werden die dahinter halt wieder sichtbar. Beziehungsweise löscht Du ja nur einen Button und erzeugst dann gleich wieder alle bis auf den einen neu.
Sonstige Anmerkungen: Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Die Rückruffunktionen können dann nicht mehr so einfach auf globale Variablen zugreifen, was sie aber auch nicht sollten, denn Funktionen und Methoden sollten alles was sie ausser Konstanten benötigen als Argument(e) übergeben bekommen. Da braucht man dann noch an ein paar anderen Stellen `functools.partial()` beziehungsweise kommt man bei jeder nicht-trivialen GUI nicht um objektorientierte Programmierung herum.
Faustregel für Kommentare: Nicht beschreiben *was* gemacht wird, denn das steht bereits im Code, sondern *warum* der das *so* macht, sofern das nicht offensichtlich ist. Hinter ein ``something.sort()`` den Kommentar ``# Sort list`` zu schreiben bringt dem Leser genau Null Erkenntnisgewinn.
Abkürzungen bei Namen sind nicht gut. Ein Name soll dem Leser vermitteln was der Wert dahinter bedeutet, nicht zum rätselraten zwingen.
Eine leere Zeichenkette ist kein sinnvoller Platzhalterwert für einen `Button`. Das ist verwirrend. Für ”Nichts” ist der Wert `None` da. Wobei man sich an der Stelle überlegen sollte ob man da nicht gleich den `Button` erzeugt. Das würde den Code einfacher machen und auch die Fehlerbehebung das immer wieder unnötig Buttons erzeugt und übereinander gestapelt werden. Wobei an der Stelle auch das Wörterbuch entweder problematisch ist, weil jedes Wort nur einmal hinzugefügt werden kann, oder man einen Test an der Stelle braucht, der aktiv verhindert das ein Wort mehr als einmal hinzugefügt werden kann. Falls man jedes Wort nur einmal zur gleichen Zeit verwenden können soll, wäre es in der GUI auch gut wenn der Benutzer Worte die er nicht hinzufügen kann entweder gar nicht erst sieht, oder sie zumindest optisch als nicht aktiv erkennbar sind.
Beim aktualisieren werden die vorhandenen Buttons in dem Wörterbuch überhaupt nicht verwendet. Das sollten sie aber, statt immer neue zu erzeugen. Und zwar nur die Buttons und nicht die Namen/Worte, denn wenn man neue Buttons schon in der Funktion zum hinzufügen hinzugefügt (dazu ist die ja da), dann braucht man in der Aktualisierungsfunktion nur die bereits vorhandenen Buttons neu anzuordnen. Und man muss dann auch den `Frame` in dem die Buttons stecken auch gar nicht in die Funktion zum aktualisieren weiterreichen.
Wenn man über irgendetwas iteriert und zusätzlich eine laufende Zahl benötigt, gibt es dafür die `enumerate()`-Funktion.
Wörterbücher haben eine `pop()`-Methode die einen Wert zu einem Schlüssel liefert *und* das Schlüssel/Wert-Paar aus dem Wörterbuch entfernt.
Code: Alles auswählen
#!/usr/bin/env python3
import tkinter as tk
from functools import partial
def add_related_word(word_variable, word_to_button, words_frame, *_args):
word = word_variable.get()
if word not in word_to_button:
word_to_button[word] = tk.Button(
words_frame,
text=word,
command=partial(
delete_related_word_button, word_to_button, word
),
)
update_related_word_buttons(word_to_button)
def delete_related_word_button(word_to_button, word):
word_to_button.pop(word).destroy()
update_related_word_buttons(word_to_button)
def update_related_word_buttons(word_to_button):
for i, button in enumerate(word_to_button.values()):
button.grid(column=i, row=0, sticky=tk.NW)
def main():
words = ["brimbambum", "haus", "oolbath", "pflanze", "wasser"]
window = tk.Tk()
tk.Label(window, text="Related Words: ").grid(row=0, column=0, sticky=tk.W)
word_to_button = {}
word_variable = tk.StringVar(value="Related Words...")
tk.OptionMenu(window, word_variable, *words).grid(
row=0, column=1, sticky=tk.W
)
words_frame = tk.Frame(window)
words_frame.grid(row=1, column=1, sticky=tk.W)
word_variable.trace(
"w",
partial(add_related_word, word_variable, word_to_button, words_frame),
)
window.mainloop()
if __name__ == "__main__":
main()