如何使用模型/视图/控制器方法制作GUI? [英] How do I make a GUI using the model/view/controller method?
问题描述
我需要了解模型/视图/控制器方法背后的概念以及如何以这种方式编写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
:给定StoryModel
和StoryView
的类将告诉其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
特定于模型.
因此,您可以创建GtkStoryView
或CursesStoryView
,而无需更改Model
和Controller
中的任何代码. (这对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 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 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屋!