如何使用模型/视图/控制器方法制作GUI? [英] How do I make a GUI using the model/view/controller method?

查看:60
本文介绍了如何使用模型/视图/控制器方法制作GUI?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要了解模型/视图/控制器方法背后的概念以及如何以这种方式编写GUI.这只是一个非常基本,简单的GUI.有人可以向我解释如何使用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 玩具MVC(模型视图控制器) Tkinter文档中的设计"可能正是您要寻找的.我个人会设计一些不同的东西,但这在大多数情况下是有道理的.

关键是要分离模型和视图,然后由控制器来连接模型和视图.

因此,您将拥有以下类,而不是其中包含所有内容的Application:

  • StoryModel:一个故事的模型.
  • StoryView:粘贴在框架内的窗口或其他小部件-尽管在Tk中,您也可以轻松地使其成为框架本身.
  • StoryController:给定StoryModelStoryView的类将告诉其StoryView创建适当的小部件以显示该故事,然后监视Model和View的更改和传输他们从一个到另一个.

鉴于此,您可以创建一个简单的Application,该Application创建一个StoryModel,一个StoryView,一个用于放入View的框架窗口和一个用于连接模型和视图的StoryController.

例如,StoryModel可能看起来像这样:

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

然后,您可以创建一个AlternateStoryView,以不同的方式显示相同的信息,然后更改Application以创建每个视图中的一个,并为每个视图创建一个控制器,并将其附加到同一模型上.例如,您可以创建一个不使用网格的视图,而是自动将其布局:

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

如果您了解trace方法,您可能会注意到Observable与使用Tkinter.StringVar并没有什么不同,等等.但是它的优点(除了没有trace的笨拙语法之外…… )就是这种方式没有Tkinter特定于模型.

因此,您可以创建GtkStoryViewCursesStoryView,而无需更改ModelController中的任何代码. (这对ToyMVC不太有效,因为addButton.config(command=self.addMoney)之类的东西无法准确转换为Gtk +或curses,除非您构建了一个大的Tk仿真层...但是您不必在设计中犯该错误.)

另外,请注意,在所有模型变量周围使用Observable包装器绝对不是连接控制器的唯一方法,甚至不一定是最Python的方法.

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 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.

这篇关于如何使用模型/视图/控制器方法制作GUI?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆