How do I make a GUI using the model/view/controlle

2019-02-11 02:28发布

问题:

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()

回答1:

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 a StoryModel and a StoryView, will tell its StoryView 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 one StoryModel, one StoryView, one frame window to put the View in, and one StoryController to connect up the model and view.

For example, StoryModel could look like this:

class StoryModel(object):
    body_parts = ['bellybutton', 'big toe', 'medulla oblongato']
    def __init__(self):
        self.person = ObservableStr('')
        # ...
        self.is_itchy = ObservableBool(False)
        # ...
    def tell(self):
        story = "The famous explorer "
        # ...
        return story

And then you can get fancy and create an AlternateStoryView that displays the same information in a different way, and change the Application 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:

class AlternateStoryView(Frame):
    def __init__(self, master):
        super(StoryView, self).__init__(master)
    def add_label(label):
        label = Label(self, text=label)
        label.pack()
        return label

If you know about the trace method, you may notice that an Observable isn't really any different than using Tkinter.StringVar, etc. But the advantage (besides not having the clunky syntax of trace…) is that there's nothing Tkinter-specific about the model this way.

So, you can create a GtkStoryView or a CursesStoryView, without changing any of the code in the Model and Controller. (This doesn't quite work with ToyMVC, because things like addButton.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.



回答2:

If you want to start/learn MVC web development using python language i suggest to get started with Django Framework