Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Hallo

Zur Übung erstelle ich gerade ein Hauptmenü für meinen Reaktionszeittest.

Den Code habe ich entsprechend erweitert:

Code: Alles auswählen

from tkinter import Tk, Button
from random import choice
import time
from statistics import mean



DESIGNATED_COLOR = "red"
SWITCH_TIME = 1000


class Main_Window:
    def __init__(self, app_window):
        self.app_window = app_window
        app_window.title("Game")
        app_window.geometry('640x480')

        self.start_button = Button(app_window, text="Start", command=game.start_game) # !!!
        self.start_button.pack()
        self.close_button = Button(app_window, text="Close", command=app_window.quit)
        self.close_button.pack()

class Game:
    def __init__(self, game_window):
        self.game_window = game_window
        self.colors = ["green", "red", "blue", "yellow", "black"]
        self.color = None
        self.reaction_times = []
        self.false_positives = 0
        self.color_missed = 0
        self.key_too_often_pressed = 0
        self.stop_test = False
        self.color_locked = False

    def start_game(self):
        self.game_window.attributes("-fullscreen", True)
        self.canvas = Tk.Canvas(self.game_window, width=self.game_window.winfo_screenwidth(),
                                height=self.game_window.winfo_screenheight(),
                                highlightthickness=0)
        self.canvas.pack()
        self.change_bg_color(DESIGNATED_COLOR)


    def change_bg_color(self, color_locked = False):
        if self.stop_test == True:
            self.game_window.quit()

        if not self.color_locked and self.color == DESIGNATED_COLOR:
            self.color_missed += 1

        self.color_locked = color_locked
        current_color = self.color

        while True:
            if self.color == current_color:
                self.color = choice(self.colors)
            else:
                break

        self.canvas.config(bg=self.color)
        self.start_time = time.monotonic() # Ereignis Timer
        self.game_window.after(SWITCH_TIME, self.change_bg_color)


    def key_pressed(self):
        if self.color == DESIGNATED_COLOR:
            if self.color_locked:
                self.key_too_often_pressed += 1
            else:
                reaction_time = time.monotonic() - self.start_time
                self.reaction_times.append(reaction_time)
                self.color_locked = True
        else:
            self.false_positives += 1

    def stop_game(self):
        self.stop_test = True
        if self.reaction_times:
            # mean() auf Liste ohne Einträge führt zu einem Fehler
            print("Gemittelte Reaktionszeit:", mean(self.reaction_times))
            print("Reaktionszeit pro richtiger Farbe:", self.reaction_times)
        print("Bei richtiger Farbe zu oft gedrückt:", self.key_too_often_pressed)
        print("Bei richtiger Farbe nicht gedrückt:", self.color_missed)
        print("Bei falscher Farbe gedrückt:", self.false_positives)


def main():
    root = Tk()
    root.bind("<Escape>", lambda e: game.stop_game())
    main_window = Main_Window(root)
    game = Game(root)
    root.bind("<space>", lambda e: game.key_pressed()) # Ereignis Tastendruck

    root.mainloop()



if __name__ == '__main__':
    main()
Leider funktioniert der Aufruf der Methode `start_game()` vom Objekt `game` nicht.

Es erscheint folgende Fehlermeldung:

Code: Alles auswählen

NameError: name 'game' is not defined
Woran liegt es, dass es so nicht funktioniert?

Gruß
Atalanttore
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na wie soll das denn gehen, dass du erst das Fenster erzeugst, und *danach* das Game-Objekt, aber schon im MainWindow eben das game-Objekt benutzen willst?

Es gibt verschiedene Arten das Problem zu loesen, aber angesichts der Tatsache, dass dein MainWindow im Grunde gar nichts macht, und umgekehrt dein Game ein sehr intimes Verhaeltnis dazu hat, die beiden Klassen einfach zu einer zusammen zu fassen, und gut ist.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Das Hauptmenü soll alsbald noch um Schaltflächen und Textfelder erweitert werden. Außerdem sollen noch weitere Fenster (u.a. ein Ergebnisfenster) dazukommen.

Gruß
Atalanttore
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dann musst du eben dem MainWindow das Game bekannt machen, NACHDEM es erzeugt wurde. Die Game klasse manipuliert es ja schon jetzt. Wenn du ein Attribut self.game anlegst, das per default auf None ist, aber dann im Konstruktor von Game auf self setzt, dann geht’s doch.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

__deets__ hat geschrieben: Montag 20. August 2018, 22:27 Wenn du ein Attribut self.game anlegst, das per default auf None ist, aber dann im Konstruktor von Game auf self setzt, dann geht’s doch.
Auch nach mehreren Tagen erschließt sich mir leider nicht was du mit diesem Satz genau meinst.

Das Programm läuft zwar jetzt etwas weiter, aber bei einem Klick auf "Start" wird lediglich das Hauptmenü auf Vollbild vergrößert und in der Konsole erscheint folgender Fehler:

Code: Alles auswählen

AttributeError: type object 'Tk' has no attribute 'Canvas'
Der Code sieht jetzt so aus:

Code: Alles auswählen

from tkinter import Tk, Button
from random import choice
import time
from statistics import mean



DESIGNATED_COLOR = "red"
SWITCH_TIME = 1000


class Main_Window:
    def __init__(self, app_window, game=None):
        self.game = game
        self.app_window = app_window
        app_window.title("Game")
        app_window.geometry('640x480')

        self.start_button = Button(app_window, text="Start", command=game.start_game) # !!!
        self.start_button.pack()
        self.close_button = Button(app_window, text="Close", command=app_window.quit)
        self.close_button.pack()

class Game:
    def __init__(self, game_window):
        self.game_window = game_window
        self.colors = ["green", "red", "blue", "yellow", "black"]
        self.color = None
        self.reaction_times = []
        self.false_positives = 0
        self.color_missed = 0
        self.key_too_often_pressed = 0
        self.stop_test = False
        self.color_locked = False

    def start_game(self):
        self.game_window.attributes("-fullscreen", True)
        self.canvas = Tk.Canvas(self.game_window, width=self.game_window.winfo_screenwidth(),
                                height=self.game_window.winfo_screenheight(),
                                highlightthickness=0)
        self.canvas.pack()
        self.change_bg_color(DESIGNATED_COLOR)


    def change_bg_color(self, color_locked = False):
        if self.stop_test == True:
            self.game_window.quit()

        if not self.color_locked and self.color == DESIGNATED_COLOR:
            self.color_missed += 1

        self.color_locked = color_locked
        current_color = self.color

        while True:
            if self.color == current_color:
                self.color = choice(self.colors)
            else:
                break

        self.canvas.config(bg=self.color)
        self.start_time = time.monotonic() # Ereignis Timer
        self.game_window.after(SWITCH_TIME, self.change_bg_color)


    def key_pressed(self):
        if self.color == DESIGNATED_COLOR:
            if self.color_locked:
                self.key_too_often_pressed += 1
            else:
                reaction_time = time.monotonic() - self.start_time
                self.reaction_times.append(reaction_time)
                self.color_locked = True
        else:
            self.false_positives += 1

    def stop_game(self):
        self.stop_test = True
        if self.reaction_times:
            # mean() auf Liste ohne Einträge führt zu einem Fehler
            print("Gemittelte Reaktionszeit:", mean(self.reaction_times))
            print("Reaktionszeit pro richtiger Farbe:", self.reaction_times)
        print("Bei richtiger Farbe zu oft gedrückt:", self.key_too_often_pressed)
        print("Bei richtiger Farbe nicht gedrückt:", self.color_missed)
        print("Bei falscher Farbe gedrückt:", self.false_positives)


def main():
    root = Tk()
    root.bind("<Escape>", lambda e: game.stop_game())
    game = Game(root)
    main_window = Main_Window(root, game)
    root.bind("<space>", lambda e: game.key_pressed()) # Ereignis Tastendruck

    root.mainloop()



if __name__ == '__main__':
    main()
Gruß
Atalanttore
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Klasse ›Main_Window‹ ist eigentlich nur eine Funktion und der Inhalt kann quasi komplett nach ›main‹ verschoben werden. Die Fehlermeldung sagt eigentlich ganz klar, dass eine Tk-Klasse kein Attribut Canvas hat, denn das ist im Modul tkinter.

Zeile 46: explizit auf True prüfen braucht man nicht. `if self.stop_test:`
Zeile 55: eine while-True-Schleife ist nur sinnvoll, wenn die Abbruchbedingung innerhalb des Blocks geprüft wird, hier:

Code: Alles auswählen

        current_color = self.color
        while self.color == current_color:
            self.color = choice(self.colors)
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Danke fürs Durchgehen meines Codes.

Ich habe den angepassten Code nun um ein Ausgabefenster erweitert:

Code: Alles auswählen

from tkinter import *
from random import choice
import time
from statistics import mean



DESIGNATED_COLOR = "red"
SWITCH_TIME = 1000



class Game:
    def __init__(self, game_window):
        self.game_window = game_window
        self.colors = ["green", "red", "blue", "yellow", "black"]
        self.color = None
        self.reaction_times = []
        self.false_positives = 0
        self.color_missed = 0
        self.key_too_often_pressed = 0
        self.stop_test = False
        self.color_locked = False

    def start_game(self):
        self.game_window.attributes("-fullscreen", True)
        self.canvas = Canvas(self.game_window, width=self.game_window.winfo_screenwidth(),
                                height=self.game_window.winfo_screenheight(),
                                highlightthickness=0)
        self.canvas.pack()
        self.change_bg_color()


    def change_bg_color(self, color_locked = False):
        if self.stop_test:
            self.game_window.quit()

        if not self.color_locked and self.color == DESIGNATED_COLOR:
            self.color_missed += 1

        self.color_locked = color_locked
        current_color = self.color

        while self.color == current_color:
            self.color = choice(self.colors)

        self.canvas.config(bg=self.color)
        self.start_time = time.monotonic() # Ereignis Timer
        self.game_window.after(SWITCH_TIME, self.change_bg_color)


    def key_pressed(self):
        if self.color == DESIGNATED_COLOR:
            if self.color_locked:
                self.key_too_often_pressed += 1
            else:
                reaction_time = time.monotonic() - self.start_time
                self.reaction_times.append(reaction_time)
                self.color_locked = True
        else:
            self.false_positives += 1


    def show_results(self):
        self.stop_test = True
        results_window = Toplevel(self)
        results_window.title("Ergebnisse")

        if self.reaction_times:
            # mean() auf Liste ohne Einträge führt zu einem Fehler
            label_reaction_times = Label(self, text="Gemittelte Reaktionszeit: {}".format(mean(self.reaction_times)))

            label_for_listbox = Label(self, text="Reaktionszeit pro richtiger Farbe:")
            listbox_reaction_times = Listbox(self)
            for item in self.reaction_times:
                listbox_reaction_times.insert(item)

            label_reaction_times.grid(row=0, column=0)
            label_for_listbox.grid(row=1, column=0)
            listbox_reaction_times.grid(row=1, column=1)

        label_too_often = Label(self, text="Bei richtiger Farbe zu oft gedrückt: {}".format(self.key_too_often_pressed))
        label_color_missed = Label(self, text="Bei richtiger Farbe nicht gedrückt: {}".format(self.color_missed))
        label_false_positives = Label(self, text="Bei falscher Farbe gedrückt: {}".format(self.false_positives))
        ok_button = Button(self, text="OK", command=results_window.quit)

        label_too_often.grid(row=2, column=0)
        label_color_missed.grid(row=3, column=0)
        label_false_positives.grid(row=4, column=0)
        ok_button.grid(row=6, column=0)

        # if self.reaction_times:
        #     # mean() auf Liste ohne Einträge führt zu einem Fehler
        #     print("Gemittelte Reaktionszeit:", mean(self.reaction_times))
        #     print("Reaktionszeit pro richtiger Farbe:", self.reaction_times)
        # print("Bei richtiger Farbe zu oft gedrückt:", self.key_too_often_pressed)
        # print("Bei richtiger Farbe nicht gedrückt:", self.color_missed)
        # print("Bei falscher Farbe gedrückt:", self.false_positives)


def main():
    root = Tk()
    root.bind("<Escape>", lambda e: game.show_results())

    game = Game(root)

    root.bind("<space>", lambda e: game.key_pressed()) # Ereignis Tastendruck

    root.title("Spiel")
    root.geometry('640x480')

    start_button = Button(root, text="Start", command=game.start_game)  # !!!
    start_button.pack()
    close_button = Button(root, text="Schließen", command=root.quit)
    close_button.pack()

    root.mainloop()



if __name__ == '__main__':
    main()
Leider hakt es wieder irgendwo und die Fehlermeldung `AttributeError: 'Game' object has no attribute 'tk'` verstehe ich nicht wirklich.
Mit `root = Tk()` und `game = Game(root)` müsste `Game` eigentlich `tk` übergeben bekommen haben. `Tk()` und `tk` sind nicht unbedingt dasselbe, aber im Code wird nirgends einem Objekt der Bezeichner `tk` zugewiesen.

Gruß
Atalanttore
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich bekomme den Fehler auch nicht, dein Skript funktioniert fuer mich.
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Da im ganzen Quelltext kein `tk` vorkommt, kann das eigentlich nicht sein, und bei mir passiert das auch nicht. Kannst Du den gesamten Traceback zeigen‽
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Da Du nicht angegeben hast, wo der Fehler auftritt, kann man nur raten. In ›show_results‹ benutzt Du die Game-Instanz an Stellen, wo eine Tk-Instanz erwartet wird. Geht natürlich nicht.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Der komplette Traceback ist folgender, wenn (hätte ich vielleicht dazu schreiben sollen :oops: ) man das Programm mit der ESC-Taste beendet.

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1702, in __call__
    return self.func(*args)
  File "/home/ata/source/test.py", line 103, in <lambda>
    root.bind("<Escape>", lambda e: game.show_results())
  File "/home/ata/source/test.py", line 66, in show_results
    results_window = Toplevel(self)
  File "/usr/lib/python3.6/tkinter/__init__.py", line 2339, in __init__
    BaseWidget.__init__(self, master, 'toplevel', cnf, {}, extra)
  File "/usr/lib/python3.6/tkinter/__init__.py", line 2289, in __init__
    BaseWidget._setup(self, master, cnf)
  File "/usr/lib/python3.6/tkinter/__init__.py", line 2259, in _setup
    self.tk = master.tk
AttributeError: 'Game' object has no attribute 'tk'

@Sirius3: Welche Stellen in folgender Funktion ›show_results‹ meinst du genau?

Code: Alles auswählen

    def show_results(self):
        self.stop_test = True
        results_window = Toplevel(self)
        results_window.title("Ergebnisse")

        if self.reaction_times:
            # mean() auf Liste ohne Einträge führt zu einem Fehler
            label_reaction_times = Label(self, text="Gemittelte Reaktionszeit: {}".format(mean(self.reaction_times)))

            label_for_listbox = Label(self, text="Reaktionszeit pro richtiger Farbe:")
            listbox_reaction_times = Listbox(self)
            for item in self.reaction_times:
                listbox_reaction_times.insert(item)

            label_reaction_times.grid(row=0, column=0)
            label_for_listbox.grid(row=1, column=0)
            listbox_reaction_times.grid(row=1, column=1)

        label_too_often = Label(self, text="Bei richtiger Farbe zu oft gedrückt: {}".format(self.key_too_often_pressed))
        label_color_missed = Label(self, text="Bei richtiger Farbe nicht gedrückt: {}".format(self.color_missed))
        label_false_positives = Label(self, text="Bei falscher Farbe gedrückt: {}".format(self.false_positives))
        ok_button = Button(self, text="OK", command=results_window.quit)

        label_too_often.grid(row=2, column=0)
        label_color_missed.grid(row=3, column=0)
        label_false_positives.grid(row=4, column=0)
        ok_button.grid(row=6, column=0)
Gruß
Atalanttore
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Atalanttore: Die Zeile steht doch im Traceback: ``results_window = Toplevel(self)``.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Ich habe die Zeile geändert und meine Überlegungen als Kommentar hinzugefügt:

Code: Alles auswählen

def main():
    root = Tk() # 1. Tk-Instanz 'root' wird instanziert

    game = Game(root) # 2. Referenz auf Tk-Instanz 'root' wird übergeben
    
class Game:
    def __init__(self, game_window): # 3. Formaler Parameter 'game_window' enthält Referenz auf Tk-Instanz 'root'
        self.game_window = game_window # 4. Objektvariable 'self.game_window' bekommt Referenz auf Tk-Instanz 'root'

    def show_results(self):       
        results_window = Toplevel(self.game_window) # 5. Toplevel bekommt Referenz auf die Tk-Instanz 'root'
Funktioniert so leider wieder nicht:

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1702, in __call__
    return self.func(*args)
  File "/home/ata/source/test.py", line 103, in <lambda>
    root.bind("<Escape>", lambda e: game.show_results())
  File "/home/ata/source/test.py", line 82, in show_results
    label_too_often = Label(self, text="Bei richtiger Farbe zu oft gedrückt: {}".format(self.key_too_often_pressed))
  File "/usr/lib/python3.6/tkinter/__init__.py", line 2763, in __init__
    Widget.__init__(self, master, 'label', cnf, kw)
  File "/usr/lib/python3.6/tkinter/__init__.py", line 2289, in __init__
    BaseWidget._setup(self, master, cnf)
  File "/usr/lib/python3.6/tkinter/__init__.py", line 2259, in _setup
    self.tk = master.tk
AttributeError: 'Game' object has no attribute 'tk'
Gruß
Atalanttore
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das passt doch alles vorne und hinten nicht zusammmen. Nirgendwo im gezeigten Code kommt ein Label vor. Aber in der Fehlermeldung. Solange Code und Fehler nicht zusammen passen, kann hier doch keiner was dazu sagen.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

Ich habe nur die von __blackjack__ beschriebene Codezeile geändert.

Gruß
Atalanttore
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich finde es etwas viel verlangt, das ich mir hier Code aus mehreren Posts zusammen mit vorgeschlagenen Aenderungen selbst zusammen basteln soll, um dann am Ende hoffentlich mit etwas dazustehen, dass dem entspricht, was du da gemacht hast. Und dabei dann auch noch davon auszugehen, dass weder ich noch du etwas falsch verstanden haben.

Wenn du nicht bereit bist, Code mit dazugehoerigem Fehler zu posten, kann ich dir nicht helfen. Und ob andere das koennen, wage ich zu bezweifeln. Das gehoert schon zu deinen Aufgaben, wenn du dir Hilfe wuenschst.
Atalanttore
User
Beiträge: 407
Registriert: Freitag 6. August 2010, 17:03

__deets__ hat geschrieben: Sonntag 26. August 2018, 20:22 Und dabei dann auch noch davon auszugehen, dass weder ich noch du etwas falsch verstanden haben.
Ich gehe eigentlich davon aus, dass ich etwas falsch verstanden und programmiert habe.


Der Code sieht momentan so aus:

Code: Alles auswählen

from tkinter import *
from random import choice
import time
from statistics import mean



DESIGNATED_COLOR = "red"
SWITCH_TIME = 1000



class Game:
    def __init__(self, game_window): # 3. Formaler Parameter 'game_window' enthält Referenz auf Tk-Instanz 'root'
        self.game_window = game_window # 4. Objektvariable 'self.game_window' bekommt Referenz auf Tk-Instanz 'root'
        self.colors = ["green", "red", "blue", "yellow", "black"]
        self.color = None
        self.reaction_times = []
        self.false_positives = 0
        self.color_missed = 0
        self.key_too_often_pressed = 0
        self.stop_test = False
        self.color_locked = False

    def start_game(self):
        self.game_window.attributes("-fullscreen", True)
        self.canvas = Canvas(self.game_window, width=self.game_window.winfo_screenwidth(),
                                height=self.game_window.winfo_screenheight(),
                                highlightthickness=0)
        self.canvas.pack()
        self.change_bg_color()


    def change_bg_color(self, color_locked = False):
        if self.stop_test:
            self.game_window.quit()

        if not self.color_locked and self.color == DESIGNATED_COLOR:
            self.color_missed += 1

        self.color_locked = color_locked
        current_color = self.color

        while self.color == current_color:
            self.color = choice(self.colors)

        self.canvas.config(bg=self.color)
        self.start_time = time.monotonic() # Ereignis Timer
        self.game_window.after(SWITCH_TIME, self.change_bg_color)


    def key_pressed(self):
        if self.color == DESIGNATED_COLOR:
            if self.color_locked:
                self.key_too_often_pressed += 1
            else:
                reaction_time = time.monotonic() - self.start_time
                self.reaction_times.append(reaction_time)
                self.color_locked = True
        else:
            self.false_positives += 1


    def show_results(self):
        self.stop_test = True
        results_window = Toplevel(self.game_window) # 5. Toplevel bekommt Referenz auf die Tk-Instanz 'root'
        results_window.title("Ergebnisse")

        if self.reaction_times:
            # mean() auf Liste ohne Einträge führt zu einem Fehler
            label_reaction_times = Label(self, text="Gemittelte Reaktionszeit: {}".format(mean(self.reaction_times)))

            label_for_listbox = Label(self, text="Reaktionszeit pro richtiger Farbe:")
            listbox_reaction_times = Listbox(self)
            for item in self.reaction_times:
                listbox_reaction_times.insert(item)

            label_reaction_times.grid(row=0, column=0)
            label_for_listbox.grid(row=1, column=0)
            listbox_reaction_times.grid(row=1, column=1)

        label_too_often = Label(self, text="Bei richtiger Farbe zu oft gedrückt: {}".format(self.key_too_often_pressed))
        label_color_missed = Label(self, text="Bei richtiger Farbe nicht gedrückt: {}".format(self.color_missed))
        label_false_positives = Label(self, text="Bei falscher Farbe gedrückt: {}".format(self.false_positives))
        ok_button = Button(self, text="OK", command=results_window.quit)

        label_too_often.grid(row=2, column=0)
        label_color_missed.grid(row=3, column=0)
        label_false_positives.grid(row=4, column=0)
        ok_button.grid(row=6, column=0)


def main():
    root = Tk() # 1. Tk-Instanz 'root' wird instanziert
    root.bind("<Escape>", lambda e: game.show_results())

    game = Game(root) # 2. Referenz auf Tk-Instanz 'root' wird übergeben

    root.bind("<space>", lambda e: game.key_pressed()) # Ereignis Tastendruck
    root.title("Spiel")
    root.geometry('640x480')

    start_button = Button(root, text="Start", command=game.start_game)  # !!!
    start_button.pack()
    close_button = Button(root, text="Schließen", command=root.quit)
    close_button.pack()

    root.mainloop()


if __name__ == '__main__':
    main()

Drückt man während der Programmausführung auf die ESC-Taste kommen folgende Fehler:

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 1702, in __call__
    return self.func(*args)
  File "/home/ata/source/test.py", line 95, in <lambda>
    root.bind("<Escape>", lambda e: game.show_results())
  File "/home/ata/source/test.py", line 82, in show_results
    label_too_often = Label(self, text="Bei richtiger Farbe zu oft gedrückt: {}".format(self.key_too_often_pressed))
  File "/usr/lib/python3.6/tkinter/__init__.py", line 2763, in __init__
    Widget.__init__(self, master, 'label', cnf, kw)
  File "/usr/lib/python3.6/tkinter/__init__.py", line 2289, in __init__
    BaseWidget._setup(self, master, cnf)
  File "/usr/lib/python3.6/tkinter/__init__.py", line 2259, in _setup
    self.tk = master.tk
AttributeError: 'Game' object has no attribute 'tk'

Gruß
Atalanttore
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und schon bricht Klarheit aus. Schau dir doch mal genau an, WAS du da als erstes Argument uebergibst. Und was das sein sollte.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Atakanttore: ich zitiere mich gerne selbst: In ›show_results‹ benutzt Du die Game-Instanz an Stellen, wo eine Tk-Instanz erwartet wird. Das wichtige daran ist der Plural bei `Stellen`.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Atalanttore

Habe dein Skript ein wenig manipuliert (nicht optimiert):

Code: Alles auswählen

from tkinter import *
from random import choice
import time
from statistics import mean



DESIGNATED_COLOR = "red"
SWITCH_TIME = 1000



class Game:
    def __init__(self, game_window): # 3. Formaler Parameter 'game_window' enthält Referenz auf Tk-Instanz 'root'
        self.game_window = game_window # 4. Objektvariable 'self.game_window' bekommt Referenz auf Tk-Instanz 'root'
        self.colors = ["green", "red", "blue", "yellow", "black"]
        self.color = None
        self.reaction_times = []
        self.false_positives = 0
        self.color_missed = 0
        self.key_too_often_pressed = 0
        self.stop_test = False
        self.color_locked = False

    def start_game(self):
        self.game_window.attributes("-fullscreen", True)
        self.canvas = Canvas(self.game_window, width=self.game_window.winfo_screenwidth(),
                                height=self.game_window.winfo_screenheight(),
                                highlightthickness=0)
        self.canvas.pack()
        self.change_bg_color()


    def change_bg_color(self, color_locked = False):
        if self.stop_test:
            #self.game_window.quit()
            return

        if not self.color_locked and self.color == DESIGNATED_COLOR:
            self.color_missed += 1

        self.color_locked = color_locked
        current_color = self.color

        while self.color == current_color:
            self.color = choice(self.colors)

        self.canvas.config(bg=self.color)
        self.start_time = time.monotonic() # Ereignis Timer
        self.game_window.after(SWITCH_TIME, self.change_bg_color)


    def key_pressed(self):
        if self.color == DESIGNATED_COLOR:
            if self.color_locked:
                self.key_too_often_pressed += 1
            else:
                reaction_time = time.monotonic() - self.start_time
                self.reaction_times.append(reaction_time)
                self.color_locked = True
        else:
            self.false_positives += 1


    def show_results(self, event=None):
        self.stop_test = True
        results_window = Toplevel(self.game_window) # 5. Toplevel bekommt Referenz auf die Tk-Instanz 'root'
        results_window.title("Ergebnisse")

        if self.reaction_times:
            # mean() auf Liste ohne Einträge führt zu einem Fehler
            label_reaction_times = Label(self, text="Gemittelte Reaktionszeit: {}".format(mean(self.reaction_times)))

            #label_for_listbox = Label(self, text="Reaktionszeit pro richtiger Farbe:")
            #listbox_reaction_times = Listbox(self)
            label_for_listbox = Label(results_window, text="Reaktionszeit pro richtiger Farbe:")
            listbox_reaction_times = Listbox(results_window)

            for item in self.reaction_times:
                listbox_reaction_times.insert(item)

            label_reaction_times.grid(row=0, column=0)
            label_for_listbox.grid(row=1, column=0)
            listbox_reaction_times.grid(row=1, column=1)

        #label_too_often = Label(self, text="Bei richtiger Farbe zu oft gedrückt: {}".format(self.key_too_often_pressed))
        #label_color_missed = Label(self, text="Bei richtiger Farbe nicht gedrückt: {}".format(self.color_missed))
        #label_false_positives = Label(self, text="Bei falscher Farbe gedrückt: {}".format(self.false_positives))
        #ok_button = Button(self, text="OK", command=results_window.quit)

        label_too_often = Label(results_window, text="Bei richtiger Farbe zu oft gedrückt: {}".format(self.key_too_often_pressed))
        label_color_missed = Label(results_window, text="Bei richtiger Farbe nicht gedrückt: {}".format(self.color_missed))
        label_false_positives = Label(results_window, text="Bei falscher Farbe gedrückt: {}".format(self.false_positives))
        ok_button = Button(results_window, text="OK", command=results_window.quit)

        label_too_often.grid(row=2, column=0)
        label_color_missed.grid(row=3, column=0)
        label_false_positives.grid(row=4, column=0)
        ok_button.grid(row=6, column=0)


def main():
    root = Tk() # 1. Tk-Instanz 'root' wird instanziert
    #root.bind("<Escape>", lambda e: game.show_results())

    game = Game(root) # 2. Referenz auf Tk-Instanz 'root' wird übergeben
    root.bind("<Escape>", lambda e: game.show_results())
    root.bind("<space>", lambda e: game.key_pressed()) # Ereignis Tastendruck
    root.title("Spiel")
    root.geometry('640x480')

    start_button = Button(root, text="Start", command=game.start_game)  # !!!
    start_button.pack()
    close_button = Button(root, text="Schließen", command=root.quit)
    close_button.pack()

    root.mainloop()


if __name__ == '__main__':
    main()
Gruss wuf ;-)
Take it easy Mates!
Antworten