Python Temperature conversion MVC style: why am I getting “TypeError: buttonPressed() missing 1 required positional argument: 'self'”

瘦欲@ 提交于 2019-12-25 03:27:52

问题


newbie-wanna-be python-programmer here. I had a homework assignment (that I couldn't get) and now that the class is over, I feel comfortable asking for help here. I'm still really curious about this error and what I'm doing wrong... I'm sure it's related to how the CONTROLLER and the VIEW are referring to each other, but I this error is beyond my understanding. I've been stuck on this for about three days. I could really use help- because I really want to understand python and MVC.

Please see screen cap below for context.

Error:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python33\lib\tkinter\__init__.py", line 1482, in __call__
    return self.func(*args)
TypeError: buttonPressed() missing 1 required positional argument: 'self'

VIEW: myFrame.py

"""writing event handlers"""

import tkinter
import glue
class MyFrame(tkinter.Frame): #creates window for controls in an object made
                              #from a class called "tkinter.Frame" 
    """
    Class myFrame is a tkinter.Frame...
    It contains two buttons, two entry areas, and four labels:
    one button a converter;
    one button quits the program;
    one entry is for celsius;
    one entry is for fahrenheit;
    and the labels prompt user for input, and label the entry values as needed.
    """

    def __init__(self, controller):
        """
        places the controls on the frame
        """
        tkinter.Frame.__init__(self) #initilizes the superclass 
        self.pack()  #required for the buttons to show up properly.
        self.controller = glue.Controller #saves ref to controller to call methods on
                                          #contoller object when user generates events

#Fahrenheit Input Prompt
        self.fahrenheitLabel = tkinter.Label(self)
        self.fahrenheitLabel["text"] = ("Enter Fahrenheit Value:")
        self.fahrenheitLabel.pack({"side":"left"})

#Fahrenheit Entry Space
        self.fahrenheitEntrySpace = tkinter.Entry(self)
        self.fahrenheitEntrySpace == self.fahrenheitEntrySpace.insert(1, "0")  
        self.fahrenheitEntrySpace.pack({"side":"left"})

#Fahrenheit Value label
        self.fahrenheitLabel = tkinter.Label(self)
        self.fahrenheitLabel["text"] = ("Fahrenheit Degrees")
        self.fahrenheitLabel.pack({"side":"left"})

#Converter button 
        self.convertButton=tkinter.Button(self)
        self.convertButton["text"]= "Convert"
        self.convertButton["command"]=self.controller.buttonPressed
        # an object that remembers both self and reply when later called
        self.convertButton.pack({"side":"left"})       

#Quit button
        self.quitButton = tkinter.Button(self)
        self.quitButton["text"] = "Press\nhere to\n***QUIT***"
        self.quitButton["command"] = self.quit
        #the statement above attaches the event handler
        #self.quit() to the quit button
        self.quitButton.pack({"side":"right"})

#Celsius Value label
        self.celsiusLabel = tkinter.Label(self)
        self.celsiusLabel["text"] = ("Celsius Degrees")
        self.celsiusLabel.pack({"side":"right"})

#Celsius Entry Space
        self.celsiusEntrySpace = tkinter.Entry(self)
        self.celsiusEntrySpace["text"] == self.celsiusEntrySpace.insert(1, "0")
        self.celsiusEntrySpace.pack({"side":"right"})


#Celsius Input Prompt
        self.celsiusLabel = tkinter.Label(self)
        self.celsiusLabel["text"] = ("Enter Celsius Value:")
        self.celsiusLabel.pack({"side":"right"})

#Test program
if __name__=="__main__":
    root = tkinter.Tk()
    view = MyFrame() #puts the frame onto the user's screen.
    view.mainloop()
    root.destroy()

MODEL: counter.py

import tkinter


class Convert: #the MODEL

    '''
    class counter is the MODEL for a simple program that exemplifies
    the MODEL/VIEW/CONTROLLER architecture.

    It mostly just maintains two formulas that convert Fahrenheit to Celsius
    and Celsius to Fahrenheit each time the f2C() or c2F methods are called.

    in a real MVC app, the MODEL would contain all the business logic.
    Note that the model never contains a reference to the VIEW.
    '''
    def __init__(self):
        self.fahrenheitEntrySpace = 0
        self.celsiusEntrySpace = 0

    def convertTempF2C(self):
        fahrenheit = fahrenheitEntrySpace.get()
        if fahrenheit != 0.0:
            celsius = (fahrenheit - 32) * 5 / 9
        else:
            celsius = -17.7777778 

    def convertTempC2F(self):
        celsius = celsiusEntrySpace.get()
        if celsius != 0.0:
            fahrenheit = (celsius *  9.0/5.0 + 32)          
        else:
            fahrenheit = 32

    def __str__(self):
        return str(self.counter)

CONTROLLER: "glue.py"

import tkinter

import myFrame #the VIEW

import counter #the MODEL

class Controller:

    """
    The CONTROLLER for an app that follows the MODEL/VIEW/CONTROLLER architecture.
    When the user presses a button on the VIEW,
    this controller calls the appropriate methods in the model.
    The controller handles all the communication between the model and the view.
    """

    def __init__(self):

        """
        This starts the TK framework up;
        instantiates the model;
        instantiates the VIEW;
        and states the event loop that waits for the user to press a button on the view
        """
        root = tkinter.Tk() #This starts the TK framework up;
        self.model = counter.Convert() #instantiates the model
        self.view = myFrame.MyFrame(self) #instantiates the VIEW
        self.view.mainloop() # states event loop waits for user to press button on view
        root.destroy() #lets user quit

    def buttonPressed(self):

        """
        Convert F --> C
        """

        self.model.convertTempF2C(self.view.fahrenheitEntrySpace.get)
        #MODEL creates new celsius temp from(fahrenheit input) 

        self.view.celsiusEntrySpace.pop()
        #replaces VIEW's old default celsius value

        self.view.celsiusEntrySpace.insert(self.model.celsius)
        #and insert's MODEL's newly converted (celsius) value

        """
        Convert C --> F
        """

        self.model.convertTempC2F(self.view.celsiusEntrySpace.get)
        #MODEL creates new fahrenheit temp from  (celsius input)

        self.view.fahrenheitEntrySpace.pop() 
        #replaces VIEW's old default 0 fahrenheit value 

        self.view.fahrenheitEntrySpace.insert(self.model.fahrenheit)
        #and insert's MODEL's newly converted (fahrenheit) value

if __name__=="__main__":
    c = Controller()

screen cap

http://imgur.com/pAQc3Zw <-- link to screen cap (I can't post w/o 10 reputation.

Edit: After fixing the self ref loop, I ran into other issues as seen here: Python MVC architecture Temperature Conversion: Why am I getting "NameError: global name 'view' is not defined"


回答1:


In MyFrame.__init__, you're saving a reference to the Controller class:

self.controller = glue.Controller

But you're not actually creating an instance of Controller, which means Controller.__init__ is never called. That's probably not what you meant to do.

It also means that when you do this:

    self.convertButton["command"]=self.controller.buttonPressed

You're really saying

    self.convertButton["command"]= glue.Controller.buttonPressed

Which means you're making an unbound method the callback for convertButton. Unbound means that the method isn't bound to a particular instance of Controller, which means self won't implicitly be passed to it - hence the error you're getting. Your program is creating a Controller instance when it starts, and that is what's calling MyFrame.__init__. You're actually 99% of the way to do things properly - you're passing the Controller instance to MyFrame.__init__:

    self.view = myFrame.MyFrame(self) #instantiates the VIEW

So now all you need to do is assign that instance to self.controllerController.init`:

def __init__(self, controller):
    """
    places the controls on the frame
    """
    tkinter.Frame.__init__(self)
    self.pack()
    self.controller = controller  # NOT glue.Controller


来源:https://stackoverflow.com/questions/25273465/python-temperature-conversion-mvc-style-why-am-i-getting-typeerror-buttonpres

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!