letztes Listenelement wird nicht richtig dargestellt

Fragen zu Tkinter.
Antworten
Cortez
User
Beiträge: 115
Registriert: Montag 31. Dezember 2018, 15:28

Hallo,

ich habe diesmal zur Übung ein Programm entwickelt, dass Farben in einer Liste speichert und diese dann in einem Label der Reihenfolge nach wieder ausgibt. Funktioniert auch soweit -
Nun wollte ich, dass nach der Wiedergabe der Farben jeweils ein neues Listenelement dazukommt - nur was sehr komisch ist - das jeweils letzte Listenelement wird dann nicht mehr richtig dargestellt - das Label zeigt die entsprechende Farbe nicht mehr an, sondern nur die, die neu dazukommt.
Ich hoffe, dass ich das irgendwie einigermaßen klar dargestellt habe.

Hier mal der Code:

Code: Alles auswählen

import tkinter as tk
import random
import time

class Programm:
    def __init__(self):
        self.root =tk.Tk()
        
        self.zufall = 0
        self.counter = 0
        self.memorizer = []
        
        self.box = tk.Label(self.root, bg="white" ,width=20,height=20)
        self.box.grid(row=0,column=0,columnspan=3)
        
        
        self.hitme = tk.Button(self.root, text = "start", command=self.spielen)
        self.hitme.grid(row=1,column=1)
        
        self.abfr = tk.Button(self.root, text="abspielen",command=lambda: self.abfragen())
        self.abfr.grid(row=1,column=2)
        
    def spielen(self):
    
        if len(self.memorizer)>0:
            print (self.memorizer)
        self.zufall = random.randint(1,3)
        if self.zufall == 1:
            self.box["bg"] = "red"
            self.memorizer.append("rot")
        elif self.zufall == 2:
            self.box["bg"] = "blue"
            self.memorizer.append("blau")
        else:
            self.box["bg"] = "green"
            self.memorizer.append("gruen")
            
    
            
    def abfragen(self):
        #self.box["bg"] = "white"
        
        print (self.counter, "Zähler")
        print ((len(self.memorizer)-1), "Laenge Liste um eins verringert")
        
        if self.counter <= len(self.memorizer)-1:
            print (self.memorizer)
            #for self.counter in range(0, len(self.memorizer)):
            #print (self.memorizer[self.counter])
            if (self.memorizer[self.counter]) == "rot":
                
                self.box["bg"] = "red"
            elif (self.memorizer[self.counter]) == "blau":
                
                self.box["bg"] = "blue"
            else:
                
                self.box["bg"] = "green"
            self.counter += 1
        
        
        if self.counter == len(self.memorizer):
            
            self.zufall = random.randint(1,3)
            if self.zufall == 1:
                self.box["bg"] = "red"
                self.memorizer.append("rot")
            elif self.zufall == 2:
                self.box["bg"] = "blue"
                self.memorizer.append("blau")
            else:
                self.box["bg"] = "green"
                self.memorizer.append("gruen")
            print ("neuer Eintrag erfolgt")
            self.counter = 0
            return 
            
        self.box.after(3000,self.abfragen)
    
    def hinzufuegen(self):
        s
def main():
    programm = Programm()
    programm.root.mainloop()


#Hauptprogramm
if __name__ == '__main__':
    main()
Vielen Dank schonmal für eure Hinweise.
Benutzeravatar
__blackjack__
User
Beiträge: 12984
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Cortez: `time` wird importiert aber nirgends verwendet.

Die `hinzufuegen()`-Methode sieht etwas, ähm, unfertig aus.

`hitme` und `abfr` sind keine guten Namen für die Schaltflächen. Beide Namen/Attribute werden im Code auch gar nicht wirklich gebraucht.

Beim "abspielen"-Button ist der ``lambda``-Ausdruck überflüssig — bei dem anderen Button gibt es das ``lambda`` ja auch nicht.

`zufall` ist nicht wirklich Zustand von dem Programm, warum ist das dann ein Attribut?

Das der Code die Farben als Zeichenketten in zwei Sprachen verarbeitet ist völlig unnötig umständlich. Und auch das von ganzen Zahlen auf Farben abgebildet werden muss führt zu unnötigem Code. Da die Farbnamen für den Benutzer nirgends angezeigt werden, machen die deutschen Farbnamen keinen Sinn, denn die Englichen kann man direkt zum setzen der Labelfarbe verwenden. Und statt von einer ganzen Zufallszahl auf Farben abzubilden kann man viel einfacher mit `random.choice()` einfach einen Farbnamen aus einer Sequenz von Farbnamen auswählen.

Der Code zum anhängen einer neuen Farbe steht kopiert in zwei Methoden. Der sollte nur einmal im Programm stehen.

``self.counter <= len(self.memorizer) - 1`` kann man ohne das ``- 1`` schreiben wenn man auf kleiner statt auf kleiner-oder-gleich testet.

Und das nächste ``if`` ist, wenn alles korrekt funktioniert, das Gegenteil von dem ersten Test, also eigentlich ein ``else``-Zweig. Wobei ich die beiden Zweige umdrehen würde, weil der Gleichheitstest IMHO einfacher zu verstehen ist und man bekommt dann auch keine Probleme bei Randfällen wie der leeren Liste.

Das ``return`` kann aus der Methode auch raus wenn man den Code der damit ”übersprungen” werden soll einfach in den anderen Zweig steckt.

`counter` ist eigentlich auch nicht wirklich ein Attribut, das kann man bei `abfragen()` auch als Argument durchreichen und ich würde es auch `index` nennen, denn als das wird der Wert verwendet. Bei `index` ist klarer, dass 0 ein Element meint, während bei `counter` die Erwartung entstehen könnte, dass es gar kein Element gibt.

Wobei man dem Abspielknopf dann vielleicht doch einen Namen spendieren sollte bzw. ein Attribut, weil vielleicht Sinn machen würde zumindest diesen Knopf zu deaktivieren während das Abspielen läuft, damit man das während des Abspielens nicht nebenläufig ein weiteres mal starten kann.

Das `grid()` geht nicht so ganz auf. Es gibt nur zwei Spalten.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import random
import tkinter as tk


class Program:
    COLOURS = ["red", "green", "blue"]

    def __init__(self):
        self.root = tk.Tk()
        self.memorizer = []

        self.box = tk.Label(self.root, bg="white", width=20, height=20)
        self.box.grid(row=0, column=0, columnspan=2)

        tk.Button(self.root, text="start", command=self.spielen).grid(
            row=1, column=0
        )
        self.play_button = tk.Button(
            self.root, text="abspielen", command=self.abfragen
        )
        self.play_button.grid(row=1, column=1)

    def spielen(self):
        if self.memorizer:
            print(self.memorizer)
        colour = random.choice(self.COLOURS)
        self.box["background"] = colour
        self.memorizer.append(colour)

    def abfragen(self, index=0):
        print(index, "Index")
        print(len(self.memorizer), "Länge Liste")

        if index == len(self.memorizer):
            self.spielen()
            print("neuer Eintrag erfolgt")
            self.play_button["state"] = tk.NORMAL
        else:
            print(self.memorizer)
            self.play_button["state"] = tk.DISABLED
            self.box["background"] = self.memorizer[index]
            self.root.after(3000, self.abfragen, index + 1)


def main():
    program = Program()
    program.root.mainloop()


if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Cortez
User
Beiträge: 115
Registriert: Montag 31. Dezember 2018, 15:28

Hallo,

ich danke dir erstmal sehr für deine Hilfe.

Die gute Nachricht: Wenn ich den Wert durchreiche, dann funktioniert es jetzt.

Ich weiß auch, dass meine Methode nicht die beste war, dennoch würde mich interessieren, wo mein Denkfehler lag - (weil ich anstatt "else" einen zweiten "if" -Zweig genutzt habe oder weil ich den Index direkt in der Funktion und nicht im after-Teil hochgezählt habe?

Mit dem "return" wollte ich eigentlich auch nichts überspringen, sondern die Funktion beenden, nachdem der Zähler eins über der Listenlänge ist. (Ansonsten wäre eine after-Funktion ja eine Endlosschleife...)

Eine Frage hätte ich noch: Die Colours-Liste - warum packst du die nicht in die __init__ - Funktion mit rein?

Nochmals vielen Dank für deine Antwort
Benutzeravatar
__blackjack__
User
Beiträge: 12984
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Cortez: Wenn die Farben variabel wären würde ich sie der `__init__()` übergeben, da sie aber, zumindest im Beispiel, konstant waren definiere ich sie als Konstante auf der Klasse.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten