TypeError: 'Function' missing one required positional argument: 'self'

1.7k views Asked by At

Code:

class App:

    root = Tk()
    button1 = Button()
    button2 = Button()
    button3 = Button()

    img1 = PhotoImage(file="blueBox.png")
    img2 = PhotoImage(file="redBox.png")
    deckImage = PhotoImage(file="blackBox.png")

The Error occurs after trying to call one of the 3 functions below:

    def showcolor1(self):
        if self.card1 != 0:
            self.card2 = 1
        else:
            self.card1 = 1

        self.button1.configure(image=self.getArrayValue(0))
        self.button1.configure(state=DISABLED)
        self.setChoosenPicture(self.getArrayValue(0))

I removed the functions that will be called by showcolor1/2/3, because they worked. There is just a problem with calling these functions with the Buttons command:

    def showcolor2(self):
        if self.card1 != 0:
            self.card2 = 2
        else:
            self.card1 = 2

        self.button2.configure(image=self.getArrayValue(1))
        self.button2.configure(state=DISABLED)
        self.setChoosenPicture(self.getArrayValue(1))

    def showcolor3(self):
        if self.card1 != 0:
            self.card2 = 3
        else:
            self.card1 = 3

        self.button2.configure(image=self.getArrayValue(1))
        self.button2.configure(state=DISABLED)
        self.setChoosenPicture(self.getArrayValue(2))


    button1 = Button(master=root, text="", image=deckImage, state=NORMAL, command=showcolor1)
    button1.pack()
    button2 = Button(master=root, text="", image=deckImage, state=NORMAL, command=showcolor2)
    button2.pack()
    button3 = Button(master=root, text="", image=deckImage, state=NORMAL, command=showcolor3)
    button3.pack()

    root.mainloop()

a = App()

Here is the whole code: It should show 3 Buttons that are black. ANd if you press BUtton 1 it should change his color. When you press 2 Buttons it checks if they have the same color or not. ::

from tkinter import *
class App:
    
    root = Tk()
    testArray = [1, 2, 2]
    cAmount = 0
    card1 = 0
    card2 = 0
    button1 = Button()
    button2 = Button()
    button3 = Button()
    
    img1 = PhotoImage(file="blueBox.png")
    img2 = PhotoImage(file="redBox.png")
    deckImage = PhotoImage(file="blackBox.png")

    def checkCards(self):
        if self.testArray[self.card1] == self.testArray[self.card2]:
            print("CORRECT")
        elif self.testArray[self.card1] != self.testArray[self.card2]:
            print("FALSE")
            if self.card1 == 1 or self.card2 == 1:
                self.button1.configure(image=self.deckImage)
                self.button1.configure(state=NORMAL)
            if self.card1 == 2 or self.card2 == 2:
                self.button2.configure(image=self.deckImage)
                self.button2.configure(state=NORMAL)
            if self.card1 == 3 or self.card2 == 3:
                self.button3.configure(image=self.deckImage)
                self.button3.configure(state=NORMAL)

    def setChoosenPicture(self):
        self.cAmount = self.cAmount + 1
        if self.cAmount == 2:
            self.checkCards()
            self.cAmount = 0
        else:
            return

    def getArrayValue(self, buttonIndex):
        if self.testArray[buttonIndex] == 1:
            return self.img1
        elif self.testArray[buttonIndex] == 2:
            return self.img2

    def showcolor1(self):
        if self.card1 != 0:
            self.card2 = 1
        else:
            self.card1 = 1

        self.button1.configure(image=self.getArrayValue(0))
        self.button1.configure(state=DISABLED)
        self.setChoosenPicture(self.getArrayValue(0))

    def showcolor2(self):
        if self.card1 != 0:
            self.card2 = 2
        else:
            self.card1 = 2

        self.button2.configure(image=self.getArrayValue(1))
        self.button2.configure(state=DISABLED)
        self.setChoosenPicture(self.getArrayValue(1))

    def showcolor3(self):
        if self.card1 != 0:
            self.card2 = 3
        else:
            self.card1 = 3

        self.button2.configure(image=self.getArrayValue(1))
        self.button2.configure(state=DISABLED)
        self.setChoosenPicture(self.getArrayValue(2))


    button1 = Button(master=root, text="", image=deckImage, state=NORMAL, command=showcolor1)
    button1.pack()
    button2 = Button(master=root, text="", image=deckImage, state=NORMAL, command=showcolor2)
    button2.pack()
    button3 = Button(master=root, text="", image=deckImage, state=NORMAL, command=showcolor3)
    button3.pack()

    root.mainloop()


    a = App()

The Error Message

4

There are 4 answers

1
Vihang Vidwans On

You have not defined the function in a class. The function must be called on an instance if there is 'self' parameter. e.g if x is an instance then you can call it as x.showcolor3().

1
Blckknght On

The problem with your code is that you're running too much stuff inside the class statement, when you probably want to be running it later, at top level. This especially causes a problem when you want to use the methods you've defined in the class. They use self, which should refer to an instance of the class. But you can't create an instance inside the body of the class statement itself, since the class hasn't finished being created yet.

Here's a much example that will raise the same error as your code:

class Foo:
    def bar(self):
        print("bar")

    bar()

    # this also won't work, though you won't get here due to the error above
    f = Foo()  # if you remove the line above, this will raise a NameError about Foo

To fix it, you need to move the code that uses the methods out of the class body. It might make sense to put it in a function, but it can work at top level too, if that makes more sense. In my very simple example, it probably does:

class Foo:
    def bar(self):
        print("bar")

# unindent here!
f = Foo()
f.bar()

In your code, you might want the more button setup code to happen in an __init__ method. That will be called by Python automatically just after each instance has been created. You'll need to think a bit about which of the class variables you're currently designing should be shared by all instances and which should be specific to each instance. I don't know your design for the class well enough to tell you what is best, though making everything instance attributes might be a safe bet. The __init__ method gets self passed in automatically.

Here's a version of your App class that shows a more typical style for a Tkinter program, with all the data being put in instance variables, rather than class variables:

class App:
    # get rid of all the class-variable code
    def __init__(self, root):
        self.root = root
        self.testArray = [1, 2, 2]
        self.cAmount = 0
        self.card1 = 0
        self.card2 = 0
    
        self.img1 = PhotoImage(file="blueBox.png")
        self.img2 = PhotoImage(file="redBox.png")
        self.deckImage = PhotoImage(file="blackBox.png")

        self.button1 = Button(master=root, text="", image=self.deckImage, state=NORMAL, command=self.showcolor1)
        self.button1.pack()
        self.button2 = Button(master=root, text="", image=self.deckImage, state=NORMAL, command=self.showcolor2)
        self.button2.pack()
        self.button3 = Button(master=root, text="", image=self.deckImage, state=NORMAL, command=self.showcolor3)
        self.button3.pack()

    # the rest of the methods are the same


# unindent for some more top level code to start the app running
root = Tk()
app = App(root)
root.mainloop()

Note that the initialization of the Button objects changed very slightly, they're now using command=self.showcolor1 and similar, rather than just using a method name.

4
ahmetknk On

You are not sending a parameter to showcolor3() function.

If you want to send a parameter to the function you can use lambda function:

button3 = tk.Button(master=root,text="",command=lambda self = instance: showcolor3(self))

By the way, It seems like you have not defined the showcolor function in a class but I can not say for sure since I am unable to see the code as a whole. If that is the case, you must define the function in a class if you want to invoke it by using an instance.

Edit: I see you edited your question. You have to create a class first then create methods for its instances. Your problem may be insufficient knowledge about OOP. It may be a good idea to look at the OOP concepts.

0
Zeeking786 On

Just write your last line out of all functions,all things are perfect i run it and i got this error only try it .

from tkinter import *


class App:

    root = Tk()
    testArray = [1, 2, 2]
    cAmount = 0
    card1 = 0
    card2 = 0
    button1 = Button()
    button2 = Button()
    button3 = Button()

    img1 = PhotoImage(file="blueBox.png")
    img2 = PhotoImage(file="redBox.png")
    deckImage = PhotoImage(file="blackBox.png")

    def checkCards(self):
        if self.testArray[self.card1] == self.testArray[self.card2]:
            print("CORRECT")
        elif self.testArray[self.card1] != self.testArray[self.card2]:
            print("FALSE")
            if self.card1 == 1 or self.card2 == 1:
                self.button1.configure(image=self.deckImage)
                self.button1.configure(state=NORMAL)
            if self.card1 == 2 or self.card2 == 2:
                self.button2.configure(image=self.deckImage)
                self.button2.configure(state=NORMAL)
            if self.card1 == 3 or self.card2 == 3:
                self.button3.configure(image=self.deckImage)
                self.button3.configure(state=NORMAL)

    def setChoosenPicture(self):
        self.cAmount = self.cAmount + 1
        if self.cAmount == 2:
            self.checkCards()
            self.cAmount = 0
        else:
            return

    def getArrayValue(self, buttonIndex):
        if self.testArray[buttonIndex] == 1:
            return self.img1
        elif self.testArray[buttonIndex] == 2:
            return self.img2

    def showcolor1(self):
        if self.card1 != 0:
            self.card2 = 1
        else:
            self.card1 = 1

        self.button1.configure(image=self.getArrayValue(0))
        self.button1.configure(state=DISABLED)
        self.setChoosenPicture(self.getArrayValue(0))

    def showcolor2(self):
        if self.card1 != 0:
            self.card2 = 2
        else:
            self.card1 = 2

        self.button2.configure(image=self.getArrayValue(1))
        self.button2.configure(state=DISABLED)
        self.setChoosenPicture(self.getArrayValue(1))

    def showcolor3(self):
        if self.card1 != 0:
            self.card2 = 3
        else:
            self.card1 = 3

        self.button2.configure(image=self.getArrayValue(1))
        self.button2.configure(state=DISABLED)
        self.setChoosenPicture(self.getArrayValue(2))

    button1 = Button(
        master=root, text="", image=deckImage, state=NORMAL, command=showcolor1
    )
    button1.pack()
    button2 = Button(
        master=root, text="", image=deckImage, state=NORMAL, command=showcolor2
    )
    button2.pack()
    button3 = Button(
        master=root, text="", image=deckImage, state=NORMAL, command=showcolor3
    )
    button3.pack()

    root.mainloop()

a = App() //This needs to be out