Seite 1 von 2

Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Montag 20. August 2018, 15:31
von Atalanttore
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

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Montag 20. August 2018, 20:25
von __deets__
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.

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Montag 20. August 2018, 22:18
von Atalanttore
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

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Montag 20. August 2018, 22:27
von __deets__
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.

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Samstag 25. August 2018, 16:13
von Atalanttore
__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

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Samstag 25. August 2018, 16:59
von Sirius3
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)

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Samstag 25. August 2018, 19:33
von Atalanttore
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

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Samstag 25. August 2018, 21:08
von __deets__
Ich bekomme den Fehler auch nicht, dein Skript funktioniert fuer mich.

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Samstag 25. August 2018, 21:13
von __blackjack__
@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‽

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Samstag 25. August 2018, 21:14
von Sirius3
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.

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Sonntag 26. August 2018, 15:47
von Atalanttore
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

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Sonntag 26. August 2018, 16:32
von __blackjack__
@Atalanttore: Die Zeile steht doch im Traceback: ``results_window = Toplevel(self)``.

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Sonntag 26. August 2018, 17:06
von Atalanttore
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

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Sonntag 26. August 2018, 17:53
von __deets__
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.

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Sonntag 26. August 2018, 20:14
von Atalanttore
Ich habe nur die von __blackjack__ beschriebene Codezeile geändert.

Gruß
Atalanttore

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Sonntag 26. August 2018, 20:22
von __deets__
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.

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Sonntag 26. August 2018, 20:44
von Atalanttore
__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

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Sonntag 26. August 2018, 20:51
von __deets__
Und schon bricht Klarheit aus. Schau dir doch mal genau an, WAS du da als erstes Argument uebergibst. Und was das sein sollte.

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Montag 27. August 2018, 06:59
von Sirius3
@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`.

Re: Methode von Objekt B aus Objekt A aufrufen funktioniert nicht

Verfasst: Montag 27. August 2018, 10:46
von wuf
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 ;-)