I need to understand the concept behind the model/view/controller method and how to write a GUI that way. Here's just a really basic, simple GUI. Can someone explain to me how to rewrite this code using MVC?
from tkinter import *
class Application(Frame):
""" GUI application that creates a story based on user input. """
def __init__(self, master):
""" Initialize Frame. """
super(Application, self).__init__(master)
self.grid()
self.create_widgets()
def create_widgets(self):
""" Create widgets to get story information and to display story. """
# create instruction label
Label(self,
text = "Enter information for a new story"
).grid(row = 0, column = 0, columnspan = 2, sticky = W)
# create a label and text entry for the name of a person
Label(self,
text = "Person: "
).grid(row = 1, column = 0, sticky = W)
self.person_ent = Entry(self)
self.person_ent.grid(row = 1, column = 1, sticky = W)
# create a label and text entry for a plural noun
Label(self,
text = "Plural Noun:"
).grid(row = 2, column = 0, sticky = W)
self.noun_ent = Entry(self)
self.noun_ent.grid(row = 2, column = 1, sticky = W)
# create a label and text entry for a verb
Label(self,
text = "Verb:"
).grid(row = 3, column = 0, sticky = W)
self.verb_ent = Entry(self)
self.verb_ent.grid(row = 3, column = 1, sticky = W)
# create a label for adjectives check buttons
Label(self,
text = "Adjective(s):"
).grid(row = 4, column = 0, sticky = W)
# create itchy check button
self.is_itchy = BooleanVar()
Checkbutton(self,
text = "itchy",
variable = self.is_itchy
).grid(row = 4, column = 1, sticky = W)
# create joyous check button
self.is_joyous = BooleanVar()
Checkbutton(self,
text = "joyous",
variable = self.is_joyous
).grid(row = 4, column = 2, sticky = W)
# create electric check button
self.is_electric = BooleanVar()
Checkbutton(self,
text = "electric",
variable = self.is_electric
).grid(row = 4, column = 3, sticky = W)
# create a label for body parts radio buttons
Label(self,
text = "Body Part:"
).grid(row = 5, column = 0, sticky = W)
# create variable for single, body part
self.body_part = StringVar()
self.body_part.set(None)
# create body part radio buttons
body_parts = ["bellybutton", "big toe", "medulla oblongata"]
column = 1
for part in body_parts:
Radiobutton(self,
text = part,
variable = self.body_part,
value = part
).grid(row = 5, column = column, sticky = W)
column += 1
# create a submit button
Button(self,
text = "Click for story",
command = self.tell_story
).grid(row = 6, column = 0, sticky = W)
self.story_txt = Text(self, width = 75, height = 10, wrap = WORD)
self.story_txt.grid(row = 7, column = 0, columnspan = 4)
def tell_story(self):
""" Fill text box with new story based on user input. """
# get values from the GUI
person = self.person_ent.get()
noun = self.noun_ent.get()
verb = self.verb_ent.get()
adjectives = ""
if self.is_itchy.get():
adjectives += "itchy, "
if self.is_joyous.get():
adjectives += "joyous, "
if self.is_electric.get():
adjectives += "electric, "
body_part = self.body_part.get()
# create the story
story = "The famous explorer "
story += person
story += " had nearly given up a life-long quest to find The Lost City of "
story += noun.title()
story += " when one day, the "
story += noun
story += " found "
story += person + ". "
story += "A strong, "
story += adjectives
story += "peculiar feeling overwhelmed the explorer. "
story += "After all this time, the quest was finally over. A tear came to "
story += person + "'s "
story += body_part + ". "
story += "And then, the "
story += noun
story += " promptly devoured "
story += person + ". "
story += "The moral of the story? Be careful what you "
story += verb
story += " for."
# display the story
self.story_txt.delete(0.0, END)
self.story_txt.insert(0.0, story)
# main
def main():
root = Tk()
root.title("Mad Lib")
app = Application(root)
root.mainloop()
main()
ToyMVC "Toy MVC (Model View Controller) design" in the Tkinter docs is probably what you're looking for. I would personally design things a bit differently, but it mostly makes sense.
The key is separating out the model and view, and the controller is then all the bits that connect up a model and a view.
So, instead of having an
Application
with everything in it, you'd have these classes:StoryModel
: A model of a story.StoryView
: A window or other widget that you stick inside the frame—although in Tk, you can just as easily make it the frame itself.StoryController
: A class that, given aStoryModel
and aStoryView
, will tell itsStoryView
to create the appropriate widgets to display that story, and will then monitor both Model and View for changed and transmit them from one to the other.Given that, you can create a simple
Application
that creates oneStoryModel
, oneStoryView
, one frame window to put the View in, and oneStoryController
to connect up the model and view.For example,
StoryModel
could look like this:And then you can get fancy and create an
AlternateStoryView
that displays the same information in a different way, and change theApplication
to create one of each view, and a controller for each, attached to the same model. For example, you might create a view that didn't use a grid, and instead automatically laid things out:If you know about the
trace
method, you may notice that anObservable
isn't really any different than usingTkinter.StringVar
, etc. But the advantage (besides not having the clunky syntax oftrace
…) is that there's nothingTkinter
-specific about the model this way.So, you can create a
GtkStoryView
or aCursesStoryView
, without changing any of the code in theModel
andController
. (This doesn't quite work with ToyMVC, because things likeaddButton.config(command=self.addMoney)
won't exactly translate to Gtk+ or curses unless you built a big Tk emulation layer… but you don't have to make that mistake in your design.)Also, note that using
Observable
wrappers around all of your model variables is definitely not the only way to hook up a controller, or even necessarily the most Pythonic.If you want to start/learn MVC web development using python language i suggest to get started with Django Framework