Exception in Tkinter callback: TypeError: 'NoneType' object

Fragen zu Tkinter.
Antworten
Newcomer
User
Beiträge: 131
Registriert: Sonntag 15. Mai 2011, 20:41

hi, ich bin neu hier im Forum und das is meine erste Frage, also beschwert euch nicht über einen eventuellen schlechten Stil. Ich bin Hobbyprogrammierer und das schon oder erst seit einem Jahr.
Ich hab ne Frage bezüglich einem Error in Python. Also ich hab mit tkinter angefangen, und dachte mir ich programmiere tik tak to mit tkinter. Alles klappt, aber wenn ich auf ein zweiten Button drücke, kommt eine Fehlermeldung: Exception in Tkinter callback: TypeError: 'NoneType' object
Komischerweise läuft das Programm trotzdem wie gewollt ab... Das Problem ist bei dieser Fehlermeldung nur, dass ich nirgends über ein None Typ Object iteriere und dort wo ich es tat, hab ich None mit "None" (also Stringtyp) ersetzt, aber trotzdem erhalte ich die Fehlermeldung, das versteh ich nicht.
Wenn ihr euch das mal anschauen würdet, wärs nett.

Hier der Code:

Code: Alles auswählen

from tkinter import *
from copy import deepcopy


class ObjectSaver(object):
    def __init__(self):
        self.__merkeObject=[None]
    def get(self,instanz):
        self.__merkeObject.append(instanz)
    def replace_with(self,index):
        if type(index) is not int:
            raise TypeError("Index Argument must be an Integer")
        wort=self.__merkeObject.pop(index)
        self.__merkeObject.insert(index,None)
        return wort
    def printOut(self):
        return self.__merkeObject


class Gewonnen(IndexError):
    pass

merker=ObjectSaver()
class Hintergrund():
    WinnerKreis="Kreis hat gewonnen"
    WinnerKreuz="Kreuz hat gewonnen"
    
    def __init__(self,speicher):
        NoneL=[[i,k,l] for i in ["None","None"] for k in ["None","None"] for l in ["None","None"]]
        NoneL.extend(["None","None","None"])
        
        self.kreisFeld=deepcopy(NoneL)
        self.kreuzFeld=deepcopy(NoneL)
        self.speicher=speicher
       
                        
        
        
        self.kreuzDran=False
        self.kreisDran=True
        self.com=[["0","1","2"],["3","4","5"],["6","7","8"],["0","3","6"],["1","4","7"],["2","5","8"],["0","4","8"],["2","4","6"]]
        self.fenster=Tk()
        self.infromationLabel=Label(self.fenster,text="")
        self.value=IntVar()
        self.label=Label(self.fenster,text="Käsekästchen")
        self.spielsteine=[
        Checkbutton(self.fenster,indicatoron=0,height=6,width=15,onvalue=1,offvalue=1,variable=self.value,command=self.aktion),
        Checkbutton(self.fenster,indicatoron=0,height=6,width=15,onvalue=2,offvalue=2,variable=self.value,command=self.aktion),
        Checkbutton(self.fenster,indicatoron=0,height=6,width=15,onvalue=3,offvalue=3,variable=self.value,command=self.aktion),
        Checkbutton(self.fenster,indicatoron=0,height=6,width=15,onvalue=4,offvalue=4,variable=self.value,command=self.aktion),
        Checkbutton(self.fenster,indicatoron=0,height=6,width=15,onvalue=5,offvalue=5,variable=self.value,command=self.aktion),
        Checkbutton(self.fenster,indicatoron=0,height=6,width=15,onvalue=6,offvalue=6,variable=self.value,command=self.aktion),
        Checkbutton(self.fenster,indicatoron=0,height=6,width=15,onvalue=7,offvalue=7,variable=self.value,command=self.aktion),
        Checkbutton(self.fenster,indicatoron=0,height=6,width=15,onvalue=8,offvalue=8,variable=self.value,command=self.aktion),
        Checkbutton(self.fenster,indicatoron=0,height=6,width=15,onvalue=9,offvalue=9,variable=self.value,command=self.aktion)]
        zählerZeile=0
        zählerSpalte=-1
        for i in self.spielsteine:
            self.speicher.get(i)
            zählerSpalte +=1
            if zählerSpalte >=3:
                zählerSpalte=0
                zählerZeile +=1
            i.grid(row=zählerZeile,column=zählerSpalte,pady=5,padx=5)
        self.fenster.mainloop()
        self.infromationLabel.pack(side=BOTTOM)
        
            
            
                
                    
    def aktion(self):
        
        if self.kreisDran:
            self.infromationLabel.config(text="Kreis ist an der Reihe")
            
            self.kreisDran=False
            self.kreuzDran=True
            self.kreis(self.value.get())
        elif self.kreuzDran:
            self.infromationLabel.config(text="Kreuz ist an der Reihe")
            self.kreisDran=True
            self.kreuzDran=False
            self.kreuz(self.value.get())

    def kreis(self,value):
        
        
        



        
        objekte=self.speicher.printOut()
        einzelner_button=objekte[value]
        einzelner_button.destroy()
        value -=1
        kreisLabel=Label(self.fenster,height=6,width=15,text="O",font=100)
        row=value//3
        column=value%3
        kreisLabel.grid(row=row,column=column,pady=5,padx=5)
        

        self._ordneEin(self.kreisFeld,value)
        print(self.kreisFeld)
        for i in self.kreisFeld:
            if not "None" in i:
                print(self.kreisFeld,"kreis")
                raise Gewonnen("Kreis hat gewonnen")
                
        
        
        
        
        
        
            
            
                      
            
        
        
        

    def kreuz(self,value):
        schublade=list()
                
                    
       
                
        objekte=self.speicher.printOut()
        einzelner_button=objekte[value]
        einzelner_button.destroy()
        value -=1
        kreisLabel=Label(self.fenster,height=6,width=15,text="X",font=100)
        row=value//3
        column=value%3
        kreisLabel.grid(row=row,column=column,pady=5,padx=5)

        
        self.kreuzFeld=self._ordneEin(self.kreuzFeld,value)
        print(self.kreuzFeld)
        for i in self.kreuzFeld:
            if not "None" in i:
                print(self.kreuzFeld,"kreuz")
            
                
                raise Gewonnen("Kreuz hat gewonnen")


    def _ordneEin(self,liste,value):
        value=str(value)
        
                   
        
        for i in deepcopy(self.com):
            for r in deepcopy(i):
                if value==r:
                    deep=i.index(r)
                    shallow=self.com.index(i)
                    
                    
                    liste[shallow][deep]=value
                    
                      


a=Hintergrund(merker)
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Puh, so läßt sich das schwer erkennen wo der Fehler herkommt, warscheinlich eine Funktion ohne Rückgabe(return), das wäre dann nämlich None. Kannst du mal den vollständigen Traceback posten?

Zu der Sachen Stil, ich glaube das Spiel heißt "Tic Tac Toe" und das geht auch wesentlich übersichtlicher, mehr will ich jetzt mal nicht mekern :wink: Aber weißt du eigentlich was die "__"-doppelten Unterstriche bewirken? Diese solltest du durch einen einfachen Unterstrich ersetzen.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Newcomer
User
Beiträge: 131
Registriert: Sonntag 15. Mai 2011, 20:41

Hey danke, dass das so schnell ging :D :D
Hier der vollständige Traceback:

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python31\lib\tkinter\__init__.py", line 1399, in __call__
    return self.func(*args)
  File "C:\py4kids\Käsekästchen.py", line 84, in aktion
  File "C:\py4kids\Käsekästchen.py", line 143, in kreuz
TypeError: 'NoneType' object is not iterable
Achso ja, ich weiß was die Unterstriche bewirken, nämlich __: private und _: protected, private: nur in klassendefinition zugriff protected: werden nicht mitimportiert, aber man kann von außen, wenn man den Namen kenn, zugreifen
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Du hast mehrer Probleme da drin, zum einen hast du für meinen geschmack zuviele und vorallem unötige "deepcopy"s drin. Zum anderen gib dir die Liste mal zweimal davor aus, dann ist sie None. Du bearbeitest diese so, das die irgendwann None wird, das heißt deine Liste ist None und über ein solches None kann nicht iteriert werden. Ich will jetzt nicht sagen fang am besten nochmal von vorne an (auch wenn das meist sehr nützlich sein kann, gerade in den Anfängen), aber du solltest auf jedenfall deinen Quelltext nochmal strukturieren. So kann man den Fehler kaum entdecken. Leg vorallem mal ein Augenmerk auf deine Liste, sprich welche Daten bekommst du und welche brauchst du zur Auswertung. Und schmeiß die "deepcopy"s raus, hier wäre meine Frage auch wieder, weißt du eigentlich was "deepcopy" macht?

Die Antwort auf meine letzte Frage ist nämlich völlig falsch. "private" und "protected" gibt es in Python nicht. Ein einfacher vorangehender Unterstrich weißt den Programmierer lediglich darauf hin das er diese Funktion nicht nutzen sollte, da sie für den internen Gebrauch geschrieben wurde. Ein doppelter Unterstrich dient dazu bei Mehrfach-Vererbungen bestimmte Variablen nicht zu überschreiben und sollte nur wenn genau weiß was man dort tut verwendet werden, sonst besser nie.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Newcomer
User
Beiträge: 131
Registriert: Sonntag 15. Mai 2011, 20:41

Hey danke :P für deine Hilfe. Anfangen werde ich jetzt nicht nochmal, aber überarbeiten werde ich es schon. Das deepcopy brauche ich nicht, da hast du recht, aber die habe ich in der vorigen Version fürn anderes problem benötigt,
und werde sie jetzt rausschmeißen :wink:
Ich werd jetzt alles nochmal überarbeiten, dann noch ne KI dazuprogrammieren, dann bin ich fertig.
Und was die Frage angeht, ich zitiere hier aus meinem Lernbuch für Python: Objektorientierte Programmierung mit Python 3
<"Private Attribute:
Stark private Attribute [...] Es ist nur möglich, innerhalb der Klassendefinition auf ein solches Attribut zuzugreifen.
schwach private Attribute [..] werden durch "from ... import*" nicht mitimportiert [...] Man kann aber problemlos auf das Attribut zugreifen, wenn man den Namen kennt.> Zitat Ende
Das private und protected (internal) kommt aus C# und kann mit _ und __ verglichen werden, so wie es im Buch geschrieben steht. Ich vertrau dem Autor einfach mal, es sei denn du bist Informatik Professor oder so was. Dann halt ich meine Klappe :lol: :D :oops: :oops: :oops: :oops:
lunar

@Newcomer: Das Zitat aus dem Buch beweist allenfalls, dass das Buch von minderer Qualität ist. In idiomatischem Python-Quelltext kennzeichnet der einzelne führende Unterstrich per Konvention ein Attribut, welches Implementierungsdetail und mithin nicht Bestandteil der öffentlichen Schnittstelle ist. Doppelte führende Unterstriche sind nur für bestimmte Spezialfälle.

Der Vergleich mit C# ist nur bedingt sinnvoll, aus einem offensichtlichen Grund: Python ist nicht C#. Daher kannst Du auf die Aussagen eines erfahrenen Python-Programmierers vertrauen, selbst wenn die Dinge in C# anders liegen.
BlackJack

@Newcomer: Vertrau dem Autor da mal lieber nicht, denn auch auf die `__`-Namen kann man zugreifen. Da passiert nur "name mangling" — konkret wird da noch der Klassenname in den Attributnamen aufgenommen — und das ist von den Leuten die Python entworfen haben eben dafür gedacht um Namenskollisionen bei Mehrfachvererbung zu verhindern. Das kann nicht aus C# kommen, da C# keine Mehrfachvererbung kennt. Wenn man keine Mehrfachvererbung hat, macht es also nicht so viel Sinn die doppelten Unterstriche zu verwenden. Zumal es dann geringfügig umständlicher wird, wenn man dann doch mal von aussen oder einer abgeleiteten Klasse auf so ein Attribut zugreifen möchte. Etwas wo viele C#/Java/…-Programmierer sagen würden, dass man das auch nicht können sollen dürfte, aber da ist die Python-Philosophie einfach eine andere. Man kann nicht alles Wissen und alle "best practices" von einer Programmiersprache einfach auf eine Andere übertragen.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@Newcomer
Nein, ich bin kein Informatik-Professor, dennoch ist das so falsch bzw. das was du dort rein interpretierst. Zudem Datenkapslung gibt es erstens nicht erst seit C#, zweitens ist protected keinesfalls gleich internal und dies kann man erst recht nicht mit _ und __ vergleichen.

Hiermal ein Beispiel zum Nachdenken:

Code: Alles auswählen

>>> class A(object):
        def __init__(self, a):
            self.__a = a

>>> class B(A):
        def __init__(self, b):
            self.__a = b

>>> B(2)._B__a
2
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Newcomer
User
Beiträge: 131
Registriert: Sonntag 15. Mai 2011, 20:41

Hey ich glaub du verstehst mich falsch, ich hab keineswegs behauptet, dass internal=protected ist, es gibt protected und protected-internal. Ich hab das in Klammern dazugeschrieben.
Aber danke für eure Hilfe und Zeit, wenn ich nochmal ne Frage hab kann ich sie dann stellen?
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Tja, sah aber so aus :roll:
Warum schreibst du es auch nicht einfach "private, protected und internal", statt in Klammern, schließlich ist es ein gleichberechtigter Zugriffsmodifizierer.

Und wieso solltest du hier keine Fragen mehr stellen dürfen? Wenn es das Thema betrifft einfach hier posten, wenn nicht dann mach einen neuen Thread auf. :wink:
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Antworten