问题
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.controller
Controller.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