Search code examples
pythonuser-interfaceattributestkinterattributeerror

Python Tkinter attribute error entry in random letter typing test


Traceback (most recent call last):
  File "C:/Users/Brett/Documents/shellrunpractice", line 83, in <module>
    app = simpleapp_tk(None)
  File "C:/Users/Brett/Documents/shellrunpractice", line 9, in __init__
    self.initialize()
  File "C:/Users/Brett/Documents/shellrunpractice", line 22, in initialize
    command=self.usertypetest(0),fg="black",bg="green")
  File "C:/Users/Brett/Documents/shellrunpractice", line 66, in usertypetest
    self.keeptime(num1)
  File "C:/Users/Brett/Documents/shellrunpractice", line 44, in keeptime
    self.entry.selection_range(0, Tkinter.END)
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1845, in __getattr__
    return getattr(self.tk, attr)
AttributeError: entry

Here is the code:

import Tkinter
import random
from time import sleep

class simpleapp_tk(Tkinter.Tk):
    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def initialize(self):
        self.grid()


        self.labelVariable = Tkinter.StringVar()
        label = Tkinter.Label(self,textvariable=self.labelVariable,anchor="w",fg="blue",bg="gold")
        label.grid(column=0,row=0,sticky='EW')
        self.labelVariable.set(u"Press button to begin!")


        self.button = Tkinter.Button(self,text=u"Start",
                                command=self.usertypetest(0),fg="black",bg="green")
        self.button.grid(column=2,row=0)


        self.labelVariable2 = Tkinter.StringVar()
        label2 = Tkinter.Label(self,textvariable=self.labelVariable2,anchor="w",fg="blue",bg="gold")
        label2.grid(column=1,row=0,sticky='EW')
        self.labelVariable2.set(u'Time')


        self.entryVariable = Tkinter.StringVar()
        self.entry = Tkinter.Entry(self,textvariable=self.entryVariable,fg="black",bg="white")
        self.entry.grid(column=0,row=1,columnspan=2,sticky='EW')
        self.entryVariable.set(u"")


        self.grid_columnconfigure(0,weight=1)
        self.grid_rowconfigure(0,weight=1)
        self.resizable(True,True)


    def keeptime(self,num1):
        self.entry.selection_range(0, Tkinter.END)
        timer = num1
        t = 0
        while timer < 1:
            self.labelVariable2.set(t)
            sleep(.01)
            t += .01 



    def usertype(self):
        randletter = random.choice('qwer')
        self.labelVariable.set("Press "+randletter)
        userinput = self.entryVariable.get
        while userinput == '':
            pass
        if userinput == randletter:
            return 'Correct'    
        else:
            return 'Incorrect'

    def usertypetest(self,num1):
        self.keeptime(num1)
        for x in range(20):
            result = usertype()
            print result
            if result == 'Correct':
                y = y+5
            else:
                y = y-2
        timer += 1
        self.labelVariable.set(str(y)+' is your score')






if __name__ == "__main__":
    app = simpleapp_tk(None)
    app.title('LoL Practice')
    app.geometry("700x50")
    app.configure(background='black')
    app.mainloop()

I don't know why I am receiving this problem as I am just starting to learn Tkinter and I am not very good yet.

The goal of this program is that when the user clicks start, a timer will start, printing to a label, and they will be shown letters randomly chosen from (Q,W,E,R), which they must type as fast as possible. The program will time them and give them a score based on their errors at the end, and also stop the timer.

I am wondering if you guys could find any other errors in my code that could be causing that, and if so how to do it. I realize my code is very bad, I'm just starting in Tkinter, and I'm not entirely sure how everything works yet.

Any general comments about how I could improve my coding would also be greatly appreciated, as would any comments on how I could write a program to do what I want, as I feel my current method is not practical/functional.


Solution

  • command= expect only function name - without () and parameters.

    Using command=self.usertypetest(0) you run self.usertypetest(0)
    and its result is assigned to command=.

    But self.usertypetest(0) use self.entry which wasn't defined yet.

    self.entry is defined after Tkinter.Button(...command=self.usertypetest(0)...)

    If you need to assing function with parameters then use lambda

    command=lambda:self.usertypetest(0)
    

    And now it works.


    EDIT:

    Full working version:

    (I changed some variable names)

    import Tkinter
    import random
    
    class simpleapp_tk(Tkinter.Tk):
    
        def __init__(self,parent):
            Tkinter.Tk.__init__(self,parent)
            self.parent = parent
            self.initialize()
            self.started = False # True when timer is running
    
        def initialize(self):
            self.grid()
    
            self.infoVariable = Tkinter.StringVar()
            self.labelInfo = Tkinter.Label(self, textvariable=self.infoVariable, anchor="w", fg="blue", bg="gold")
            self.labelInfo.grid(column=0, row=0, sticky='EW')
            self.infoVariable.set(u"Press button to begin!")
    
            self.buttonStart = Tkinter.Button(self, text=u"Start", command=self.on_buttonStart, fg="black", bg="green")
            self.buttonStart.grid(column=2, row=0)
    
            self.timeVariable = Tkinter.StringVar()
            self.labelTime = Tkinter.Label(self, textvariable=self.timeVariable, anchor="w", fg="blue", bg="gold")
            self.labelTime.grid(column=1, row=0, sticky='EW')
            self.timeVariable.set(u'Time')
    
            self.entryVariable = Tkinter.StringVar()
            self.entry = Tkinter.Entry(self, textvariable=self.entryVariable, fg="black", bg="white")
            self.entry.grid(column=0, row=1, columnspan=2, sticky='EW')
            self.entryVariable.set(u"")
            self.entry.bind('<Key>', self.on_key) # event
    
            self.grid_columnconfigure(0,weight=1)
            self.grid_rowconfigure(0,weight=1)
            self.resizable(True,True)
    
    
        def on_buttonStart(self):
            if not self.started:
                # reset values
                self.started = True
                self.number_of_letters = 20
                self.score = 0
                self.time = 0.0
                # select first letter
                self.randletter = random.choice('qwer')
                # show first letter and score
                self.infoVariable.set('Score:' + str(self.score)+' | Press ' + self.randletter)
                # start timer
                self.after(100, self.timer)
    
        def on_key(self, event):
            if not self.started:
                self.entryVariable.set('')
            else:
                if event.char == self.randletter:
                    print 'Correct', event.char
                    self.score += 5
                else:
                    print 'Incorrect', event.char
                    self.score -= 2
    
                self.number_of_letters -= 1
    
                if self.number_of_letters == 0:
                    self.started = False
                    self.entryVariable.set('')
                    self.infoVariable.set('Score:' + str(self.score))
                else:
                    self.randletter = random.choice('qwer')
                    self.infoVariable.set('Score:' + str(self.score)+' | Press ' + self.randletter)
    
        def timer(self):
            if self.started:
                self.time += .1
                self.timeVariable.set('Time: ' + str(round(self.time,1)) )
                self.after(100, self.timer)
    
    
    if __name__ == "__main__":
        app = simpleapp_tk(None)
        app.title('LoL Practice')
        app.geometry("700x50")
        app.configure(background='black')
        app.mainloop()